feat(wallpaper): 平台配置脱敏回显 + 图库元字段补齐
Some checks failed
Build and Deploy (service.xpcool.com) / build-and-deploy (push) Failing after 28s

后台做完界面才发现的两个接口缺口:

1) 平台配置不回显 → 用户只改一个开关,defaultQuery / purity / baseUrl
   会被一起清空(SaveSource 只对 apiKey/apiSecret 做了"留空即保留")。
   现在 Sources() 返回脱敏后的 config(凭据一律置空,是否已设置由
   HasApiKey 表达),前台接口不下发该字段 —— toAPISource 加 withConfig
   开关,公开接口没必要泄露自建反代地址等内部信息。

2) Item 缺 enabled / sort / remark → 后台列表无法显示启用状态,
   编辑抽屉也拿不到当前值。补齐字段并在 library.toItem 中填充,
   开源平台条目保持默认值(这三个字段对前台无意义)。

另抽出 sanitizeSourceConfig 统一处理凭据抹除,避免以后再加配置项时漏抹。
This commit is contained in:
夏犀麟 2026-09-14 01:48:23 +08:00
parent 582485ba2b
commit d0a084b80e
7 changed files with 87 additions and 19 deletions

View File

@ -36,6 +36,11 @@ type Item struct {
Tags string `json:"tags"`
Category string `json:"category"`
Filesize int64 `json:"filesize"`
// Enabled / Sort / Remark 仅自建图库有值,供后台列表展示与就地编辑;
// 开源平台条目恒为默认值,前台不关心。
Enabled int `json:"enabled"`
Sort int `json:"sort"`
Remark string `json:"remark"`
CreatedAt string `json:"createdAt"`
}
@ -50,6 +55,22 @@ type SourceInfo struct {
Hint string `json:"hint"` // 不可用原因
Remark string `json:"remark"`
HasApiKey bool `json:"hasApiKey"` // 只回传「是否已设置」,不回传明文
// Config 是脱敏后的平台配置,供后台编辑界面回显(前台接口不下发)。
// apiKey / apiSecret 恒为空串 —— 提交时留空即表示「不修改已保存的 Key」。
Config *SourceConfig `json:"config"`
}
// SourceConfig 是平台可配置项,与 dto.WallpaperSourceConfig 一一对应。
type SourceConfig struct {
ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"`
DefaultQuery string `json:"defaultQuery"`
// Purity 分级过滤Wallhavensfw / sketchy / nsfw默认只放 sfw。
Purity string `json:"purity"`
// Categories 分类Wallhavengeneral/anime/people 组合。
Categories string `json:"categories"`
// BaseUrl 覆盖平台接口地址(自建反代或镜像)。
BaseUrl string `json:"baseUrl"`
}
// ---------------------------------------------------------------------------

View File

@ -167,7 +167,9 @@ func (c *Controller) SourceList(ctx context.Context, req *wallpaperv1.AdminSourc
}
out := make([]*wallpaperv1.SourceInfo, 0, len(list))
for i := range list {
out = append(out, toAPISource(&list[i]))
// 后台带配置:编辑界面需要回显 defaultQuery / purity / baseUrl 等,
// 否则用户只改开关也会把这些字段一并清空。
out = append(out, toAPISource(&list[i], true))
}
return &wallpaperv1.AdminSourceListRes{List: out}, nil
}

View File

