- 全项目版本号统一:v3.x 迭代号(26.9/26.9/26.9/26.9 及裸 v2/v3)→ 26.9, 覆盖 Go 注释 / 文档 / openapi.yaml / README×4 / 前端源码(80+ 处) - v31_test.go 更名 custom_code_test.go;TestV2AccessorDefaults → TestKVAccessorDefaults - docs/api/00-overview.md 更新日志合并为单条 26.9 条目(修复错位拼接) - .goreleaser.yaml 头部注释与实际一致(Pro 2.18.1 / GITEA_TOKEN / semver tag 要求) - CI:release-image.yml → ci.yml,仅保留 vet+test 门禁; 镜像发布移交 GoReleaser Pro(原 build-push 的 tag 校验与 26.9 版本方案冲突,历史 9 次失败) - 前端重建:server/web/dist 与 web-embed 同步(docs 文案嵌入更新)
This commit is contained in:
@@ -78,7 +78,7 @@ func registerAdmin(r *gin.Engine, d *Deps) {
|
||||
authed.PATCH("/settings/password", d.adminChangePassword)
|
||||
authed.POST("/settings/password", d.adminChangePassword)
|
||||
|
||||
// v3 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎)
|
||||
// 26.9 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎)
|
||||
authed.POST("/storage/switch", d.adminStorageSwitch)
|
||||
|
||||
// 审计日志查询(需求 ③;logs 为 list 的别名)
|
||||
@@ -404,7 +404,7 @@ func buildAdminFileItem(fc *model.FileCodes, now time.Time) gin.H {
|
||||
"hasDownloadLimit": fc.ExpiredCount >= 0, "has_download_limit": fc.ExpiredCount >= 0,
|
||||
"isPermanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0, "is_permanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0,
|
||||
"remainingDownloads": remaining, "remaining_downloads": remaining,
|
||||
"engine": fc.Engine, // v3:归属引擎(管理端展示/排查用)
|
||||
"engine": fc.Engine, // 26.9:归属引擎(管理端展示/排查用)
|
||||
}
|
||||
if fc.FileHash != nil {
|
||||
item["fileHash"] = *fc.FileHash
|
||||
@@ -784,7 +784,7 @@ func (d *Deps) adminFilePreview(c *gin.Context) {
|
||||
// ============ 配置 ============
|
||||
|
||||
// configKeys 管理端可见/可改的配置键(不含 jwt_secret;admin_token 屏蔽展示)。
|
||||
// v2 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。
|
||||
// 26.9 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。
|
||||
var configKeys = []string{
|
||||
"site_name", "name", "description", "page_explain", "keywords",
|
||||
"notify_title", "notify_content", "notify_enabled", "logo_url", "favicon_url",
|
||||
@@ -794,11 +794,11 @@ var configKeys = []string{
|
||||
"code_generate_type", "enableChunk",
|
||||
"uploadMinute", "uploadCount", "errorMinute", "errorCount",
|
||||
"loginCount", "loginMinute",
|
||||
"opacity", "background", "showAdminAddr", "robotsText", "site_domain", // v3.1:站点对外域名
|
||||
"upload_rate", "download_rate", // v3.2:上下行带宽字节/秒(0=不限速)
|
||||
"opacity", "background", "showAdminAddr", "robotsText", "site_domain", // 26.9:站点对外域名
|
||||
"upload_rate", "download_rate", // 26.9:上下行带宽字节/秒(0=不限速)
|
||||
"adminSessionExpire", "storage_path", "local_storage_path",
|
||||
"file_storage",
|
||||
// v3 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过)
|
||||
// 26.9 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过)
|
||||
"storage_engine",
|
||||
"local_storage_path",
|
||||
"webdav_url", "webdav_root_path", "webdav_username", "webdav_password",
|
||||
@@ -807,7 +807,7 @@ var configKeys = []string{
|
||||
}
|
||||
|
||||
// intConfigKeys 需按 schema 边界校验的整型键(adminConfigUpdate 归一化用)。
|
||||
// v1 既有键保留原语义;v2 新增键(max_file_size/max_save_count/notify_enabled)
|
||||
// v1 既有键保留原语义;26.9 新增键(max_file_size/max_save_count/notify_enabled)
|
||||
// 的边界来自 settings.KVSchema(单一事实来源在 config/schema.go)。
|
||||
var intConfigKeys = []string{
|
||||
"openUpload", "enableChunk", "showAdminAddr", "storageLimit",
|
||||
@@ -815,7 +815,7 @@ var intConfigKeys = []string{
|
||||
"loginCount", "loginMinute", "max_save_seconds", "uploadSize",
|
||||
"adminSessionExpire",
|
||||
"max_save_count", "max_file_size", "notify_enabled",
|
||||
"upload_rate", "download_rate", // v3.2
|
||||
"upload_rate", "download_rate", // 26.9
|
||||
}
|
||||
|
||||
// validateConfigValue 按 settings.KVSchema 校验单个配置值:
|
||||
@@ -905,7 +905,7 @@ func toStrSlice(v any) []string {
|
||||
}
|
||||
|
||||
// adminConfigGet 读取配置(对齐参考 get_config:admin_token 屏蔽、jwt_secret 不下发)。
|
||||
// v3:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。
|
||||
// 26.9:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。
|
||||
func (d *Deps) adminConfigGet(c *gin.Context) {
|
||||
cfg := d.Cfg
|
||||
out := gin.H{}
|
||||
@@ -925,7 +925,7 @@ func (d *Deps) adminConfigGet(c *gin.Context) {
|
||||
}
|
||||
// jwt_secret 永不下发
|
||||
delete(out, "jwt_secret")
|
||||
// 引擎运行时状态(v3:热切换即时生效,无需重启)
|
||||
// 引擎运行时状态(26.9:热切换即时生效,无需重启)
|
||||
out["_engine_hint"] = gin.H{
|
||||
"storage_backend": d.Store.CurrentName(),
|
||||
"engines": gin.H{"local": true, "s3": true, "webdav": true},
|
||||
@@ -973,7 +973,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
||||
if !known {
|
||||
continue
|
||||
}
|
||||
// 类型归一 + schema 边界校验(v2:数值/字符串长度/列表键统一走 KVSchema)
|
||||
// 类型归一 + schema 边界校验(26.9:数值/字符串长度/列表键统一走 KVSchema)
|
||||
isInt := false
|
||||
for _, key := range intConfigKeys {
|
||||
if key == k {
|
||||
@@ -1067,7 +1067,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
||||
dbPatch["jwt_secret"] = settings.GenerateJWTSecret()
|
||||
}
|
||||
|
||||
// v3.1:site_domain 规范化(http(s)://host[:port];空=用当前地址)
|
||||
// 26.9:site_domain 规范化(http(s)://host[:port];空=用当前地址)
|
||||
if raw, ok := dbPatch["site_domain"]; ok {
|
||||
sv, _ := raw.(string)
|
||||
normalized, err := normalizeSiteDomain(sv)
|
||||
@@ -1078,7 +1078,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
||||
dbPatch["site_domain"] = normalized
|
||||
}
|
||||
|
||||
// v3 引擎键处理:参数键与 storage_engine 分离。
|
||||
// 26.9 引擎键处理:参数键与 storage_engine 分离。
|
||||
// 1) storage_engine 只接受合法枚举;
|
||||
// 2) 敏感凭据键空串/掩码=不修改(避免管理端表单回显把密钥抹掉);
|
||||
// 3) 先持久化普通键+参数键 → Invalidate 对应引擎缓存 → 再尝试 Switch 新引擎;
|
||||
@@ -1152,7 +1152,7 @@ func engineOfParamKeys(patch map[string]any) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// adminStorageSwitch v3 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。
|
||||
// adminStorageSwitch 26.9 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。
|
||||
// 成功:持久化 storage_engine KV 并返回当前引擎;失败:503 且原引擎不变。
|
||||
func (d *Deps) adminStorageSwitch(c *gin.Context) {
|
||||
var body struct {
|
||||
@@ -1302,7 +1302,7 @@ func (d *Deps) fileByID(c *gin.Context, id int64) (*model.FileCodes, error) {
|
||||
// deleteFileCode 删除分享记录与存储文件(文本分享无存储文件)。
|
||||
func (d *Deps) deleteFileCode(c *gin.Context, fc *model.FileCodes) error {
|
||||
if fc.Text == nil && fc.FilePath != nil && fc.UUIDFileName != nil {
|
||||
// v3:删除走文件归属引擎(旧引擎里的文件也要能删掉)
|
||||
// 26.9:删除走文件归属引擎(旧引擎里的文件也要能删掉)
|
||||
if delStore, dErr := d.storeFor(fc.Engine); dErr == nil {
|
||||
if err := delStore.DeleteFile(c.Request.Context(), fileSavePath(fc)); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||
return errInternal("存储文件删除失败: " + err.Error())
|
||||
|
||||
Reference in New Issue
Block a user