feat: complete v0.2 source and sync management

This commit is contained in:
2026-07-19 01:33:45 +08:00
parent 4e9a13981d
commit 5b5129fad5
21 changed files with 367 additions and 39 deletions
+18 -7
View File
@@ -18,6 +18,7 @@ CREATE TABLE IF NOT EXISTS sources (
id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL, base_url TEXT NOT NULL, token_encrypted TEXT NOT NULL, remote_user TEXT,
webhook_supported INTEGER NOT NULL DEFAULT 0, sync_status TEXT NOT NULL DEFAULT 'pending', last_synced_at TEXT, last_error TEXT,
is_enabled INTEGER NOT NULL DEFAULT 1, disabled_at TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, base_url)
);
CREATE TABLE IF NOT EXISTS posts (
@@ -44,7 +45,8 @@ CREATE TABLE IF NOT EXISTS reports (
CREATE TABLE IF NOT EXISTS sync_jobs (
id INTEGER PRIMARY KEY, source_id INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
kind TEXT NOT NULL, payload_json TEXT, status TEXT NOT NULL DEFAULT 'queued', attempts INTEGER NOT NULL DEFAULT 0,
last_error TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, run_after TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
trigger TEXT NOT NULL DEFAULT 'manual', last_error TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
started_at TEXT, finished_at TEXT, run_after TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS source_members (
source_id INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
@@ -55,18 +57,27 @@ CREATE TABLE IF NOT EXISTS source_members (
CREATE INDEX IF NOT EXISTS posts_public_idx ON posts(visibility, hidden, created_at DESC);
CREATE INDEX IF NOT EXISTS sync_jobs_idx ON sync_jobs(status, run_after);
CREATE UNIQUE INDEX IF NOT EXISTS source_remote_identity_unique ON sources(base_url, remote_user) WHERE remote_user IS NOT NULL;
CREATE TABLE IF NOT EXISTS schema_migrations (
version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`);
db.exec("INSERT OR IGNORE INTO source_members(source_id,user_id,role) SELECT id,user_id,'owner' FROM sources");
const sourceColumns = db.prepare("PRAGMA table_info(sources)").all() as { name: string }[];
if (!sourceColumns.some((column) => column.name === "webhook_secret_hash")) {
db.exec("ALTER TABLE sources ADD COLUMN webhook_secret_hash TEXT");
}
if (!sourceColumns.some((column) => column.name === "last_webhook_at")) {
db.exec("ALTER TABLE sources ADD COLUMN last_webhook_at TEXT");
function applyColumnMigration(version: number, table: string, column: string, sql: string) {
const columns = db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[];
if (!columns.some((item) => item.name === column)) db.exec(sql);
db.prepare("INSERT OR IGNORE INTO schema_migrations(version) VALUES(?)").run(version);
}
applyColumnMigration(1, "sources", "webhook_secret_hash", "ALTER TABLE sources ADD COLUMN webhook_secret_hash TEXT");
applyColumnMigration(2, "sources", "last_webhook_at", "ALTER TABLE sources ADD COLUMN last_webhook_at TEXT");
applyColumnMigration(3, "sources", "is_enabled", "ALTER TABLE sources ADD COLUMN is_enabled INTEGER NOT NULL DEFAULT 1");
applyColumnMigration(4, "sources", "disabled_at", "ALTER TABLE sources ADD COLUMN disabled_at TEXT");
applyColumnMigration(5, "sync_jobs", "trigger", "ALTER TABLE sync_jobs ADD COLUMN trigger TEXT NOT NULL DEFAULT 'manual'");
applyColumnMigration(6, "sync_jobs", "started_at", "ALTER TABLE sync_jobs ADD COLUMN started_at TEXT");
applyColumnMigration(7, "sync_jobs", "finished_at", "ALTER TABLE sync_jobs ADD COLUMN finished_at TEXT");
const admin = process.env.ADMIN_USERNAME;
const adminPassword = process.env.ADMIN_PASSWORD;
if (admin && adminPassword) {