邻接查询 tie-break 与 pinned 排序键与列表序对齐,补表驱动测试(round2 P1-1); GC DELETE 带条件复查,防 SELECT 与 DELETE 间恢复/引用竞态(P2-6); 备份经 umask 收紧创建即 0600,消除 chmod 前暴露窗口(P2-7); page_size 改 strconv.Atoi 全文解析,拒部分解析脏值(P2-13); OpenData 不迁移、不做版本守卫(D6/P1-5)。
72 lines
2.0 KiB
Go
72 lines
2.0 KiB
Go
package store
|
||
|
||
import (
|
||
"database/sql"
|
||
"errors"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// Settings 键白名单(§7.1 SettingsDTO 同源;admin_password_hash 永不进入 API 响应)。
|
||
const (
|
||
KeyAdminPasswordHash = "admin_password_hash"
|
||
KeySiteTitle = "site_title"
|
||
KeySiteDesc = "site_desc"
|
||
KeyPageSize = "page_size"
|
||
)
|
||
|
||
// GetSetting 读取单个设置。
|
||
func (s *Store) GetSetting(key string) (string, bool, error) {
|
||
var v string
|
||
err := s.db.QueryRow(`SELECT value FROM settings WHERE key=?`, key).Scan(&v)
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return "", false, nil
|
||
}
|
||
return v, err == nil, err
|
||
}
|
||
|
||
// SetSetting 写入单个设置(INSERT OR REPLACE)。
|
||
func (s *Store) SetSetting(key, value string) error {
|
||
_, err := s.db.Exec(`INSERT INTO settings (key, value) VALUES (?, ?)
|
||
ON CONFLICT(key) DO UPDATE SET value=excluded.value`, key, value)
|
||
return err
|
||
}
|
||
|
||
// SiteSettings 对外的站点设置白名单视图。
|
||
type SiteSettings struct {
|
||
SiteTitle string `json:"site_title"`
|
||
SiteDesc string `json:"site_desc"`
|
||
PageSize int `json:"page_size"`
|
||
}
|
||
|
||
// Defaults,未初始化时兜底。
|
||
const (
|
||
DefaultSiteTitle = "Pure Note"
|
||
DefaultSiteDesc = ""
|
||
DefaultPageSize = 10
|
||
)
|
||
|
||
// GetSiteSettings 读取站点设置(白名单三键,带默认值)。
|
||
func (s *Store) GetSiteSettings() (*SiteSettings, error) {
|
||
ss := &SiteSettings{SiteTitle: DefaultSiteTitle, SiteDesc: DefaultSiteDesc, PageSize: DefaultPageSize}
|
||
if v, ok, err := s.GetSetting(KeySiteTitle); err != nil {
|
||
return nil, err
|
||
} else if ok && v != "" {
|
||
ss.SiteTitle = v
|
||
}
|
||
if v, ok, err := s.GetSetting(KeySiteDesc); err != nil {
|
||
return nil, err
|
||
} else if ok {
|
||
ss.SiteDesc = v
|
||
}
|
||
if v, ok, err := s.GetSetting(KeyPageSize); err != nil {
|
||
return nil, err
|
||
} else if ok {
|
||
// strconv.Atoi 全文解析:拒绝 "10abc" 类部分解析的脏值(评审 round2 P2-13)
|
||
if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil && n >= 1 && n <= 100 {
|
||
ss.PageSize = n
|
||
}
|
||
}
|
||
return ss, nil
|
||
}
|