1import { Database } from "bun:sqlite";2import { mkdirSync } from "node:fs";3import { join } from "node:path";456const SCHEMA = `7CREATE TABLE IF NOT EXISTS settings (8 key TEXT PRIMARY KEY,9 value TEXT NOT NULL10);11CREATE TABLE IF NOT EXISTS repos (12 name TEXT PRIMARY KEY,13 public INTEGER NOT NULL DEFAULT 0,14 category TEXT NOT NULL DEFAULT '',15 homepage TEXT NOT NULL DEFAULT '',16 sort_order INTEGER NOT NULL DEFAULT 0,17 install1 TEXT NOT NULL DEFAULT '',18 install2 TEXT NOT NULL DEFAULT '',19 install3 TEXT NOT NULL DEFAULT '',20 last_sync_at INTEGER,21 last_sync_error TEXT22);23`;2425export type RepoSettings = {26 name: string;27 homepage: string;28 installLines: string[];29};3031type RepoRow = {32 name: string;33 homepage: string;34 install1: string;35 install2: string;36 install3: string;37};3839export function openKeprDb(dataDir: string): Database {40 mkdirSync(dataDir, { recursive: true });41 const db = new Database(join(dataDir, "kepr.db"), { strict: true });42 db.run("PRAGMA journal_mode = WAL");43 db.run("PRAGMA busy_timeout = 5000");44 db.run(SCHEMA);45 return db;46}4748export function setting(db: Database, key: string): string | null {49 const row = db50 .query<{ value: string }, [string]>("SELECT value FROM settings WHERE key = ?")51 .get(key);52 return row?.value ?? null;53}545556export function registerRepo(db: Database, name: string): void {57 db.query("INSERT OR IGNORE INTO repos (name) VALUES (?)").run(name);58}5960export function recordSync(db: Database, name: string, at: number, error: string | null): void {61 db.query("UPDATE repos SET last_sync_at = ?, last_sync_error = ? WHERE name = ?").run(62 at,63 error,64 name,65 );66}676869export function publicRepo(db: Database, name: string): RepoSettings | null {70 const row = db71 .query<RepoRow, [string]>(72 "SELECT name, homepage, install1, install2, install3 FROM repos WHERE name = ? AND public = 1",73 )74 .get(name);75 if (!row) return null;76 const installLines = [row.install1, row.install2, row.install3].filter((line) => line !== "");77 return { name: row.name, homepage: row.homepage, installLines };78}