Refactor gateway into modular architecture
Split 1878-line server.py into 15 focused modules: - config.py: all env vars and constants - database.py: schema, init, seed logic - sessions.py: session/token CRUD - proxy.py: proxy_request, SERVICE_MAP, resolve_service - responses.py: ResponseMixin for handler helpers - auth.py: login/logout/register handlers - dashboard.py: dashboard, apps, connections, pinning - command.py: AI command bar - integrations/booklore.py: auth, books, cover, import - integrations/kindle.py: send-to-kindle, file finder - integrations/karakeep.py: save/delete bookmarks - integrations/qbittorrent.py: download status - integrations/image_proxy.py: external image proxy server.py is now thin routing only (~344 lines). All routes, methods, status codes, and responses preserved exactly. Added PYTHONUNBUFFERED=1 to Dockerfile for live logging.
This commit is contained in:
133
gateway/database.py
Normal file
133
gateway/database.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
Platform Gateway — Database initialization and access.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import sqlite3
|
||||
|
||||
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 default admin user if empty
|
||||
user_count = c.execute("SELECT COUNT(*) FROM users").fetchone()[0]
|
||||
if user_count == 0:
|
||||
pw_hash = hashlib.sha256("admin".encode()).hexdigest()
|
||||
c.execute("INSERT INTO users (username, password_hash, display_name) VALUES (?, ?, ?)",
|
||||
("admin", pw_hash, "Yusuf"))
|
||||
conn.commit()
|
||||
print("[Gateway] Created default user: admin / admin")
|
||||
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user