feat(edit): P2 条目就地编辑 + P3 备份/审计/改密

- 迁移 v17:entry.updated_at(乐观锁)+ audit 表
- GET/PATCH /api/items/{key}:字段编辑、标签替换、409 冲突、400 校验
- 详情页 [编辑] → Drawer 表单(标题/简介/标签/评价/正文/封面/跳转),保存后局部刷新
- GET /api/backup:VACUUM INTO 一致性快照,附件下载
- /admin 系统 Tab:修改密码(清空会话)、下载备份
- 审计:item.update / settings.update
- 测试 46 passed(entries 4 例)
- 文档:ADMIN-PLAN P2/P3 记录、README
This commit is contained in:
stash
2026-10-29 12:59:21 +08:00
parent 9ae5a455c5
commit 8f688885ed
12 changed files with 865 additions and 12 deletions
+32 -6
View File
@@ -75,7 +75,8 @@ CREATE TABLE IF NOT EXISTS entry (
hot INTEGER NOT NULL DEFAULT 0 CHECK (hot IN (0, 1)),-- 是否热门:列表里显示 HOT 徽标
views INTEGER NOT NULL DEFAULT 0, -- 浏览量,详情页操作条「眼睛」后的数字
likes INTEGER NOT NULL DEFAULT 0, -- 点赞数,详情页操作条「赞」后的数字
sort INTEGER NOT NULL DEFAULT 0 -- 条目顺序(组内或分区内),越小越靠前
sort INTEGER NOT NULL DEFAULT 0, -- 条目顺序(组内或分区内),越小越靠前
updated_at INTEGER NOT NULL DEFAULT 0 -- 最近一次编辑时间(unix 秒),0 = 从未编辑
);
CREATE INDEX IF NOT EXISTS entry_parent_idx ON entry(node_key, sort);
@@ -128,6 +129,29 @@ CREATE TABLE IF NOT EXISTS setting (
version INTEGER NOT NULL DEFAULT 1,
updated_at INTEGER NOT NULL
);
-- ========== v17:审计 ==========
CREATE TABLE IF NOT EXISTS audit (
id INTEGER PRIMARY KEY AUTOINCREMENT,
at INTEGER NOT NULL,
action TEXT NOT NULL,
target TEXT,
detail TEXT
);
"#;
/// v16 → v17:条目加 `updated_at`(乐观锁),新增审计表。
const V17_SQL: &str = r#"
ALTER TABLE entry ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0;
CREATE TABLE IF NOT EXISTS audit (
id INTEGER PRIMARY KEY AUTOINCREMENT,
at INTEGER NOT NULL,
action TEXT NOT NULL,
target TEXT,
detail TEXT
);
"#;
/// v15 → v16 的增量(老库升级用;新库直接走 SCHEMA_SQL)。
@@ -171,7 +195,7 @@ pub struct SeedReport {
///
/// 从 v16 起有了第一套迁移:`migrate` 会把老库逐级升上来,
/// 只有「比程序更新」或「没有迁移路径」的版本才拒绝启动。
pub const SCHEMA_VERSION: i64 = 16;
pub const SCHEMA_VERSION: i64 = 17;
fn current_version(conn: &Connection) -> Result<i64, StashError> {
conn.pragma_query_value(None, "user_version", |row| row.get(0))
@@ -180,10 +204,7 @@ fn current_version(conn: &Connection) -> Result<i64, StashError> {
/// 把库升到 `SCHEMA_VERSION`,返回升级后的版本。
///
/// - 0:全新库,建全量结构;
/// - 15:补 v16 的登录/设置表;
/// - 16:已是最新;
/// - 其它:拒绝启动(不猜结构)。
/// 逐级迁移:0 建全量;15 → 16 → 17;已最新直接返回;更高版本拒绝。
pub fn migrate(conn: &Connection) -> Result<i64, StashError> {
let mut version = current_version(conn)?;
@@ -213,6 +234,11 @@ pub fn migrate(conn: &Connection) -> Result<i64, StashError> {
version = 16;
}
if version == 16 {
conn.execute_batch(V17_SQL).map_err(map_error)?;
version = 17;
}
conn.pragma_update(None, "user_version", version)
.map_err(map_error)?;