From 81f5d451bc2d22127236de65ba0d0067a10eabcd Mon Sep 17 00:00:00 2001 From: freefire Date: Mon, 3 Aug 2026 16:58:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E7=AE=80=20app.ini=EF=BC=9A=E4=BB=85?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E7=AB=AF=E5=8F=A3/=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9B=AE=E5=BD=95/JWT=EF=BC=8C=E4=B8=9A=E5=8A=A1=E9=A1=B9?= =?UTF-8?q?=E6=94=B9=E7=94=B1=E7=AE=A1=E7=90=86=E5=90=8E=E5=8F=B0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- README.md | 8 +-- ROADMAP.md | 2 +- app.ini.example | 37 +----------- config/config.go | 138 ++++--------------------------------------- config/ini.go | 135 ++---------------------------------------- docs/introduction.md | 2 +- handler/seo.go | 7 +-- router/router.go | 19 ------ service/seo.go | 7 +-- service/settings.go | 100 ------------------------------- 10 files changed, 24 insertions(+), 431 deletions(-) diff --git a/README.md b/README.md index 9213846..d7057af 100644 --- a/README.md +++ b/README.md @@ -174,21 +174,15 @@ cp app.ini.example /opt/jiang13/app.ini ```ini [server] HTTP_PORT = 3000 -ROOT_URL = https://bbs.iioio.com [paths] DATA = data [security] JWT_SECRET = - -[oauth] -CLIENT_ID = gitea -CLIENT_SECRET = -REDIRECT_URIS = https://git.iioio.com/user/oauth2/jiang13/callback ``` -完整示例见仓库根目录 [`app.ini.example`](app.ini.example)。`ROOT_URL` 与 `[oauth]` 可作首次种子;日常请在管理后台「系统设置 → OIDC / SSO」配置(保存即生效)。 +完整示例见仓库根目录 [`app.ini.example`](app.ini.example)。OIDC、邮件、Gitea 同步、对象存储等请在管理后台「系统设置」配置(保存即生效)。 **优先级:** 命令行显式参数 > `app.ini` > 内置默认值。 diff --git a/ROADMAP.md b/ROADMAP.md index 05d3336..10dc2ac 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -14,7 +14,7 @@ | 前台 SPA(React) | ✅ | 浏览、发帖、回复、管理操作已统一在 SPA 内 | | 管理后台 | ✅ | React 后台 `/admin/*`,与前台风格一致 | | 评论系统 | ✅ | 换行显示已修复 | -| OIDC Provider | ✅ | 可供 Gitea 等站点 SSO(`ROOT_URL` + `[oauth]`) | +| OIDC Provider | ✅ | 可供 Gitea 等站点 SSO(管理后台配置 ROOT_URL 与 OAuth 应用) | --- diff --git a/app.ini.example b/app.ini.example index b1b483b..f2e88ee 100644 --- a/app.ini.example +++ b/app.ini.example @@ -1,12 +1,10 @@ ; 姜十三论坛 Jiang13 Forum — 配置文件示例(风格类似 Gitea app.ini) ; 复制为程序工作目录下的 app.ini 后修改。也可直接启动程序,首次会自动生成。 ; 修改后重启进程/服务生效。命令行 --port / --data 等优先级更高。 +; OIDC / 邮件 / Gitea 同步 / 对象存储等请在管理后台「系统设置」配置。 [server] HTTP_PORT = 3000 -; 对外公网根地址(无尾斜杠);也可在管理后台 OIDC 设置中填写 -; 例:https://bbs.iioio.com -ROOT_URL = https://bbs.iioio.com [paths] ; 相对路径相对于工作目录(默认可执行文件所在目录) @@ -15,36 +13,3 @@ DATA = data [security] ; 留空则自动生成并持久化到 data/.jwt_secret(勿把生产密钥提交到仓库) JWT_SECRET = - -[oauth] -; 可选:启动时若管理后台尚未配置,会用此处种子写入 oauth_clients 一次 -; 日常请优先在管理后台「系统设置 → OIDC / SSO」修改(保存即生效) -; Gitea 认证源名称须与回调路径一致,例如名称 jiang13 对应: -; https://git.iioio.com/user/oauth2/jiang13/callback -CLIENT_ID = gitea -CLIENT_SECRET = 请替换为足够长的随机字符串 -; 多个回调用逗号分隔 -REDIRECT_URIS = https://git.iioio.com/user/oauth2/jiang13/callback - -[gitea] -; 可选:同步会员公开仓库到侧栏 /projects(优先在管理后台配置) -; BASE_URL 例:https://git.iioio.com -BASE_URL = -; 只读 Access Token(需能列出用户公开仓库) -TOKEN = -; 首次种子时若 BASE_URL+TOKEN 齐全且此项为 true,则写入并启用同步 -SYNC_ENABLED = false - -[storage] -; 可选种子:仅当管理后台尚未配置时写入一次;日常请在「系统设置 → 对象存储」修改(保存即生效) -; TYPE = local | s3 -TYPE = local -; 以下仅 TYPE = s3 时作为种子(MinIO / 七牛 / 又拍 / 阿里云 OSS 等) -ENDPOINT = -REGION = us-east-1 -BUCKET = -ACCESS_KEY = -SECRET_KEY = -PUBLIC_BASE_URL = -PREFIX = -FORCE_PATH_STYLE = true diff --git a/config/config.go b/config/config.go index 0c05557..bf13c55 100644 --- a/config/config.go +++ b/config/config.go @@ -8,24 +8,12 @@ import ( "strings" ) -// StorageTypeLocal / StorageTypeS3 上传存储后端 +// StorageTypeLocal / StorageTypeS3 上传存储后端(管理后台运行时配置) const ( StorageTypeLocal = "local" StorageTypeS3 = "s3" ) -// S3Config S3 兼容对象存储(MinIO / 七牛 / 又拍 / 阿里云 OSS 等) -type S3Config struct { - Endpoint string // 例:https://s3.example.com 或 s3.example.com:9000 - Region string - Bucket string - AccessKey string - SecretKey string - PublicBaseURL string // 公开访问根 URL(无尾斜杠),上传后返回此前缀下的绝对地址 - Prefix string // 对象 key 前缀(可选,如 forum/) - ForcePathStyle bool // path-style(MinIO 等通常为 true;AWS 官方多为 false) -} - // Config 应用全局配置:默认读工作目录下 app.ini,命令行可覆盖 type Config struct { // 工作目录(默认可执行文件所在目录) @@ -34,23 +22,10 @@ type Config struct { ConfigFile string // 监听端口 Port int - // 对外公网根地址(无尾斜杠),OIDC Issuer 使用 - RootURL string // 数据目录:SQLite、上传、日志(绝对路径) DataDir string // JWT 签名密钥 JWTSecret string - // OIDC 客户端(P0:写死在 app.ini,供 Gitea 对接) - OAuthClientID string - OAuthClientSecret string - OAuthRedirectURIs []string - // Gitea API 同步种子(可选,运行时以管理后台为准) - GiteaBaseURL string - GiteaToken string - GiteaSyncEnabled bool - // 上传存储:local(默认)或 s3 - StorageType string - S3 S3Config // 日志文件路径 LogFile string // 系统服务控制动作:install|uninstall|start|stop|restart|status,空表示正常运行 @@ -113,55 +88,24 @@ func Parse() (*Config, error) { jwtSecret = fileCfg.JWTSecret } - storageType := strings.ToLower(strings.TrimSpace(fileCfg.StorageType)) - if storageType == "" { - storageType = StorageTypeLocal - } - if storageType != StorageTypeLocal && storageType != StorageTypeS3 { - return nil, fmt.Errorf("storage.TYPE 无效: %q(可选 local / s3)", fileCfg.StorageType) - } - cfg := &Config{ - WorkPath: workPath, - ConfigFile: configFile, - Port: port, - RootURL: normalizeRootURL(fileCfg.RootURL), - DataDir: absData, - JWTSecret: jwtSecret, - OAuthClientID: fileCfg.OAuthClientID, - OAuthClientSecret: fileCfg.OAuthClientSecret, - OAuthRedirectURIs: splitCSV(fileCfg.OAuthRedirectURIs), - GiteaBaseURL: normalizeRootURL(fileCfg.GiteaBaseURL), - GiteaToken: fileCfg.GiteaToken, - GiteaSyncEnabled: fileCfg.GiteaSyncEnabled, - StorageType: storageType, - S3: S3Config{ - Endpoint: strings.TrimSpace(fileCfg.S3Endpoint), - Region: strings.TrimSpace(fileCfg.S3Region), - Bucket: strings.TrimSpace(fileCfg.S3Bucket), - AccessKey: strings.TrimSpace(fileCfg.S3AccessKey), - SecretKey: strings.TrimSpace(fileCfg.S3SecretKey), - PublicBaseURL: normalizeRootURL(fileCfg.S3PublicBaseURL), - Prefix: normalizeStoragePrefix(fileCfg.S3Prefix), - ForcePathStyle: fileCfg.S3ForcePathStyle, - }, + WorkPath: workPath, + ConfigFile: configFile, + Port: port, + DataDir: absData, + JWTSecret: jwtSecret, LogFile: filepath.Join(absData, "jiang13.log"), ServiceAction: action, } - // [storage] 仅作首次种子;运行时以管理后台为准,此处不强制校验 S3 完整性 - needDirs := action == "" || action == "install" if needDirs { // 首次启动自动生成 app.ini,便于像 Gitea 一样改文件而不记一长串参数 if !configExists { dataRel := resolveDataRelForINI(workPath, absData) if err := writeAppINI(configFile, fileSettings{ - Port: port, - DataRel: dataRel, - StorageType: StorageTypeLocal, - S3ForcePathStyle: true, - S3Region: "us-east-1", + Port: port, + DataRel: dataRel, }); err != nil { return nil, fmt.Errorf("生成默认配置文件失败: %w", err) } @@ -174,25 +118,9 @@ func Parse() (*Config, error) { iniJWT = jwtSecret } if err := writeAppINI(configFile, fileSettings{ - Port: port, - DataRel: dataRel, - JWTSecret: iniJWT, - RootURL: fileCfg.RootURL, - OAuthClientID: fileCfg.OAuthClientID, - OAuthClientSecret: fileCfg.OAuthClientSecret, - OAuthRedirectURIs: fileCfg.OAuthRedirectURIs, - GiteaBaseURL: fileCfg.GiteaBaseURL, - GiteaToken: fileCfg.GiteaToken, - GiteaSyncEnabled: fileCfg.GiteaSyncEnabled, - StorageType: fileCfg.StorageType, - S3Endpoint: fileCfg.S3Endpoint, - S3Region: fileCfg.S3Region, - S3Bucket: fileCfg.S3Bucket, - S3AccessKey: fileCfg.S3AccessKey, - S3SecretKey: fileCfg.S3SecretKey, - S3PublicBaseURL: fileCfg.S3PublicBaseURL, - S3Prefix: fileCfg.S3Prefix, - S3ForcePathStyle: fileCfg.S3ForcePathStyle, + Port: port, + DataRel: dataRel, + JWTSecret: iniJWT, }); err != nil { return nil, fmt.Errorf("更新配置文件失败: %w", err) } @@ -294,50 +222,6 @@ func (c *Config) FilterWordsPath() string { return filepath.Join(c.DataDir, "filter_words.txt") } -func normalizeRootURL(raw string) string { - u := strings.TrimSpace(raw) - u = strings.TrimRight(u, "/") - return u -} - -// normalizeStoragePrefix 规范化对象 key 前缀:去首尾空白与首斜杠,非空时保证尾斜杠 -func normalizeStoragePrefix(raw string) string { - p := strings.TrimSpace(raw) - p = strings.TrimPrefix(p, "/") - if p == "" { - return "" - } - return strings.TrimSuffix(p, "/") + "/" -} - -func (s S3Config) validate() error { - if s.Endpoint == "" { - return fmt.Errorf("storage.TYPE=s3 时必须配置 ENDPOINT") - } - if s.Bucket == "" { - return fmt.Errorf("storage.TYPE=s3 时必须配置 BUCKET") - } - if s.AccessKey == "" || s.SecretKey == "" { - return fmt.Errorf("storage.TYPE=s3 时必须配置 ACCESS_KEY 与 SECRET_KEY") - } - if s.PublicBaseURL == "" { - return fmt.Errorf("storage.TYPE=s3 时必须配置 PUBLIC_BASE_URL(公开访问根地址)") - } - return nil -} - -func splitCSV(raw string) []string { - parts := strings.Split(raw, ",") - out := make([]string, 0, len(parts)) - for _, p := range parts { - p = strings.TrimSpace(p) - if p != "" { - out = append(out, p) - } - } - return out -} - func generateRandomSecret(n int) string { const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, n) diff --git a/config/ini.go b/config/ini.go index c6971ff..4a33659 100644 --- a/config/ini.go +++ b/config/ini.go @@ -18,34 +18,15 @@ const ( // fileSettings 从 app.ini 读出的原始值(尚未解析为绝对路径) type fileSettings struct { - Port int - DataRel string - JWTSecret string - RootURL string - OAuthClientID string - OAuthClientSecret string - OAuthRedirectURIs string - GiteaBaseURL string - GiteaToken string - GiteaSyncEnabled bool - StorageType string - S3Endpoint string - S3Region string - S3Bucket string - S3AccessKey string - S3SecretKey string - S3PublicBaseURL string - S3Prefix string - S3ForcePathStyle bool + Port int + DataRel string + JWTSecret string } func defaultFileSettings() fileSettings { return fileSettings{ - Port: defaultPort, - DataRel: defaultDataRel, - StorageType: StorageTypeLocal, - S3Region: "us-east-1", - S3ForcePathStyle: true, + Port: defaultPort, + DataRel: defaultDataRel, } } @@ -69,7 +50,6 @@ func loadAppINI(path string) (fileSettings, error) { } out.Port = p } - out.RootURL = strings.TrimSpace(sec.Key("ROOT_URL").String()) } if sec, err := cfg.GetSection("paths"); err == nil { @@ -82,36 +62,6 @@ func loadAppINI(path string) (fileSettings, error) { out.JWTSecret = strings.TrimSpace(sec.Key("JWT_SECRET").String()) } - if sec, err := cfg.GetSection("oauth"); err == nil { - out.OAuthClientID = strings.TrimSpace(sec.Key("CLIENT_ID").String()) - out.OAuthClientSecret = strings.TrimSpace(sec.Key("CLIENT_SECRET").String()) - out.OAuthRedirectURIs = strings.TrimSpace(sec.Key("REDIRECT_URIS").String()) - } - - if sec, err := cfg.GetSection("gitea"); err == nil { - out.GiteaBaseURL = strings.TrimSpace(sec.Key("BASE_URL").String()) - out.GiteaToken = strings.TrimSpace(sec.Key("TOKEN").String()) - out.GiteaSyncEnabled = sec.Key("SYNC_ENABLED").MustBool(false) - } - - if sec, err := cfg.GetSection("storage"); err == nil { - if v := strings.TrimSpace(sec.Key("TYPE").String()); v != "" { - out.StorageType = v - } - out.S3Endpoint = strings.TrimSpace(sec.Key("ENDPOINT").String()) - if v := strings.TrimSpace(sec.Key("REGION").String()); v != "" { - out.S3Region = v - } - out.S3Bucket = strings.TrimSpace(sec.Key("BUCKET").String()) - out.S3AccessKey = strings.TrimSpace(sec.Key("ACCESS_KEY").String()) - out.S3SecretKey = strings.TrimSpace(sec.Key("SECRET_KEY").String()) - out.S3PublicBaseURL = strings.TrimSpace(sec.Key("PUBLIC_BASE_URL").String()) - out.S3Prefix = strings.TrimSpace(sec.Key("PREFIX").String()) - if sec.HasKey("FORCE_PATH_STYLE") { - out.S3ForcePathStyle = sec.Key("FORCE_PATH_STYLE").MustBool(true) - } - } - return out, nil } @@ -121,18 +71,10 @@ func writeAppINI(path string, s fileSettings) error { return err } - storageType := strings.TrimSpace(s.StorageType) - if storageType == "" { - storageType = StorageTypeLocal - } - region := strings.TrimSpace(s.S3Region) - if region == "" { - region = "us-east-1" - } - var b strings.Builder b.WriteString("; 姜十三论坛 Jiang13 Forum — 配置文件(风格类似 Gitea app.ini)\n") b.WriteString("; 修改后重启进程/服务生效。命令行参数优先级高于本文件。\n") + b.WriteString("; OIDC / 邮件 / Gitea 同步 / 对象存储等请在管理后台「系统设置」配置。\n") b.WriteString(";\n") b.WriteString("; 默认位置:程序工作目录下的 app.ini\n") b.WriteString("; 可用 --config / --work-path 覆盖。\n") @@ -140,11 +82,6 @@ func writeAppINI(path string, s fileSettings) error { b.WriteString("[server]\n") b.WriteString("HTTP_PORT = ") b.WriteString(strconv.Itoa(s.Port)) - b.WriteString("\n") - b.WriteString("; 对外公网根地址(无尾斜杠),OIDC Issuer / Discovery 依赖此项\n") - b.WriteString("; 例:https://bbs.iioio.com\n") - b.WriteString("ROOT_URL = ") - b.WriteString(s.RootURL) b.WriteString("\n\n") b.WriteString("[paths]\n") b.WriteString("; 相对路径相对于工作目录(默认可执行文件所在目录)\n") @@ -155,66 +92,6 @@ func writeAppINI(path string, s fileSettings) error { b.WriteString("; 留空则自动生成并持久化到 data/.jwt_secret(勿把生产密钥提交到仓库)\n") b.WriteString("JWT_SECRET = ") b.WriteString(s.JWTSecret) - b.WriteString("\n\n") - b.WriteString("[oauth]\n") - b.WriteString("; 作为 OIDC Provider 时,给 Gitea 等客户端使用的凭据(P0 写死在配置)\n") - b.WriteString("; Gitea 认证源名称需与回调路径一致,例如名称 jiang13 对应:\n") - b.WriteString("; https://git.iioio.com/user/oauth2/jiang13/callback\n") - b.WriteString("CLIENT_ID = ") - b.WriteString(s.OAuthClientID) - b.WriteString("\n") - b.WriteString("CLIENT_SECRET = ") - b.WriteString(s.OAuthClientSecret) - b.WriteString("\n") - b.WriteString("; 多个回调用逗号分隔\n") - b.WriteString("REDIRECT_URIS = ") - b.WriteString(s.OAuthRedirectURIs) - b.WriteString("\n\n") - b.WriteString("[gitea]\n") - b.WriteString("; 可选:同步会员公开仓库到侧栏 /projects\n") - b.WriteString("BASE_URL = ") - b.WriteString(s.GiteaBaseURL) - b.WriteString("\n") - b.WriteString("TOKEN = ") - b.WriteString(s.GiteaToken) - b.WriteString("\n") - b.WriteString("SYNC_ENABLED = ") - b.WriteString(strconv.FormatBool(s.GiteaSyncEnabled)) - b.WriteString("\n\n") - b.WriteString("[storage]\n") - b.WriteString("; 可选种子:日常请在管理后台「系统设置 → 对象存储」配置(保存即生效)\n") - b.WriteString("TYPE = ") - b.WriteString(storageType) - b.WriteString("\n") - b.WriteString("; 以下仅作首次种子(MinIO / 七牛 / 又拍 / 阿里云 OSS 等)\n") - b.WriteString("; ENDPOINT = https://s3.example.com\n") - b.WriteString("ENDPOINT = ") - b.WriteString(s.S3Endpoint) - b.WriteString("\n") - b.WriteString("REGION = ") - b.WriteString(region) - b.WriteString("\n") - b.WriteString("BUCKET = ") - b.WriteString(s.S3Bucket) - b.WriteString("\n") - b.WriteString("ACCESS_KEY = ") - b.WriteString(s.S3AccessKey) - b.WriteString("\n") - b.WriteString("SECRET_KEY = ") - b.WriteString(s.S3SecretKey) - b.WriteString("\n") - b.WriteString("; 公开访问根 URL(无尾斜杠);上传后返回 PUBLIC_BASE_URL/avatars/xxx.jpg\n") - b.WriteString("; PUBLIC_BASE_URL = https://cdn.example.com/forum\n") - b.WriteString("PUBLIC_BASE_URL = ") - b.WriteString(s.S3PublicBaseURL) - b.WriteString("\n") - b.WriteString("; 对象 key 前缀(可选),如 forum/\n") - b.WriteString("PREFIX = ") - b.WriteString(s.S3Prefix) - b.WriteString("\n") - b.WriteString("; MinIO 等多为 true;AWS S3 官方多为 false\n") - b.WriteString("FORCE_PATH_STYLE = ") - b.WriteString(strconv.FormatBool(s.S3ForcePathStyle)) b.WriteString("\n") return os.WriteFile(path, []byte(b.String()), 0644) diff --git a/docs/introduction.md b/docs/introduction.md index 40ae6e7..1f60e41 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -95,7 +95,7 @@ Feed 可按 **最新发帖 / 最新回复 / 热门讨论** 切换。浅色与暗 - **单二进制**:静态资源已内嵌,不必再配 Nginx 专门反代前端文件; - **零外部数据库**:SQLite 落在数据目录,备份就是拷贝文件; -- **app.ini 配置**:风格类似 Gitea,端口、数据目录、站点根地址一眼能改; +- **app.ini 配置**:风格类似 Gitea,端口与数据目录一眼能改;业务项在管理后台配置; - **系统服务**:内置 Linux systemd / Windows Service 安装与启停; - **跨平台**:Windows / Linux / macOS 均可编译与运行。 diff --git a/handler/seo.go b/handler/seo.go index 5c02faa..5b723bd 100644 --- a/handler/seo.go +++ b/handler/seo.go @@ -409,12 +409,7 @@ func (h *Handlers) userPageMeta(base, siteName, defaultImage string, user *model } func (h *Handlers) publicBaseURL(c *gin.Context) string { - cfgRoot := "" - if h.Cfg != nil { - cfgRoot = h.Cfg.RootURL - } - origin := requestOrigin(c) - return h.Settings.SitePublicBaseURL(cfgRoot, origin) + return h.Settings.SitePublicBaseURL(requestOrigin(c)) } func requestOrigin(c *gin.Context) string { diff --git a/router/router.go b/router/router.go index 1513ead..ffa9679 100644 --- a/router/router.go +++ b/router/router.go @@ -6,7 +6,6 @@ import ( "net/http" "os" "path/filepath" - "strings" "github.com/gin-gonic/gin" "git.iioio.com/freefire/jiang13-forum/config" @@ -31,24 +30,6 @@ func Setup(cfg *config.Config) (*gin.Engine, error) { filter.LoadFromFile(cfg.FilterWordsPath()) settingsSvc := service.NewForumSettingsService() - settingsSvc.SeedOIDCFromINI( - cfg.RootURL, - cfg.OAuthClientID, - cfg.OAuthClientSecret, - strings.Join(cfg.OAuthRedirectURIs, ","), - ) - settingsSvc.SeedGiteaFromINI(cfg.GiteaBaseURL, cfg.GiteaToken, cfg.GiteaSyncEnabled) - settingsSvc.SeedStorageFromINI( - cfg.StorageType, - cfg.S3.Endpoint, - cfg.S3.Region, - cfg.S3.Bucket, - cfg.S3.AccessKey, - cfg.S3.SecretKey, - cfg.S3.PublicBaseURL, - cfg.S3.Prefix, - cfg.S3.ForcePathStyle, - ) // SPA 入口 HTML 注入标题与品牌 JSON,避免刷新时先闪默认文案 embed_static.SetSPADocumentTitle(func() string { return settingsSvc.SiteBranding().DocumentTitle() diff --git a/service/seo.go b/service/seo.go index 7be2802..6eda3cd 100644 --- a/service/seo.go +++ b/service/seo.go @@ -23,12 +23,9 @@ type SitemapURL struct { } // SitePublicBaseURL 公开站点根地址(无尾斜杠) -// 优先 OIDC / 配置中的 RootURL,否则根据请求 Host 推断 -func (s *ForumSettingsService) SitePublicBaseURL(cfgRoot, requestOrigin string) string { +// 优先管理后台 OIDC 中的 ROOT_URL,否则根据请求 Host 推断 +func (s *ForumSettingsService) SitePublicBaseURL(requestOrigin string) string { root := normalizeRootURL(s.getString(SettingOIDCRootURL, "")) - if root == "" { - root = normalizeRootURL(cfgRoot) - } if root == "" { root = normalizeRootURL(requestOrigin) } diff --git a/service/settings.go b/service/settings.go index c1b1bc2..f38a88b 100644 --- a/service/settings.go +++ b/service/settings.go @@ -716,33 +716,6 @@ func (s *ForumSettingsService) UpdateOIDCConfig(in OIDCConfig) error { return nil } -// SeedOIDCFromINI 若库中尚未配置,则用 app.ini 种子一次(直写 oauth_clients) -func (s *ForumSettingsService) SeedOIDCFromINI(rootURL, clientID, clientSecret, redirectURIsCSV string) { - rootURL = normalizeRootURL(rootURL) - clientID = strings.TrimSpace(clientID) - clientSecret = strings.TrimSpace(clientSecret) - uris := normalizeRedirectURIs(redirectURIsCSV) - if rootURL == "" && clientID == "" && clientSecret == "" && uris == "" { - return - } - if s.getString(SettingOIDCRootURL, "") == "" && rootURL != "" { - _ = s.setString(SettingOIDCRootURL, rootURL) - } - if CountEnabledOAuthClients() == 0 && clientID != "" && clientSecret != "" && uris != "" { - _, _ = s.CreateOAuthClient(OAuthClientInput{ - ClientID: clientID, - Name: "Gitea", - RedirectURIs: uris, - ClientSecret: clientSecret, - }) - } - if s.getString(SettingOIDCEnabled, "0") == "0" && - s.getString(SettingOIDCRootURL, "") != "" && - CountEnabledOAuthClients() > 0 { - _ = s.setString(SettingOIDCEnabled, "1") - } -} - // GiteaSyncConfig 读取 Gitea 同步配置(含 Token 明文,供服务内部使用) func (s *ForumSettingsService) GiteaSyncConfig() GiteaSyncConfig { interval, _ := strconv.Atoi(s.getString(SettingGiteaSyncIntervalMin, "60")) @@ -811,26 +784,6 @@ func (s *ForumSettingsService) UpdateGiteaSyncConfig(in GiteaSyncConfig) error { return nil } -// SeedGiteaFromINI 若库中尚未配置,则用 app.ini 种子一次 -func (s *ForumSettingsService) SeedGiteaFromINI(baseURL, token string, enabled bool) { - baseURL = normalizeRootURL(baseURL) - token = strings.TrimSpace(token) - if baseURL == "" && token == "" && !enabled { - return - } - if s.getString(SettingGiteaBaseURL, "") == "" && baseURL != "" { - _ = s.setString(SettingGiteaBaseURL, baseURL) - } - if s.getString(SettingGiteaToken, "") == "" && token != "" { - _ = s.setString(SettingGiteaToken, token) - } - if enabled && s.getString(SettingGiteaSyncEnabled, "0") == "0" && - s.getString(SettingGiteaBaseURL, "") != "" && - s.getString(SettingGiteaToken, "") != "" { - _ = s.setString(SettingGiteaSyncEnabled, "1") - } -} - // StorageConfig 读取上传存储配置(含密钥明文,供内部使用) func (s *ForumSettingsService) StorageConfig() StorageConfig { secret := s.getString(SettingStorageSecretKey, "") @@ -934,59 +887,6 @@ func (s *ForumSettingsService) UpdateStorageConfig(in StorageConfig) error { return nil } -// SeedStorageFromINI 若库中尚未配置 S3,则用 app.ini 种子一次 -func (s *ForumSettingsService) SeedStorageFromINI(storageType, endpoint, region, bucket, accessKey, secretKey, publicBaseURL, prefix string, forcePathStyle bool) { - storageType = normalizeStorageType(storageType) - endpoint = strings.TrimSpace(endpoint) - region = strings.TrimSpace(region) - bucket = strings.TrimSpace(bucket) - accessKey = strings.TrimSpace(accessKey) - secretKey = strings.TrimSpace(secretKey) - publicBaseURL = normalizeRootURL(publicBaseURL) - prefix = normalizeObjectPrefix(prefix) - - if storageType == "local" && endpoint == "" && bucket == "" && accessKey == "" && secretKey == "" && publicBaseURL == "" { - return - } - - if s.getString(SettingStorageEndpoint, "") == "" && endpoint != "" { - _ = s.setString(SettingStorageEndpoint, endpoint) - } - if s.getString(SettingStorageRegion, "") == "" && region != "" { - _ = s.setString(SettingStorageRegion, region) - } - if s.getString(SettingStorageBucket, "") == "" && bucket != "" { - _ = s.setString(SettingStorageBucket, bucket) - } - if s.getString(SettingStorageAccessKey, "") == "" && accessKey != "" { - _ = s.setString(SettingStorageAccessKey, accessKey) - } - if s.getString(SettingStorageSecretKey, "") == "" && secretKey != "" { - _ = s.setString(SettingStorageSecretKey, secretKey) - } - if s.getString(SettingStoragePublicBaseURL, "") == "" && publicBaseURL != "" { - _ = s.setString(SettingStoragePublicBaseURL, publicBaseURL) - } - if s.getString(SettingStoragePrefix, "") == "" && prefix != "" { - _ = s.setString(SettingStoragePrefix, prefix) - } - // 仅当库中仍为默认 local 且 INI 明确为 s3、且关键字段齐全时切到 s3 - if storageType == "s3" && - normalizeStorageType(s.getString(SettingStorageType, "local")) == "local" && - s.getString(SettingStorageEndpoint, "") != "" && - s.getString(SettingStorageBucket, "") != "" && - s.getString(SettingStorageAccessKey, "") != "" && - s.getString(SettingStorageSecretKey, "") != "" && - s.getString(SettingStoragePublicBaseURL, "") != "" { - _ = s.setString(SettingStorageType, "s3") - if forcePathStyle { - _ = s.setString(SettingStorageForcePathStyle, "1") - } else { - _ = s.setString(SettingStorageForcePathStyle, "0") - } - } -} - // SiteBranding 读取站点品牌配置 func (s *ForumSettingsService) SiteBranding() SiteBranding { name := strings.TrimSpace(s.getString(SettingSiteName, siteBrandingDefaults[SettingSiteName]))