系统设置迁入后台:站点地址 / OAuth 凭据 / R2 存储 / 管理员账号,保存即生效无需重启
- 生效规则统一为「后台填了用后台的,没填回落环境变量」,老部署不改 env 照常跑 - config.Resolver:短缓存解析有效配置,存储后端按配置签名热重建;后台保存主动失效 - 秘密项(client secret / bot token / R2 密钥 / 管理员密码)接口永不回显明文, 只报「是否已配置、来自哪里」;留空保存 = 保持现值 - 管理员密码 bcrypt 入库,DB 哈希优先、显式设置的 env 密码作解锁后路; 后台改过密码后 admin/admin 开发模式立即失效 - 设置页新增「登录与存储」标签,基础信息加站点地址;秘密项带来源提示 - 监听地址 / 数据库 / 目录 / ONE_SECRET / Passkey 仍留环境变量(启动期依赖)
This commit is contained in:
1 parent
bf3ce934dd
commit
e68400b389
23 files changed
+957
-126
No files matched your search
+14
-12
@@ -26,14 +26,17 @@ import (
|
||||
|
||||
type API struct {
|
||||
Store *store.Store
|
||||
Cfg *config.Config
|
||||
Blobs storage.BlobStore // 文件上传的存储后端(main.go 装配,与 admin 共享)
|
||||
// Cfg 是环境变量配置(兜底值)。设置了 Res 时一律以 Res.Config()
|
||||
// 的有效配置为准(DB 里的系统设置优先);Res 为 nil(部分测试)才直接用它。
|
||||
Cfg *config.Config
|
||||
// Res 解析后台「系统设置」(DB 叠加 env),main.go 装配;可为 nil。
|
||||
Res *config.Resolver
|
||||
Blobs storage.BlobStore // 文件上传的存储后端(Res 设置时的兜底,二者取其一)
|
||||
// Thumbs 是缩略图磁盘缓存(main.go 装配,DataDir/.thumbnail_cache)。
|
||||
// 为 nil 时 /uploads/thumb/ 路由直接回原图。
|
||||
Thumbs *thumbs.Store
|
||||
// 评论区读者会话与 GitHub OAuth(main.go 装配)
|
||||
ReaderSessions *auth.ReaderSessions
|
||||
GH auth.GitHub
|
||||
// AdminSessions 是后台管理员会话(admin.Sessions 满足它)。
|
||||
// 前台访客登录时命中「已绑定给站主」的身份就靠它发后台会话,
|
||||
// 所以除了 Verify 还要 Issue/TTL。
|
||||
@@ -42,9 +45,6 @@ type API struct {
|
||||
Issue(user string) (string, time.Time)
|
||||
TTL() int
|
||||
}
|
||||
// 其余登录方式(main.go 装配,未配置的自动不开放)
|
||||
GG auth.Google
|
||||
TG auth.Telegram
|
||||
// Passkeys 是 WebAuthn 服务(main.go 装配;nil 表示未启用,路由不开放)
|
||||
Passkeys *auth.Passkeys
|
||||
|
||||
@@ -125,9 +125,11 @@ func (a *API) site(w http.ResponseWriter, r *http.Request) {
|
||||
httpx.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
// /api/site 是公开端点:管理员用户名只服务后台设置页,不外泄
|
||||
st.AdminUsername = ""
|
||||
st.AuthorAvatarURL = a.avatarURL(st.AuthorAvatarKey)
|
||||
// uploads_public_base 告诉前端哪些图片直链是自己的存储(可转 /uploads/thumb/ 缩略图)
|
||||
httpx.OK(w, map[string]any{"settings": st, "uploads_public_base": a.Cfg.UploadsPublicBase})
|
||||
httpx.OK(w, map[string]any{"settings": st, "uploads_public_base": a.cfg().UploadsPublicBase})
|
||||
}
|
||||
|
||||
// avatarURL 把 settings 里的头像 key 解析成可访问 URL。
|
||||
@@ -141,7 +143,7 @@ func (a *API) avatarURL(key string) string {
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return storage.FileURL(f.Store, f.Key, a.Cfg.UploadsPublicBase)
|
||||
return storage.FileURL(f.Store, f.Key, a.cfg().UploadsPublicBase)
|
||||
}
|
||||
|
||||
func listOptions(r *http.Request, defSize int) store.ListOptions {
|
||||
@@ -276,7 +278,7 @@ func (a *API) RSS(w http.ResponseWriter, r *http.Request) {
|
||||
httpx.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
base := strings.TrimSuffix(a.Cfg.SiteURL, "/")
|
||||
base := strings.TrimSuffix(a.siteURL(), "/")
|
||||
|
||||
feed := rssFeed{
|
||||
Version: "2.0",
|
||||
@@ -397,12 +399,12 @@ func (a *API) uploads(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// R2 且配了公开域名:302 到直链,后端不出流量
|
||||
if f.Store == "r2" && a.Cfg.UploadsPublicBase != "" {
|
||||
http.Redirect(w, r, storage.FileURL(f.Store, f.Key, a.Cfg.UploadsPublicBase), http.StatusFound)
|
||||
if f.Store == "r2" && a.cfg().UploadsPublicBase != "" {
|
||||
http.Redirect(w, r, storage.FileURL(f.Store, f.Key, a.cfg().UploadsPublicBase), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
rc, size, err := a.Blobs.Open(r.Context(), f.Key)
|
||||
rc, size, err := a.blobs().Open(r.Context(), f.Key)
|
||||
if err != nil {
|
||||
httpx.NotFound(w)
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user