/** * SQLite 数据层(§4.1) * 表:settings / ignored_packages / logs */ import Database from 'better-sqlite3' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) // 数据目录:环境变量 DATA_DIR 可覆盖(Docker 挂载 manager-data) const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, 'data') fs.mkdirSync(DATA_DIR, { recursive: true }) const db = new Database(path.join(DATA_DIR, 'manager.db')) db.pragma('journal_mode = WAL') db.exec(` CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); CREATE TABLE IF NOT EXISTS ignored_packages ( pkg_name TEXT PRIMARY KEY, ignored_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, action TEXT DEFAULT 'clean', pkg_name TEXT, deleted_versions TEXT, deleted_count INTEGER, freed_space TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); `) // 旧库迁移:早期 logs 表无 action 列,运行时补齐(忽略/取消忽略等操作依赖该列区分类型) const logCols = db.prepare('PRAGMA table_info(logs)').all().map(c => c.name) if (!logCols.includes('action')) { db.exec("ALTER TABLE logs ADD COLUMN action TEXT DEFAULT 'clean'") } // —— settings —— export function getSetting(key, def = null) { const row = db.prepare('SELECT value FROM settings WHERE key = ?').get(key) return row ? row.value : def } export function setSetting(key, value) { db.prepare( 'INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value', ).run(key, String(value)) } // —— ignored —— export function getIgnored() { return db.prepare('SELECT pkg_name FROM ignored_packages ORDER BY ignored_at DESC').all().map(r => r.pkg_name) } export function getIgnoredRows() { return db.prepare('SELECT pkg_name, ignored_at FROM ignored_packages ORDER BY ignored_at DESC').all() } export function isIgnored(pkgName) { return !!db.prepare('SELECT 1 FROM ignored_packages WHERE pkg_name = ?').get(pkgName) } export function addIgnored(pkgName) { db.prepare( 'INSERT INTO ignored_packages (pkg_name) VALUES (?) ON CONFLICT(pkg_name) DO NOTHING', ).run(pkgName) } export function removeIgnored(pkgName) { db.prepare('DELETE FROM ignored_packages WHERE pkg_name = ?').run(pkgName) } // —— logs —— export function addLog({ action = 'clean', pkgName, deletedVersions = null, deletedCount = 0, freedSpace = null }) { const info = db.prepare( 'INSERT INTO logs (action, pkg_name, deleted_versions, deleted_count, freed_space) VALUES (?, ?, ?, ?, ?)', ).run(action, pkgName, deletedVersions, deletedCount, freedSpace) return info.lastInsertRowid } export function getLogs(limit = 50) { return db.prepare( 'SELECT * FROM logs ORDER BY id DESC LIMIT ?', ).all(limit) } export default db