Files
blog/blog-admin/schema.sql
T
zqlit d899cd793b feat(编辑角色): editor 只能写并发布自己的文章
- D1 users 加 role 列(''/editor/admin),与 is_admin 成对写入
- Worker routes/editor.ts 放行 admin+editor,反代注入 X-Editor-Uid/User/Role(昵称 encodeURIComponent)
- editor-api 引入 identify():身份取自注入头或本机会话;文章归属记 frontmatter author_id
- 编辑发布改精确 pathspec(只提交自己文章目录),管理员保持全量;空 pathspec 显式拦截
- 国内机直连登录改为转发 CF /user/access_token 校验,CF 不可达回退本机管理员
- handoff 签名覆盖身份(ts/uid/name/role),防编辑一键跳转变管理员
- admin.js:编辑只渲染「文章编辑」tab、作者框只读;用户管理加角色下拉 + 新建用户
2026-10-05 14:34:56 +08:00

143 lines
6.7 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ===========================================================================
-- artalk-cf —— D1 / SQLite schema
-- 执行:npx wrangler d1 execute artalk-cf --remote --file=./schema.sql
-- 设计与 Artalk v2 的 gorm 模型对应,但时间统一存 unix 毫秒(INTEGER),
-- 输出时再按 Asia/Shanghai 格式化成 "YYYY-MM-DD HH:mm:ss"。
-- ===========================================================================
CREATE TABLE IF NOT EXISTS sites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
urls TEXT NOT NULL DEFAULT '', -- 逗号分隔
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key TEXT NOT NULL, -- Artalk 的 page_key,如 /20210911.html
site_name TEXT NOT NULL,
title TEXT NOT NULL DEFAULT '',
admin_only INTEGER NOT NULL DEFAULT 0,
pv INTEGER NOT NULL DEFAULT 0,
vote_up INTEGER NOT NULL DEFAULT 0,
vote_down INTEGER NOT NULL DEFAULT 0,
accessible_url TEXT NOT NULL DEFAULT '',
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_pages_key_site ON pages (key, site_name);
CREATE INDEX IF NOT EXISTS idx_pages_site ON pages (site_name);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
link TEXT NOT NULL DEFAULT '',
password TEXT NOT NULL DEFAULT '', -- 空 = 无密码;支持 (pbkdf2) / (md5) / 明文(兼容)
badge_name TEXT NOT NULL DEFAULT '',
badge_color TEXT NOT NULL DEFAULT '',
title_name TEXT NOT NULL DEFAULT '', -- 头衔徽章(金色,优先级仅次于系统徽章)
last_ip TEXT NOT NULL DEFAULT '',
last_ua TEXT NOT NULL DEFAULT '',
is_admin INTEGER NOT NULL DEFAULT 0,
role TEXT NOT NULL DEFAULT '', -- 后台角色:'' 普通评论用户 | 'editor' 编辑 | 'admin' 管理员(2026-10-05 追加,存量库需 ALTER)
receive_email INTEGER NOT NULL DEFAULT 1,
is_in_conf INTEGER NOT NULL DEFAULT 0,
token_valid_from INTEGER NOT NULL DEFAULT 0, -- 早于此刻签发的 token 失效(改密码后踢下线)
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_name_email ON users (name, email);
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
page_key TEXT NOT NULL,
site_name TEXT NOT NULL,
user_id INTEGER NOT NULL,
rid INTEGER NOT NULL DEFAULT 0, -- 直接父节点 id(0 = 顶层)
root_id INTEGER NOT NULL DEFAULT 0, -- 顶层祖先 id(顶层为 0)
is_collapsed INTEGER NOT NULL DEFAULT 0,
is_pending INTEGER NOT NULL DEFAULT 0,
is_pinned INTEGER NOT NULL DEFAULT 0,
is_verified INTEGER NOT NULL DEFAULT 0,
is_anonymous INTEGER NOT NULL DEFAULT 0, -- 匿名评论:对外显示「匿名」,真实身份只留后台
is_featured INTEGER NOT NULL DEFAULT 0, -- 走心评论:管理员精选,关于页展示(2026-10-05 追加,存量库需 ALTER)
vote_up INTEGER NOT NULL DEFAULT 0,
vote_down INTEGER NOT NULL DEFAULT 0,
ua TEXT NOT NULL DEFAULT '',
ip TEXT NOT NULL DEFAULT '',
ip_region TEXT NOT NULL DEFAULT '',
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
deleted_at INTEGER NOT NULL DEFAULT 0 -- 软删除,0 = 未删除
);
CREATE INDEX IF NOT EXISTS idx_comments_page ON comments (page_key, site_name, deleted_at);
CREATE INDEX IF NOT EXISTS idx_comments_root ON comments (root_id, deleted_at);
CREATE INDEX IF NOT EXISTS idx_comments_user ON comments (user_id, deleted_at);
CREATE INDEX IF NOT EXISTS idx_comments_created ON comments (created_at);
-- 列表/计数覆盖索引:原来列表要 ORDER BY is_pinned DESC, created_at DESC,
-- 没有匹配索引 → SQLite 每次全表扫 ~1000 行(实测列表 940 行/次、计数 1500-2000 行/次,
-- 是 D1 免费额度被打爆的主因)。带上这些列后只读命中行。
CREATE INDEX IF NOT EXISTS idx_comments_page_list
ON comments (site_name, page_key, deleted_at, is_pending, is_pinned DESC, created_at DESC, id DESC);
-- 子评论拉取(root_id IN (...))专用:让 SQLite 按 root_id seek,而不是扫整页
CREATE INDEX IF NOT EXISTS idx_comments_children
ON comments (root_id, deleted_at, is_pending, created_at, id);
CREATE INDEX IF NOT EXISTS idx_comments_site_list
ON comments (site_name, deleted_at, is_pending, created_at DESC, id DESC);
CREATE TABLE IF NOT EXISTS votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
target_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
type TEXT NOT NULL, -- 'comment' | 'page'
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_votes_unique ON votes (target_id, user_id, type);
CREATE TABLE IF NOT EXISTS notifies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
comment_id INTEGER NOT NULL,
is_read INTEGER NOT NULL DEFAULT 0,
is_emailed INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_notifies_user ON notifies (user_id, is_read);
-- 键值配置:frontend_conf / admin_users / moderator / captcha / ip_region / ...
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
-- 简单限流:按 IP + 动作统计,60 秒窗口
CREATE TABLE IF NOT EXISTS rate_limits (
bucket TEXT PRIMARY KEY, -- 如 "comment:1.2.3.4"
count INTEGER NOT NULL DEFAULT 0,
expires_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_rate_expires ON rate_limits (expires_at);
-- 验证码通过状态(按 IP 记,跨域请求带不上 cookie,所以放服务端)
CREATE TABLE IF NOT EXISTS captcha_passes (
ip TEXT PRIMARY KEY,
expires_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_captcha_expires ON captcha_passes (expires_at);
-- 验证码题目:answer 只存哈希,防止有人直接读库
CREATE TABLE IF NOT EXISTS captcha_challenges (
id TEXT PRIMARY KEY,
answer_hash TEXT NOT NULL,
ip TEXT NOT NULL,
expires_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_captcha_ch_expires ON captcha_challenges (expires_at);