后端基础层:模块定义、配置解析、SQLite 存储层、Argon2id 认证与 Markdown 渲染
- internal/config:serve/init/backup/gc 子命令参数解析(--dev 强制 loopback 守卫) - internal/store:user_version 版本化迁移(仅追加式 + 越界拒启 + --allow-newer)、 笔记/图片/引用/会话/设置 DAO、并集可见性查询、VACUUM INTO 在线备份、 回收站 30 天 + 孤儿图 7 天宽限 gc - internal/auth:Argon2id PHC 串(m=19456,t=2,p=1)、256bit token 与 SHA-256 摘要 - internal/markdown:goldmark(默认转义)+ bluemonday 双保险,摘要纯文本提取 - internal/middleware:安全头(CSP/HSTS/nosniff 等)、错误日志、 有界令牌桶限流(per-IP 桶上限 + TTL 逐出)、Origin/Referer 同源校验
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
var n int
|
||||
if _, err := fmt.Sscanf(v, "%d", &n); err == nil && n >= 1 && n <= 100 {
|
||||
ss.PageSize = n
|
||||
}
|
||||
}
|
||||
return ss, nil
|
||||
}
|
||||
Reference in New Issue
Block a user