From c892588d31754f94a87149220baeb1b906e25282 Mon Sep 17 00:00:00 2001 From: Jason Stedwell Date: Fri, 3 Jul 2026 01:36:56 -0500 Subject: [PATCH] db: add schema for API keys, audit log, and per-minute uptime; migrate links/clicks New tables: api_keys (hashed bearer tokens with scopes), audit_log (admin action trail), uptime_minutes (multi-worker-safe availability marks). Idempotent column migrations: links.redirect_type (default 302), links.password_hash, links.deleted_at, clicks.is_bot. Existing databases upgrade in place on next startup. Co-Authored-By: Claude Fable 5 --- app.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app.py b/app.py index 8adf7c0..5afd3f4 100644 --- a/app.py +++ b/app.py @@ -134,6 +134,28 @@ def init_db(): checks_total INTEGER DEFAULT 0, checks_up INTEGER DEFAULT 0 ); + CREATE TABLE IF NOT EXISTS uptime_minutes ( + minute TEXT PRIMARY KEY + ); + CREATE TABLE IF NOT EXISTS api_keys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL REFERENCES users(id), + name TEXT NOT NULL, + key_hash TEXT UNIQUE NOT NULL, + prefix TEXT NOT NULL, + scope TEXT NOT NULL DEFAULT 'read', + created_at TEXT NOT NULL, + last_used_at TEXT, + revoked INTEGER DEFAULT 0 + ); + CREATE TABLE IF NOT EXISTS audit_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + actor TEXT, + action TEXT NOT NULL, + target TEXT, + details TEXT, + created_at TEXT NOT NULL + ); CREATE INDEX IF NOT EXISTS idx_links_code ON links(code); CREATE INDEX IF NOT EXISTS idx_clicks_link ON clicks(link_id); CREATE INDEX IF NOT EXISTS idx_clicks_at ON clicks(clicked_at); @@ -146,6 +168,10 @@ def init_db(): "ALTER TABLE clicks ADD COLUMN country TEXT", "ALTER TABLE users ADD COLUMN plan TEXT NOT NULL DEFAULT 'free'", "ALTER TABLE users ADD COLUMN org_id INTEGER REFERENCES organizations(id)", + "ALTER TABLE links ADD COLUMN redirect_type INTEGER DEFAULT 302", + "ALTER TABLE links ADD COLUMN password_hash TEXT", + "ALTER TABLE links ADD COLUMN deleted_at TEXT", + "ALTER TABLE clicks ADD COLUMN is_bot INTEGER DEFAULT 0", ]: try: conn.execute(migration)