""" Platform Gateway — Database initialization and access. """ import sqlite3 import bcrypt from config import ( DB_PATH, TRIPS_URL, FITNESS_URL, INVENTORY_URL, MINIFLUX_URL, SHELFMARK_URL, SPOTIZERR_URL, BUDGET_URL, ) def get_db(): conn = sqlite3.connect(str(DB_PATH)) conn.row_factory = sqlite3.Row conn.execute("PRAGMA foreign_keys = ON") conn.execute("PRAGMA journal_mode = WAL") return conn def init_db(): conn = get_db() c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, display_name TEXT NOT NULL DEFAULT '', created_at TEXT DEFAULT CURRENT_TIMESTAMP )''') c.execute('''CREATE TABLE IF NOT EXISTS sessions ( token TEXT PRIMARY KEY, user_id INTEGER NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, expires_at TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE )''') c.execute('''CREATE TABLE IF NOT EXISTS service_connections ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, service TEXT NOT NULL, auth_type TEXT NOT NULL DEFAULT 'bearer', auth_token TEXT NOT NULL, metadata TEXT DEFAULT '{}', created_at TEXT DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, service), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE )''') c.execute('''CREATE TABLE IF NOT EXISTS apps ( id TEXT PRIMARY KEY, name TEXT NOT NULL, icon TEXT DEFAULT '', route_prefix TEXT NOT NULL, proxy_target TEXT NOT NULL, sort_order INTEGER DEFAULT 0, enabled INTEGER DEFAULT 1, dashboard_widget TEXT DEFAULT NULL )''') c.execute('''CREATE TABLE IF NOT EXISTS pinned_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, service TEXT NOT NULL DEFAULT 'inventory', item_id TEXT NOT NULL, item_name TEXT NOT NULL DEFAULT '', pinned_at TEXT DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, service, item_id), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE )''') conn.commit() # Seed default apps existing = c.execute("SELECT COUNT(*) FROM apps").fetchone()[0] if existing == 0: c.execute("INSERT INTO apps VALUES ('trips', 'Trips', 'map', '/trips', ?, 1, 1, 'upcoming_trips')", (TRIPS_URL,)) c.execute("INSERT INTO apps VALUES ('fitness', 'Fitness', 'bar-chart', '/fitness', ?, 2, 1, 'daily_calories')", (FITNESS_URL,)) c.execute("INSERT INTO apps VALUES ('inventory', 'Inventory', 'package', '/inventory', ?, 3, 1, 'items_issues')", (INVENTORY_URL,)) conn.commit() else: # Ensure inventory app exists (migration for existing DBs) inv = c.execute("SELECT id FROM apps WHERE id = 'inventory'").fetchone() if not inv: c.execute("INSERT INTO apps VALUES ('inventory', 'Inventory', 'package', '/inventory', ?, 3, 1, 'items_issues')", (INVENTORY_URL,)) conn.commit() print("[Gateway] Added inventory app") # Ensure reader app exists rdr = c.execute("SELECT id FROM apps WHERE id = 'reader'").fetchone() if not rdr: c.execute("INSERT INTO apps VALUES ('reader', 'Reader', 'rss', '/reader', ?, 4, 1, 'unread_count')", (MINIFLUX_URL,)) conn.commit() print("[Gateway] Added reader app") # Ensure books app exists (now media) books = c.execute("SELECT id FROM apps WHERE id = 'books'").fetchone() if not books: c.execute("INSERT INTO apps VALUES ('books', 'Media', 'book', '/media', ?, 5, 1, NULL)", (SHELFMARK_URL,)) conn.commit() print("[Gateway] Added media app") else: c.execute("UPDATE apps SET name = 'Media', route_prefix = '/media' WHERE id = 'books'") conn.commit() # Ensure music (spotizerr) app exists music = c.execute("SELECT id FROM apps WHERE id = 'music'").fetchone() if not music: c.execute("INSERT INTO apps VALUES ('music', 'Music', 'music', '/music', ?, 6, 1, NULL)", (SPOTIZERR_URL,)) conn.commit() print("[Gateway] Added music app") # Ensure budget app exists budget = c.execute("SELECT id FROM apps WHERE id = 'budget'").fetchone() if not budget: c.execute("INSERT OR IGNORE INTO apps VALUES ('budget', 'Budget', 'dollar-sign', '/budget', ?, 7, 1, 'budget_summary')", (BUDGET_URL,)) conn.commit() print("[Gateway] Added budget app") # Seed admin user from env vars if no users exist import os user_count = c.execute("SELECT COUNT(*) FROM users").fetchone()[0] if user_count == 0: admin_user = os.environ.get("ADMIN_USERNAME") admin_pass = os.environ.get("ADMIN_PASSWORD") admin_name = os.environ.get("ADMIN_DISPLAY_NAME", "Admin") if not admin_user or not admin_pass: print("[Gateway] WARNING: No users exist and ADMIN_USERNAME/ADMIN_PASSWORD not set. Create a user manually.") else: pw_hash = bcrypt.hashpw(admin_pass.encode(), bcrypt.gensalt()).decode() c.execute("INSERT INTO users (username, password_hash, display_name) VALUES (?, ?, ?)", (admin_user, pw_hash, admin_name)) conn.commit() print(f"[Gateway] Created admin user: {admin_user}") conn.close()