mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-09-19 12:16:24 +08:00
feat(config): Add PWA manifest.json endpoint for web app installation (#990)
* feat(config): Add PWA manifest.json endpoint for web app installation * fix: Update comment to English in manifest handler * fix: fix EOL * fix: Remove unused fmt import from manifest handler * feat: use site settings for manifest name and icon * fix(manifest): Move manifest.json route to static handler for proper CDN handling * feat: move manifest.json handler to static package and improve path handling * feat: Add custom static file handler to prevent manifest.json conflicts * fix: Integrate manifest.json handling into static file serving routes * fix: Simplify PWA manifest scope handling and static file serving - Remove CDN-specific logic for PWA manifest scope and start_url - Always use base path for PWA scope regardless of CDN configuration - Replace manual file serving logic with http.FileServer for static assets * fix: Ensure consistent base path handling in site configuration and manifest path construction * fix: Refactor trailing slash handling in site configuration * feat(static): update manifest path handling and add route for manifest.json
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package static
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -17,6 +18,20 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ManifestIcon struct {
|
||||
Src string `json:"src"`
|
||||
Sizes string `json:"sizes"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
Display string `json:"display"`
|
||||
Scope string `json:"scope"`
|
||||
StartURL string `json:"start_url"`
|
||||
Name string `json:"name"`
|
||||
Icons []ManifestIcon `json:"icons"`
|
||||
}
|
||||
|
||||
var static fs.FS
|
||||
|
||||
func initStatic() {
|
||||
@ -77,9 +92,15 @@ func initIndex(siteConfig SiteConfig) {
|
||||
utils.Log.Debug("Successfully read index.html from static files system")
|
||||
}
|
||||
utils.Log.Debug("Replacing placeholders in index.html...")
|
||||
// Construct the correct manifest path based on basePath
|
||||
manifestPath := "/manifest.json"
|
||||
if siteConfig.BasePath != "/" {
|
||||
manifestPath = siteConfig.BasePath + "/manifest.json"
|
||||
}
|
||||
replaceMap := map[string]string{
|
||||
"cdn: undefined": fmt.Sprintf("cdn: '%s'", siteConfig.Cdn),
|
||||
"base_path: undefined": fmt.Sprintf("base_path: '%s'", siteConfig.BasePath),
|
||||
"cdn: undefined": fmt.Sprintf("cdn: '%s'", siteConfig.Cdn),
|
||||
"base_path: undefined": fmt.Sprintf("base_path: '%s'", siteConfig.BasePath),
|
||||
`href="/manifest.json"`: fmt.Sprintf(`href="%s"`, manifestPath),
|
||||
}
|
||||
conf.RawIndexHtml = replaceStrings(conf.RawIndexHtml, replaceMap)
|
||||
UpdateIndex()
|
||||
@ -110,12 +131,57 @@ func UpdateIndex() {
|
||||
utils.Log.Debug("Index.html update completed")
|
||||
}
|
||||
|
||||
func ManifestJSON(c *gin.Context) {
|
||||
// Get site configuration to ensure consistent base path handling
|
||||
siteConfig := getSiteConfig()
|
||||
|
||||
// Get site title from settings
|
||||
siteTitle := setting.GetStr(conf.SiteTitle)
|
||||
|
||||
// Get logo from settings, use the first line (light theme logo)
|
||||
logoSetting := setting.GetStr(conf.Logo)
|
||||
logoUrl := strings.Split(logoSetting, "\n")[0]
|
||||
|
||||
// Use base path from site config for consistency
|
||||
basePath := siteConfig.BasePath
|
||||
|
||||
// Determine scope and start_url
|
||||
// PWA scope and start_url should always point to our application's base path
|
||||
// regardless of whether static resources come from CDN or local server
|
||||
scope := basePath
|
||||
startURL := basePath
|
||||
|
||||
manifest := Manifest{
|
||||
Display: "standalone",
|
||||
Scope: scope,
|
||||
StartURL: startURL,
|
||||
Name: siteTitle,
|
||||
Icons: []ManifestIcon{
|
||||
{
|
||||
Src: logoUrl,
|
||||
Sizes: "512x512",
|
||||
Type: "image/png",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "application/json")
|
||||
c.Header("Cache-Control", "public, max-age=3600") // cache for 1 hour
|
||||
|
||||
if err := json.NewEncoder(c.Writer).Encode(manifest); err != nil {
|
||||
utils.Log.Errorf("Failed to encode manifest.json: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate manifest"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func Static(r *gin.RouterGroup, noRoute func(handlers ...gin.HandlerFunc)) {
|
||||
utils.Log.Debug("Setting up static routes...")
|
||||
siteConfig := getSiteConfig()
|
||||
initStatic()
|
||||
initIndex(siteConfig)
|
||||
folders := []string{"assets", "images", "streamer", "static"}
|
||||
|
||||
if conf.Conf.Cdn == "" {
|
||||
utils.Log.Debug("Setting up static file serving...")
|
||||
r.Use(func(c *gin.Context) {
|
||||
|
Reference in New Issue
Block a user