feat: 初始提交(仅核心代码,已排除大文件)
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
const Database = require('better-sqlite3')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { ensureDecrypted } = require('./weekly_crypto')
|
||||
|
||||
let db = null
|
||||
|
||||
const getDB = () => {
|
||||
if (!db) throw new Error('Database not initialized')
|
||||
return db
|
||||
}
|
||||
|
||||
const tryOpen = (dbPath) => {
|
||||
try {
|
||||
const d = new Database(dbPath)
|
||||
try {
|
||||
const row = d.prepare(`PRAGMA integrity_check`).get()
|
||||
const ok = row && (row.integrity_check === 'ok' || row['integrity_check'] === 'ok' || Object.values(row)[0] === 'ok')
|
||||
if (!ok) throw new Error('integrity_check_failed')
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
return d
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const initDB = (dbPath, sealPath) => {
|
||||
if (db) return db
|
||||
|
||||
// Robust Open
|
||||
db = tryOpen(dbPath)
|
||||
if (!db) {
|
||||
// Attempt recovery
|
||||
if (fs.existsSync(dbPath)) {
|
||||
const badName = `${dbPath}.bad-${Date.now()}`
|
||||
try { fs.renameSync(dbPath, badName) } catch {}
|
||||
console.warn(`Database corrupt, renamed to ${badName}`)
|
||||
}
|
||||
|
||||
// Try decrypt from seal
|
||||
if (sealPath) ensureDecrypted(dbPath, sealPath)
|
||||
|
||||
db = tryOpen(dbPath)
|
||||
if (!db) {
|
||||
// If still fails, create fresh (previous was renamed or didn't exist)
|
||||
db = new Database(dbPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Pragma
|
||||
db.pragma('journal_mode = WAL');
|
||||
db.pragma('synchronous = NORMAL');
|
||||
db.pragma('busy_timeout = 4000');
|
||||
|
||||
// Users
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE,
|
||||
pass_salt TEXT,
|
||||
pass_hash TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
)`)
|
||||
|
||||
// Reports
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
week_id TEXT UNIQUE,
|
||||
title_enc TEXT, title_iv TEXT, title_tag TEXT,
|
||||
summary_enc TEXT, summary_iv TEXT, summary_tag TEXT,
|
||||
content_enc TEXT, content_iv TEXT, content_tag TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
)`)
|
||||
|
||||
// Events
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
report_id INTEGER,
|
||||
parent_event_id INTEGER,
|
||||
title_enc TEXT, title_iv TEXT, title_tag TEXT,
|
||||
content_enc TEXT, content_iv TEXT, content_tag TEXT,
|
||||
status_code TEXT,
|
||||
status_enc TEXT, status_iv TEXT, status_tag TEXT,
|
||||
deadline TEXT,
|
||||
push_required INTEGER DEFAULT 0,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
completed_at TEXT,
|
||||
FOREIGN KEY(report_id) REFERENCES reports(id)
|
||||
)`)
|
||||
|
||||
// Event Reviews
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS event_reviews (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_id INTEGER,
|
||||
review_enc TEXT, review_iv TEXT, review_tag TEXT,
|
||||
rating INTEGER,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
FOREIGN KEY(event_id) REFERENCES events(id)
|
||||
)`)
|
||||
|
||||
// Attachments
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS attachments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_id INTEGER,
|
||||
filename_enc TEXT, filename_iv TEXT, filename_tag TEXT,
|
||||
mime_enc TEXT, mime_iv TEXT, mime_tag TEXT,
|
||||
size INTEGER,
|
||||
chunks INTEGER DEFAULT 1,
|
||||
store_enc TEXT, store_iv TEXT, store_tag TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
FOREIGN KEY(event_id) REFERENCES events(id)
|
||||
)`)
|
||||
|
||||
// Versions
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS versions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
subject_type TEXT,
|
||||
subject_id INTEGER,
|
||||
snapshot_enc TEXT, snapshot_iv TEXT, snapshot_tag TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
)`)
|
||||
|
||||
// Logs
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
action TEXT,
|
||||
details TEXT,
|
||||
source TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
)`)
|
||||
|
||||
// Email Sends
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS email_sends (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
day TEXT,
|
||||
report_id INTEGER,
|
||||
event_id INTEGER,
|
||||
category TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
UNIQUE(day, report_id, event_id, category)
|
||||
)`)
|
||||
|
||||
// Event External (Ensure table exists for integrations)
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS event_external (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source TEXT NOT NULL,
|
||||
ext_id TEXT NOT NULL,
|
||||
event_id INTEGER NOT NULL,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
UNIQUE(source, ext_id),
|
||||
FOREIGN KEY(event_id) REFERENCES events(id)
|
||||
);`)
|
||||
|
||||
// Event Relations
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS event_relations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_id INTEGER NOT NULL,
|
||||
target_event_id INTEGER NOT NULL,
|
||||
relation_type TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
FOREIGN KEY(event_id) REFERENCES events(id),
|
||||
FOREIGN KEY(target_event_id) REFERENCES events(id)
|
||||
);`)
|
||||
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_reports_week_id ON reports(week_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_events_report_id ON events(report_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_events_parent_event_id ON events(parent_event_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_event_reviews_event_id ON event_reviews(event_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_attachments_event_id ON attachments(event_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_versions_subject ON versions(subject_type, subject_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_event_external_event_id ON event_external(event_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_event_relations_event_id ON event_relations(event_id)`)
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_event_relations_target_event_id ON event_relations(target_event_id)`)
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initDB,
|
||||
getDB
|
||||
}
|
||||
Reference in New Issue
Block a user