diff --git a/web/server/db/index.ts b/web/server/db/index.ts new file mode 100644 index 0000000..89bef51 --- /dev/null +++ b/web/server/db/index.ts @@ -0,0 +1,28 @@ +import Database from 'better-sqlite3'; +import path from 'path'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { SCHEMA_SQL } from './schema'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const DB_PATH = path.resolve(__dirname, '..', '..', 'data', 'meitu-agent.db'); + +let db: Database.Database; + +export function getDb(): Database.Database { + if (!db) { + const dir = path.dirname(DB_PATH); + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + db = new Database(DB_PATH); + db.pragma('journal_mode = WAL'); + db.pragma('foreign_keys = ON'); + } + return db; +} + +export function initDb(): void { + const d = getDb(); + d.exec(SCHEMA_SQL); +} diff --git a/web/server/db/schema.ts b/web/server/db/schema.ts new file mode 100644 index 0000000..cd8e95b --- /dev/null +++ b/web/server/db/schema.ts @@ -0,0 +1,49 @@ +export const SCHEMA_SQL = ` + CREATE TABLE IF NOT EXISTS conversations ( + id TEXT PRIMARY KEY, + title TEXT NOT NULL, + account_id TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE IF NOT EXISTS messages ( + id TEXT PRIMARY KEY, + conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE, + role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system', 'tool')), + content TEXT NOT NULL, + tool_calls TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE IF NOT EXISTS assets ( + id TEXT PRIMARY KEY, + account_id TEXT, + manifest_path TEXT, + type TEXT NOT NULL CHECK(type IN ('image', 'video')), + file_path TEXT NOT NULL, + url TEXT, + shot_index INTEGER, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE IF NOT EXISTS pipeline_runs ( + id TEXT PRIMARY KEY, + manifest_path TEXT NOT NULL, + phase TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','running','done','failed')), + started_at TEXT, + finished_at TEXT + ); + + CREATE TABLE IF NOT EXISTS configs ( + id TEXT PRIMARY KEY, + key TEXT NOT NULL UNIQUE, + value TEXT NOT NULL DEFAULT '{}', + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE INDEX IF NOT EXISTS idx_messages_conv ON messages(conversation_id, created_at); + CREATE INDEX IF NOT EXISTS idx_assets_account ON assets(account_id, created_at); + CREATE INDEX IF NOT EXISTS idx_pipeline_manifest ON pipeline_runs(manifest_path); +`;