diff --git a/internal/router/static_routes.go b/internal/router/static_routes.go index a7965d8..e9b4171 100644 --- a/internal/router/static_routes.go +++ b/internal/router/static_routes.go @@ -44,10 +44,14 @@ func initStaticRoutes(root *gin.RouterGroup) { contentType := mime.TypeByExtension(ext) if contentType == "" { switch ext { - case ".js": contentType = "application/javascript" - case ".css": contentType = "text/css" - case ".svg": contentType = "image/svg+xml" - default: contentType = "application/octet-stream" + case ".js": + contentType = "application/javascript" + case ".css": + contentType = "text/css" + case ".svg": + contentType = "image/svg+xml" + default: + contentType = "application/octet-stream" } } @@ -55,7 +59,7 @@ func initStaticRoutes(root *gin.RouterGroup) { if gzFile, err := staticFS.Open(gzPath); err == nil { defer gzFile.Close() ctx.Header("Content-Type", contentType) - + if isGzipSupported { // 极致性能:流式透传压缩包 (RSS 占用极低) ctx.Header("Content-Encoding", "gzip") @@ -85,6 +89,13 @@ func initStaticRoutes(root *gin.RouterGroup) { // logo.svg 等单文件处理 root.GET("/logo.svg", func(ctx *gin.Context) { + settings := services.NewSettingsService() + icon := settings.Get(constant.SectionSite, constant.KeyIcon) + if icon != "" { + ctx.Header("Cache-Control", "public, max-age=86400") + ctx.Data(http.StatusOK, "image/svg+xml", []byte(icon)) + return + } serveSingleFile(ctx, "logo.svg", "image/svg+xml", "public, max-age=86400") }) @@ -111,7 +122,7 @@ func initPWARoutes(root *gin.RouterGroup) { }) } - // 动态 manifest 处理 (支持由 Go 后端控制标题) + // 动态 manifest 处理 (支持由 Go 后端控制标题和图标) root.GET("/manifest.webmanifest", handleManifest) // 动态匹配 workbox-*.js (Vite PWA 生成的库文件) @@ -121,6 +132,7 @@ func initPWARoutes(root *gin.RouterGroup) { }) } + func handleManifest(ctx *gin.Context) { staticFS := static.GetFS() if staticFS == nil { @@ -150,6 +162,16 @@ func handleManifest(ctx *gin.Context) { manifest["short_name"] = title } + // 注入后端配置的图标 (首选 logo.svg) + manifest["icons"] = []map[string]interface{}{ + { + "src": "/logo.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any maskable", + }, + } + res, _ := json.Marshal(manifest) ctx.Header("Cache-Control", "public, no-cache") ctx.Data(200, "application/manifest+json", res) @@ -162,7 +184,9 @@ func serveSingleFile(ctx *gin.Context, filename string, contentType string, cach return } - if cache != "" { ctx.Header("Cache-Control", cache) } + if cache != "" { + ctx.Header("Cache-Control", cache) + } ctx.Header("Content-Type", contentType) isGzipSupported := strings.Contains(ctx.GetHeader("Accept-Encoding"), "gzip") @@ -221,7 +245,9 @@ func serveSPA(ctx *gin.Context, urlPrefix string, status int) { html := string(data) baseHref := urlPrefix + "/" - if urlPrefix == "" { baseHref = "/" } + if urlPrefix == "" { + baseHref = "/" + } html = strings.Replace(html, "", "\n ", 1) configScript := `` html = strings.Replace(html, "", configScript+"", 1) diff --git a/web/public/pwa-icon-192.png b/web/public/pwa-icon-192.png deleted file mode 100644 index 082e96f..0000000 Binary files a/web/public/pwa-icon-192.png and /dev/null differ diff --git a/web/public/pwa-icon-512.png b/web/public/pwa-icon-512.png deleted file mode 100644 index 082e96f..0000000 Binary files a/web/public/pwa-icon-512.png and /dev/null differ diff --git a/web/vite.config.ts b/web/vite.config.ts index e28f85c..ba9404e 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -70,21 +70,11 @@ export default defineConfig({ display: 'standalone', icons: [ { - src: 'pwa-icon-192.png', - sizes: '192x192', - type: 'image/png' - }, - { - src: 'pwa-icon-512.png', - sizes: '512x512', - type: 'image/png' - }, - { - src: 'pwa-icon-512.png', - sizes: '512x512', - type: 'image/png', + src: 'logo.svg', + sizes: 'any', + type: 'image/svg+xml', purpose: 'any maskable' - } + }, ] }, workbox: {