@ -37,6 +37,9 @@ func toAPIItem(v *dto.WallpaperItem) *wallpaperv1.Item {
Tags: v.Tags,
Category: v.Category,
Filesize: v.Filesize,
Enabled: v.Enabled,
Sort: v.Sort,
Remark: v.Remark,
CreatedAt: v.CreatedAt,
}
}
@ -51,8 +54,11 @@ func toAPIItems(list []dto.WallpaperItem) []*wallpaperv1.Item {
}
// toAPISource 把平台信息转成接口输出结构。
func toAPISource(v *dto.WallpaperSourceInfo) *wallpaperv1.SourceInfo {
return &wallpaperv1.SourceInfo{
//
// withConfig 控制是否下发脱敏后的配置:后台需要它回显,前台不需要
// (自建反代地址之类属于内部信息,没必要给公网浏览器)。
func toAPISource(v *dto.WallpaperSourceInfo, withConfig bool) *wallpaperv1.SourceInfo {
out := &wallpaperv1.SourceInfo{
Code: v.Code,
Name: v.Name,
Enabled: v.Enabled,
@ -63,4 +69,15 @@ func toAPISource(v *dto.WallpaperSourceInfo) *wallpaperv1.SourceInfo {
Remark: v.Remark,
HasApiKey: v.HasApiKey,
}
if withConfig && v.Config != nil {
out.Config = &wallpaperv1.SourceConfig{
ApiKey: v.Config.ApiKey,
ApiSecret: v.Config.ApiSecret,
DefaultQuery: v.Config.DefaultQuery,
Purity: v.Config.Purity,
Categories: v.Config.Categories,
BaseUrl: v.Config.BaseUrl,
}
}
return out
}

View File

@ -37,7 +37,8 @@ func (c *OpenController) Sources(ctx context.Context, req *wallpaperv1.OpenSourc
out := make([]*wallpaperv1.SourceInfo, 0, len(list))
for i := range list {
out = append(out, toAPISource(&list[i]))
// 前台不带平台配置:公开接口没必要下发 baseUrl 等内部信息。
out = append(out, toAPISource(&list[i], false))
}
return &wallpaperv1.OpenSourceListRes{List: out, MineCount: mineCount}, nil
}

View File

@ -37,6 +37,11 @@ type WallpaperItem struct {
Tags string `json:"tags"`
Category string `json:"category"`
Filesize int64 `json:"filesize"`
// Enabled / Sort / Remark 只有自建图库有值(开源平台条目恒为默认值),
// 后台列表要显示启用状态并支持就地编辑,前台不关心这三个字段。
Enabled int `json:"enabled"`
Sort int `json:"sort"`
Remark string `json:"remark"`
CreatedAt string `json:"createdAt"`
}
@ -54,6 +59,11 @@ type WallpaperSourceInfo struct {
Available bool `json:"available"`
Hint string `json:"hint"` // 不可用原因,可用时为空
Remark string `json:"remark"`
// Config 是脱敏后的平台配置,供后台编辑界面回显。
// apiKey / apiSecret 一律置空不回传明文,是否已设置由 HasApiKey 表达;
// 界面上留空提交即表示「不修改已保存的 Key」。
// 前台接口不下发本字段(避免暴露自建反代地址等内部信息)。
Config *WallpaperSourceConfig `json:"config"`
// Config 是脱敏后的配置apiKey 只回传是否已设置,不回传明文)。
HasApiKey bool `json:"hasApiKey"`
}

View File

@ -161,6 +161,9 @@ func (s *service) toItem(e *entity.Wallpaper) dto.WallpaperItem {
Tags: e.Tags,
Category: e.Category,
Filesize: e.Filesize,
Enabled: e.Enabled,
Sort: e.Sort,
Remark: e.Remark,
CreatedAt: e.CreatedAt,
}
}

View File

@ -48,6 +48,7 @@ func (s *service) Sources(ctx context.Context) ([]dto.WallpaperSourceInfo, error
Configured: !p.RequiresKey(),
Available: false,
Hint: "尚未在后台启用",
Config: sanitizeSourceConfig(dto.WallpaperSourceConfig{}),
})
continue
}
@ -61,6 +62,7 @@ func (s *service) Sources(ctx context.Context) ([]dto.WallpaperSourceInfo, error
Configured: configured,
Available: e.Enabled == 1 && configured,
Remark: e.Remark,
Config: sanitizeSourceConfig(cfg),
HasApiKey: cfg.ApiKey != "",
}
switch {
@ -216,3 +218,15 @@ func ParseSourceConfig(raw string) dto.WallpaperSourceConfig {
}
return cfg
}
// sanitizeSourceConfig 生成可安全下发给前端的配置副本:抹掉全部凭据。
//
// 后台需要它来「回显」用户此前填过的 defaultQuery / purity / baseUrl
// 否则用户只想改个开关也会把这些字段一起清空;
// 但凭据明文不能出后端,故这里只清 apiKey / apiSecret
// 是否已设置改由 HasApiKey 布尔表达。
func sanitizeSourceConfig(cfg dto.WallpaperSourceConfig) *dto.WallpaperSourceConfig {
cfg.ApiKey = ""
cfg.ApiSecret = ""
return &cfg
}