Documentation
¶
Overview ¶
Package settings 提供统一的声明式设置:分组 Schema、值存储、校验与读取。
每个模块经 app.SettingsProvider 声明自己的分组;本模块在 Start 时汇总全部分组并编译 Schema, 其他模块经 From(app) 取得 Service 读取有效值。装配顺序上本模块应排在最前。
Index ¶
- Constants
- Variables
- func Merge(defaults, overrides map[string]any) map[string]any
- func RejectSecrets(scope string, f *form.Form) error
- type Group
- type Handler
- type Module
- func (m *Module) Migrations() fs.FS
- func (m *Module) Name() string
- func (m *Module) Navigation() app.Navigation
- func (m *Module) Permissions() []app.Permission
- func (m *Module) Register(a *app.App) error
- func (m *Module) Routes(r app.Router)
- func (m *Module) Settings() []app.SettingGroup
- func (m *Module) Start(context.Context) error
- type Service
- func (s *Service) Effective(ctx context.Context, name string) (map[string]any, error)
- func (s *Service) Get(ctx context.Context, name string, out any) error
- func (s *Service) Group(name string) (*Group, bool)
- func (s *Service) Groups() []*Group
- func (s *Service) HasSecrets() bool
- func (s *Service) Public(ctx context.Context) (map[string]map[string]any, error)
- func (s *Service) RegisterGroups(groups []app.SettingGroup) error
- func (s *Service) SetKeyring(k *secret.Keyring)
- func (s *Service) SetLogger(l *slog.Logger)
- func (s *Service) Slug(ctx context.Context, text string) string
- func (s *Service) Update(ctx context.Context, name string, values map[string]any) error
- type Site
- type Store
- type ValidationError
- type Validator
- type ValueStore
Constants ¶
const ( SlugUnicode = "unicode" SlugPinyin = "pinyin" )
slug 生成策略。
const GroupSite = "site"
GroupSite 是站点基本信息分组。
const Icon = "settings"
Icon 是设置在侧边栏与页内的图标名(见 console/src/lib/icons.ts)。
const Name = "settings"
Name 是模块名,也是迁移版本表后缀与 App.Provide 的键。
const Path = "/" + Name
Path 是设置页在 Console 内的路径,也是本模块接口在 Console 平面下的前缀。
页面与接口共用这一段是刻意的:`/settings/<分组>` 既是接口地址,也是 「展开那一块」的页面地址,两者写成两个常量迟早会漂开。
Variables ¶
var ( // ErrUnknownGroup 表示分组未注册。 ErrUnknownGroup = errors.New("设置分组不存在") )
错误哨兵。
Functions ¶
Types ¶
type Group ¶
type Group struct {
app.SettingGroup
// contains filtered or unexported fields
}
Group 是已编译的设置分组。
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler 提供设置的 Console 与 Public 接口。
type Module ¶
type Module struct {
// contains filtered or unexported fields
}
Module 是设置模块。
func (*Module) Navigation ¶
func (m *Module) Navigation() app.Navigation
Navigation 实现 app.NavigationProvider:侧边栏一个入口,分组做命令面板的直达项。
侧边栏只有「站点设置」一条:所有分组现在铺在同一页上(见 console 的 settings 页), 再给每个分组画一条侧栏菜单,等于让站长先选一个分组才能开始配置, 而他要改的两项很可能分属两组。
分组仍各自下发一条 Hidden 的菜单项。Hidden 的项不进侧边栏,但仍进命令面板与标题映射 (见 app.NavItem.Hidden):Ctrl+K 里搜「邮件」应当能直接跳到邮件那一块, 而这正是把入口收成一个之后唯一会丢掉的东西。
用推导而不是逐个手写:设置分组可能来自模块,也可能来自主题或插件, 手写一份清单就必然有漏。此前 comment 分组在后端存在而在侧边栏没有入口, 正是因为那份清单是手写的。
func (*Module) Permissions ¶
func (m *Module) Permissions() []app.Permission
Permissions 实现 app.PermissionProvider。
func (*Module) Settings ¶
func (m *Module) Settings() []app.SettingGroup
Settings 实现 app.SettingsProvider:本模块自带 site 分组。
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service 是设置的读写入口:持有已注册分组、校验器与缓存。
func From ¶
From 取回设置服务;settings 模块未装配(或尚未注册)时返回 nil。
调用方须能在 nil 时退化(如 slug 策略退回 unicode),以便单独测试其他模块。
func NewService ¶
func NewService(store ValueStore) *Service
NewService 构造 Service;store 可为 nil(仅做 Schema 校验的场景)。
func (*Service) HasSecrets ¶
HasSecrets 报告已注册分组里有没有口令字段。
模块据此决定要不要去加载主密钥:没有口令字段就不碰密钥文件, 免得每个只装了本站的目录里都躺着一个没人知道该不该备份的 secret.key。
func (*Service) RegisterGroups ¶
func (s *Service) RegisterGroups(groups []app.SettingGroup) error
RegisterGroups 编译并登记分组。任一分组的 Schema 或 Defaults 不合法即整体失败: 那是模块作者的错误,必须在启动时暴露。
func (*Service) SetKeyring ¶
SetKeyring 注入主密钥,供口令字段加解密。须在开始服务之前调用。
type Site ¶
type Site struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Description string `json:"description"`
URL string `json:"url"`
Language string `json:"language"`
Timezone string `json:"timezone"`
LogoURL string `json:"logoUrl"`
FaviconURL string `json:"faviconUrl"`
SlugStrategy string `json:"slugStrategy"`
PageSize int `json:"pageSize"`
}
Site 是 site 分组的有效值。
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store 提供设置值的持久化操作。
type ValidationError ¶
type ValidationError struct {
Details []httpx.ErrorDetail
}
ValidationError 表示值未通过 Schema 或 Go 侧校验,Details 逐条定位到字段。
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator 是一份编译好的表单校验器,供设置分组之外的场景复用。
主题设置的声明由主题包的 settings.yaml 给出、随主题安装而变, 不能走 RegisterGroups 那条「启动期固定登记」的路径;但校验语义必须与站点设置完全一致, 否则主题作者要面对两套规则。故把编译与校验单独暴露出来。
校验分两步,缺一不可:JSON Schema 判「值是否合法」,Form.Missing 判 「此刻该显示的必填项是否都填了」。后者只能由 Form 回答——条件依赖的可见性是 它的知识,Schema 里只留了一句 x-show-if 的提示。
func (*Validator) DefaultValues ¶
DefaultValues 返回缺省值的副本。
type ValueStore ¶
type ValueStore interface {
// Load 读取分组的已保存值;从未保存过时返回空对象。
Load(ctx context.Context, name string) (map[string]any, error)
// Save 覆盖式写入分组的值。
Save(ctx context.Context, name string, values map[string]any) error
}
ValueStore 是设置值的持久化接口。
抽成接口不是为了将来换数据库——它的实现只有 Store 一个——而是为了让 「口令进库是密文」这类断言能在纯内存的测试里验:真去连一个 PG 才能验加密, 代价是这条最要紧的性质在最常见的开发环境里跑不起来。