fix(web): 静态资源内嵌二进制,修复 install.sh 部署后 /static 404 导致写邮件页无编辑器
- 线上经 install.sh 部署时只复制二进制 + templates,internal/web/static 未被复制,/static/vendor/quill/* 全部 404,Quill 加载失败正文不可输入 - 改为 go:embed 把 Quill css/js 编译进二进制,engine.StaticFS 提供, 运行期不再依赖部署目录/工作目录,杜绝同类部署遗漏 - 已验证:移除磁盘 static 目录后静态资源仍可正常提供
This commit is contained in:
+14
-3
@@ -4,13 +4,14 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io/fs"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@@ -56,6 +57,8 @@ type WebServer struct {
|
|||||||
caddyDataDir string
|
caddyDataDir string
|
||||||
outbound *outbound.Manager
|
outbound *outbound.Manager
|
||||||
hub *connhub.Hub
|
hub *connhub.Hub
|
||||||
|
// staticFS 内嵌的静态资源(/static 路由),二进制自包含
|
||||||
|
staticFS http.FileSystem
|
||||||
// pusher 邮件状态变化推送(IMAP 客户端实时同步),可空
|
// pusher 邮件状态变化推送(IMAP 客户端实时同步),可空
|
||||||
pusher imap_server.Pusher
|
pusher imap_server.Pusher
|
||||||
}
|
}
|
||||||
@@ -291,6 +294,12 @@ func NewWebServer(cfg config.WebConfig, stores *store.Stores, attStorage *storag
|
|||||||
template.Must(tmpl.ParseGlob("internal/web/templates/admin/*.html"))
|
template.Must(tmpl.ParseGlob("internal/web/templates/admin/*.html"))
|
||||||
engine.SetHTMLTemplate(tmpl)
|
engine.SetHTMLTemplate(tmpl)
|
||||||
|
|
||||||
|
// 内嵌静态资源根目录(/static 路由数据源)
|
||||||
|
staticSub, err := fs.Sub(staticFS, "static")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("加载内嵌静态资源失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
ws := &WebServer{
|
ws := &WebServer{
|
||||||
engine: engine,
|
engine: engine,
|
||||||
stores: stores,
|
stores: stores,
|
||||||
@@ -302,6 +311,7 @@ func NewWebServer(cfg config.WebConfig, stores *store.Stores, attStorage *storag
|
|||||||
caddyDataDir: caddyCfg.DataDir,
|
caddyDataDir: caddyCfg.DataDir,
|
||||||
outbound: ob,
|
outbound: ob,
|
||||||
hub: hub,
|
hub: hub,
|
||||||
|
staticFS: http.FS(staticSub),
|
||||||
pusher: pusher,
|
pusher: pusher,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,8 +331,9 @@ func (ws *WebServer) registerRoutes() {
|
|||||||
ws.engine.Use(middleware.SecurityHeaders())
|
ws.engine.Use(middleware.SecurityHeaders())
|
||||||
|
|
||||||
// 静态资源(本地化的 Quill 编辑器等第三方前端库)。
|
// 静态资源(本地化的 Quill 编辑器等第三方前端库)。
|
||||||
// 同源加载以满足 CSP script-src/style-src 'self',且服务器离线可用。
|
// 内嵌于二进制:同源加载以满足 CSP script-src/style-src 'self',
|
||||||
ws.engine.Static("/static", "internal/web/static")
|
// 且不依赖部署目录(install.sh 只复制二进制 + templates)。
|
||||||
|
ws.engine.StaticFS("/static", ws.staticFS)
|
||||||
|
|
||||||
// Public routes (no auth required)
|
// Public routes (no auth required)
|
||||||
ws.engine.GET("/login", authHandler.ShowLogin)
|
ws.engine.GET("/login", authHandler.ShowLogin)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
// 静态资源内嵌进二进制:install.sh 只部署二进制 + templates,
|
||||||
|
// 此前 internal/web/static/ 未被复制导致线上 /static 全部 404。
|
||||||
|
// go:embed 使前端资源自包含,不再依赖部署目录与工作目录。
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
// staticFS 包含 internal/web/static/ 下的全部静态资源
|
||||||
|
// (目前为本地化的 Quill 编辑器 css/js)。
|
||||||
|
//
|
||||||
|
//go:embed all:static
|
||||||
|
var staticFS embed.FS
|
||||||
Reference in New Issue
Block a user