120 lines
5.4 KiB
TypeScript
120 lines
5.4 KiB
TypeScript
import db from './database';
|
|
|
|
/**
|
|
* Creates all required tables if they do not already exist.
|
|
* Safe to call on every startup — uses IF NOT EXISTS throughout.
|
|
*/
|
|
export function initDatabase(log = true): void {
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS projects (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
project_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS installed_packages (app_id TEXT PRIMARY KEY, package_json TEXT NOT NULL, digest TEXT NOT NULL);
|
|
CREATE TABLE IF NOT EXISTS app_connections (app_id TEXT PRIMARY KEY, encrypted_value TEXT NOT NULL, iv TEXT NOT NULL, auth_tag TEXT NOT NULL);
|
|
|
|
CREATE TABLE IF NOT EXISTS app_icons (
|
|
id TEXT PRIMARY KEY,
|
|
png BLOB NOT NULL,
|
|
width INTEGER NOT NULL,
|
|
height INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS included_apps (
|
|
id TEXT PRIMARY KEY,
|
|
manifest_json TEXT NOT NULL,
|
|
registered_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS secrets (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
authentication_type TEXT NOT NULL,
|
|
encrypted_value TEXT NOT NULL,
|
|
iv TEXT NOT NULL,
|
|
auth_tag TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS executions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
project_id TEXT, project_name TEXT, action_id TEXT, action_name TEXT,
|
|
method TEXT NOT NULL, sanitized_url TEXT NOT NULL, status INTEGER,
|
|
duration_ms INTEGER NOT NULL, outcome TEXT NOT NULL, error_code TEXT,
|
|
error_message TEXT, preview TEXT, response_bytes INTEGER,
|
|
preview_truncated INTEGER NOT NULL DEFAULT 0, content_type TEXT
|
|
,published_app_id TEXT, published_version INTEGER, user_id TEXT
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_executions_action ON executions(action_id, id DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_executions_outcome ON executions(outcome, id DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT NOT NULL,
|
|
normalized_username TEXT NOT NULL UNIQUE,
|
|
display_name TEXT,
|
|
email TEXT,
|
|
password_hash TEXT,
|
|
role TEXT NOT NULL CHECK (role IN ('admin', 'user')),
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
provider TEXT NOT NULL DEFAULT 'local',
|
|
provider_subject TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
last_login_at TEXT
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_provider_subject
|
|
ON users(provider, provider_subject) WHERE provider_subject IS NOT NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id_hash TEXT PRIMARY KEY,
|
|
csrf_hash TEXT NOT NULL,
|
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
issued_at TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
revoked_at TEXT,
|
|
FOREIGN KEY(user_id) REFERENCES users(id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id, expires_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS published_apps (
|
|
id TEXT PRIMARY KEY,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
source_project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
display_name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
visibility TEXT NOT NULL CHECK (visibility IN ('public', 'authenticated')),
|
|
snapshot_json TEXT NOT NULL,
|
|
schema_version TEXT NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
active INTEGER NOT NULL DEFAULT 1,
|
|
published_by TEXT NOT NULL REFERENCES users(id),
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_published_apps_source ON published_apps(source_project_id, active);
|
|
`);
|
|
|
|
const executionColumns = new Set((db.pragma('table_info(executions)') as Array<{name:string}>).map((column) => column.name));
|
|
if (!executionColumns.has('published_app_id')) db.exec('ALTER TABLE executions ADD COLUMN published_app_id TEXT');
|
|
if (!executionColumns.has('published_version')) db.exec('ALTER TABLE executions ADD COLUMN published_version INTEGER');
|
|
if (!executionColumns.has('user_id')) db.exec('ALTER TABLE executions ADD COLUMN user_id TEXT');
|
|
|
|
const publicationColumns = new Set((db.pragma('table_info(published_apps)') as Array<{name:string}>).map(column => column.name));
|
|
if (!publicationColumns.has('icon_path')) db.exec('ALTER TABLE published_apps ADD COLUMN icon_path TEXT');
|
|
|
|
const userColumns = new Set((db.pragma('table_info(users)') as Array<{name:string}>).map((column) => column.name));
|
|
if (!userColumns.has('display_name')) db.exec('ALTER TABLE users ADD COLUMN display_name TEXT');
|
|
if (!userColumns.has('email')) db.exec('ALTER TABLE users ADD COLUMN email TEXT');
|
|
|
|
if (log) console.log('Database initialised');
|
|
}
|