From ca67914cb5923e345a1e26aa8eb0eb28cd824383 Mon Sep 17 00:00:00 2001 From: hxuanyu <2252193204@qq.com> Date: Mon, 26 Jan 2026 23:03:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=86=85=E5=B5=8C=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E9=A1=B5=E9=9D=A2=EF=BC=8C=E6=96=B0=E5=A2=9E=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9=20`web.path`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- config.example.yaml | 3 +++ internal/config/config.go | 7 +++++++ internal/http/router.go | 31 ++++++++++++++++++++++++++++--- web/web.go | 6 ++++++ 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 web/web.go diff --git a/README.md b/README.md index c8035d2..edf239d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - **数据库支持**:支持 SQLite, MySQL, PostgreSQL。 - **公共 API**:提供今日图片、随机图片、指定日期图片的纯图及元数据接口。 - **管理后台**:内置极简管理后台,支持 Token 管理、任务控制、配置查看。 +- **单文件分发**:支持将前端页面嵌入二进制文件,实现无依赖运行。 - **行为模式**:支持 `local`(服务转发)和 `redirect`(302 跳转至公网 URL)两种模式。 ## 快速启动 @@ -83,7 +84,7 @@ go run . .\scripts\build.ps1 [version] ``` -编译后的打包文件将生成在 `output` 目录下。 +编译后的打包文件将生成在 `output` 目录下。二进制文件已内置默认前端页面,即使不带 `web` 目录也能运行。如果需要自定义页面,可在配置中指定 `web.path`。 ### 发布流程 本项目集成了 GitHub Actions,可通过以下步骤发布新版本: diff --git a/config.example.yaml b/config.example.yaml index 2e5e893..1a69db3 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -45,3 +45,6 @@ token: feature: write_daily_files: true + +web: + path: web diff --git a/internal/config/config.go b/internal/config/config.go index f405dce..a364703 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -20,6 +20,7 @@ type Config struct { Admin AdminConfig `mapstructure:"admin"` Token TokenConfig `mapstructure:"token"` Feature FeatureConfig `mapstructure:"feature"` + Web WebConfig `mapstructure:"web"` } type ServerConfig struct { @@ -89,6 +90,10 @@ type FeatureConfig struct { WriteDailyFiles bool `mapstructure:"write_daily_files"` } +type WebConfig struct { + Path string `mapstructure:"path"` +} + // Bing 默认配置 (内置) const ( BingMkt = "zh-CN" @@ -125,6 +130,7 @@ func Init(configPath string) error { v.SetDefault("storage.local.root", "data/picture") v.SetDefault("token.default_ttl", "168h") v.SetDefault("feature.write_daily_files", true) + v.SetDefault("web.path", "web") v.SetDefault("admin.password_bcrypt", "$2a$10$fYHPeWHmwObephJvtlyH1O8DIgaLk5TINbi9BOezo2M8cSjmJchka") // 默认密码: admin123 if err := v.ReadInConfig(); err != nil { @@ -176,6 +182,7 @@ func SaveConfig(cfg *Config) error { v.Set("admin", cfg.Admin) v.Set("token", cfg.Token) v.Set("feature", cfg.Feature) + v.Set("web", cfg.Web) return v.WriteConfig() } diff --git a/internal/http/router.go b/internal/http/router.go index e2c3eeb..ddec995 100644 --- a/internal/http/router.go +++ b/internal/http/router.go @@ -1,9 +1,15 @@ package http import ( + "net/http" + "os" + "path/filepath" + _ "BingPaper/docs" + "BingPaper/internal/config" "BingPaper/internal/http/handlers" "BingPaper/internal/http/middleware" + "BingPaper/web" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" @@ -18,9 +24,28 @@ func SetupRouter() *gin.Engine { // 静态文件 r.Static("/static", "./static") - r.StaticFile("/", "./web/index.html") - r.StaticFile("/admin", "./web/index.html") - r.StaticFile("/login", "./web/index.html") + + webPath := config.GetConfig().Web.Path + indexPath := filepath.Join(webPath, "index.html") + + serveIndex := func(c *gin.Context) { + // 1. 优先尝试从配置的路径读取 + if _, err := os.Stat(indexPath); err == nil { + c.File(indexPath) + return + } + // 2. 如果外部文件不存在,则使用内置嵌入的文件 + data, err := web.FS.ReadFile("index.html") + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "web files not found"}) + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", data) + } + + r.GET("/", serveIndex) + r.GET("/admin", serveIndex) + r.GET("/login", serveIndex) api := r.Group("/api/v1") { diff --git a/web/web.go b/web/web.go new file mode 100644 index 0000000..38ca34f --- /dev/null +++ b/web/web.go @@ -0,0 +1,6 @@ +package web + +import "embed" + +//go:embed index.html +var FS embed.FS