归档 artalk-cf 评论后端 + rss-robot 到 blog-admin(含技术选型/模块分布 README)
Deploy to Production / pre-check (push) Successful in 58s
Deploy to Production / build (push) Successful in 4m3s
Deploy to Production / deploy-edgeone (push) Successful in 3m48s
Deploy to Production / finalize (push) Successful in 26s
Deploy to Production / notify-failure (push) Skipped

This commit is contained in:
zqlit committed 2026-10-04 08:45:40 +08:00
1 parent 855a001046
commit a74b3c7127
98 files changed
+15657

No files matched your search

+139
View File
@@ -0,0 +1,139 @@
-- ===========================================================================
-- 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,
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,
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);