mirror of
https://github.com/jeffusion/gitea-ai-assistant.git
synced 2026-06-04 07:26:47 +00:00
- Add database migration and repository for project review prompts - Add API endpoint for setting project-level prompts - Integrate project prompts into Agent and Codex review flows - Redesign repository management UI with dialog-based prompt editor - Replace flat buttons with Switch for webhook toggle and dedicated prompt button - Add Dialog and DropdownMenu UI components from Radix UI - Add comprehensive tests for wiring and interactions
140 lines
3.9 KiB
TypeScript
140 lines
3.9 KiB
TypeScript
/**
|
|
* SQLite database initialization and migration runner.
|
|
*
|
|
* Uses bun:sqlite (zero-dependency, built into Bun runtime).
|
|
* Single file at DATA_DIR/assistant.db with WAL mode for concurrent reads.
|
|
*/
|
|
|
|
import { Database } from 'bun:sqlite';
|
|
import { mkdirSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
|
|
import { migration001Init } from './migrations/001_init';
|
|
import { migration002RemoveLegacyReviewMode } from './migrations/002_remove_legacy_review_mode';
|
|
import { migration003RepositoryReviewPrompts } from './migrations/003_repository_review_prompts';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface Migration {
|
|
version: number;
|
|
name: string;
|
|
up(db: Database): void;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Migration registry (ordered by version)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const MIGRATIONS: Migration[] = [
|
|
migration001Init,
|
|
migration002RemoveLegacyReviewMode,
|
|
migration003RepositoryReviewPrompts,
|
|
];
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Database singleton
|
|
// ---------------------------------------------------------------------------
|
|
|
|
let db: Database | null = null;
|
|
|
|
/**
|
|
* Resolve the database file path.
|
|
* Defaults to `data/assistant.db` relative to CWD, overridable via `DATABASE_PATH` env.
|
|
*/
|
|
function getDbPath(): string {
|
|
return resolve(process.env.DATABASE_PATH || './data/assistant.db');
|
|
}
|
|
|
|
/**
|
|
* Initialize the SQLite database.
|
|
* Creates the file and parent directories if needed.
|
|
* Enables WAL mode and runs pending migrations.
|
|
*
|
|
* MUST be called once at application startup.
|
|
*/
|
|
export function initDatabase(): Database {
|
|
if (db) return db;
|
|
|
|
const dbPath = getDbPath();
|
|
const dir = dirname(dbPath);
|
|
mkdirSync(dir, { recursive: true });
|
|
|
|
db = new Database(dbPath);
|
|
|
|
// Enable WAL mode for better concurrent read performance
|
|
db.exec('PRAGMA journal_mode = WAL');
|
|
// Enable foreign keys
|
|
db.exec('PRAGMA foreign_keys = ON');
|
|
// Reasonable busy timeout for concurrent writes
|
|
db.exec('PRAGMA busy_timeout = 5000');
|
|
|
|
// Run migrations
|
|
runMigrations(db);
|
|
|
|
console.log(`📦 Database initialized at ${dbPath}`);
|
|
return db;
|
|
}
|
|
|
|
/**
|
|
* Get the database instance. Throws if not initialized.
|
|
*/
|
|
export function getDatabase(): Database {
|
|
if (!db) {
|
|
throw new Error('Database not initialized. Call initDatabase() at startup.');
|
|
}
|
|
return db;
|
|
}
|
|
|
|
/**
|
|
* Close the database connection gracefully.
|
|
*/
|
|
export function closeDatabase(): void {
|
|
if (db) {
|
|
db.close();
|
|
db = null;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Migration runner
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Create the migrations tracking table if it doesn't exist,
|
|
* then run any migrations that haven't been applied yet.
|
|
*/
|
|
function runMigrations(database: Database): void {
|
|
// Create migration tracking table
|
|
database.exec(`
|
|
CREATE TABLE IF NOT EXISTS _migrations (
|
|
version INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
)
|
|
`);
|
|
|
|
// Get already-applied versions
|
|
const applied = new Set<number>(
|
|
database
|
|
.query('SELECT version FROM _migrations ORDER BY version')
|
|
.all()
|
|
.map((row: any) => row.version as number)
|
|
);
|
|
|
|
// Run pending migrations in order
|
|
for (const migration of MIGRATIONS) {
|
|
if (applied.has(migration.version)) continue;
|
|
|
|
console.log(` ⬆️ Running migration ${migration.version}: ${migration.name}`);
|
|
|
|
database.transaction(() => {
|
|
migration.up(database);
|
|
database
|
|
.query('INSERT INTO _migrations (version, name) VALUES (?, ?)')
|
|
.run(migration.version, migration.name);
|
|
})();
|
|
}
|
|
}
|