import { Kysely, SqliteDialect } from 'kysely'; import SQLite from 'better-sqlite3'; import path from 'path'; import fs from 'fs'; export interface SessionTable { id: string; url: string; status: string; seed: number; max_states: number; states_visited: number; anomalies_found: number; started_at: number; finished_at: number | null; config_json: string; } export interface StateTable { id: string; session_id: string; url: string; title: string; dom_snapshot_path: string | null; visit_count: number; discovered_at: number; } export interface ActionTable { id: string; session_id: string; state_id: string; type: string; selector: string | null; value: string | null; url: string | null; seed: number; executed_at: number; sequence_order: number; } export interface AnomalyTable { id: string; session_id: string; type: string; severity: string; description: string; action_trace_json: string; evidence_json: string; screenshot_path: string | null; dom_snapshot_path: string | null; detected_at: number; ai_enrichment_json: string | null; ai_enriched_at: number | null; browser: string | null; browser_version: string | null; } export interface NotificationTable { id: string; anomaly_id: string; channel: string; status: string; sent_at: number | null; error: string | null; } export interface ScheduleTable { id: string; name: string; url: string; config_json: string; cron_expression: string; enabled: number; last_run_at: number | null; next_run_at: number | null; created_at: number; } export interface VisualBaselineTable { id: string; state_id: string; url: string; screenshot_path: string; approved_at: number; approved_by: string | null; width: number; height: number; } export interface VisualComparisonTable { id: string; session_id: string; state_id: string; baseline_id: string | null; current_screenshot_path: string; diff_screenshot_path: string | null; diff_pixels: number | null; diff_percent: number | null; status: string; created_at: number; } export interface PerformanceMetricTable { id: string; session_id: string; state_id: string; url: string; ttfb: number | null; dom_content_loaded: number | null; load_complete: number | null; lcp: number | null; cls: number | null; fid: number | null; inp: number | null; total_requests: number | null; failed_requests: number | null; total_transfer_size: number | null; captured_at: number; } export interface FindingTable { id: string; session_id: string; type: string; severity: string; description: string; status: string; action_trace_json: string; evidence_json: string; screenshot_path: string | null; dom_snapshot_path: string | null; browser: string | null; browser_version: string | null; ai_enrichment_json: string | null; created_at: number; resolved_at: number | null; } export interface JobTable { id: string; type: string; status: string; payload: string; result: string | null; error: string | null; attempts: number; max_attempts: number; priority: number; run_at: string; started_at: string | null; completed_at: string | null; created_at: string; updated_at: string; } export interface UserTable { id: string; email: string; name: string; password_hash: string; role: string; org_id: string | null; created_at: number; updated_at: number; } export interface OrganizationTable { id: string; name: string; slug: string; created_at: number; } export interface OrgMemberTable { id: string; org_id: string; user_id: string; role: string; joined_at: number; } export interface ApiKeyTable { id: string; user_id: string; org_id: string; name: string; key_hash: string; key_prefix: string; permissions: string; expires_at: number | null; last_used_at: number | null; created_at: number; } export interface AuthSessionTable { id: string; user_id: string; token: string; expires_at: number; created_at: number; } export interface ReportTable { id: string; title: string; format: string; status: string; filters_json: string; file_path: string | null; error_message: string | null; total_findings: number; created_at: number; completed_at: number | null; } export interface IntegrationTable { id: string; name: string; type: string; enabled: number; config_json: string; created_at: number; } export interface WebhookEndpointTable { id: string; url: string; secret: string; enabled: number; created_at: number; last_delivered_at: number | null; last_status: number | null; } export interface WebhookDeliveryTable { id: string; endpoint_id: string; event: string; payload_json: string; status: number; attempted_at: number; } export interface SSOConfigTable { id: string; organization_id: string; provider: string; enabled: number; config_json: string; created_at: number; } export interface TOTPSecretTable { id: string; user_id: string; secret: string; verified: number; created_at: number; } export interface AuditLogTable { id: string; user_id: string | null; organization_id: string | null; action: string; resource: string; resource_id: string | null; ip_address: string | null; user_agent: string | null; details_json: string; occurred_at: number; } export interface Database { sessions: SessionTable; states: StateTable; actions: ActionTable; anomalies: AnomalyTable; notifications: NotificationTable; schedules: ScheduleTable; visual_baselines: VisualBaselineTable; visual_comparisons: VisualComparisonTable; performance_metrics: PerformanceMetricTable; findings: FindingTable; jobs: JobTable; users: UserTable; organizations: OrganizationTable; org_members: OrgMemberTable; api_keys: ApiKeyTable; auth_sessions: AuthSessionTable; reports: ReportTable; integrations: IntegrationTable; webhook_endpoints: WebhookEndpointTable; webhook_deliveries: WebhookDeliveryTable; sso_configs: SSOConfigTable; totp_secrets: TOTPSecretTable; audit_logs: AuditLogTable; } export function createDatabase(config: { driver: string; path: string; url?: string }): Kysely { if (config.driver === 'postgres') { // eslint-disable-next-line @typescript-eslint/no-require-imports const { Pool } = require('pg') as { Pool: new (opts: { connectionString?: string }) => unknown }; // eslint-disable-next-line @typescript-eslint/no-require-imports const { PostgresDialect } = require('kysely') as { PostgresDialect: new (opts: { pool: unknown }) => SqliteDialect }; return new Kysely({ dialect: new PostgresDialect({ pool: new Pool({ connectionString: config.url }) }), }); } fs.mkdirSync(path.dirname(config.path), { recursive: true }); return new Kysely({ dialect: new SqliteDialect({ database: new SQLite(config.path) }), }); }