From 1a5f55eb93a91d51a9df908ed62a85caa637d889 Mon Sep 17 00:00:00 2001 From: Ben Miller Date: Mon, 29 Jun 2026 22:37:01 -0500 Subject: [PATCH] feat: implement modular architecture with blueprints for auth, chat, rooms, admin, and watchparty management --- blueprints/admin.py | 19 ------ blueprints/auth.py | 8 +-- blueprints/chat.py | 4 -- blueprints/rooms.py | 2 - blueprints/watchparty.py | 2 - models/cleanup.py | 3 - models/database.py | 125 --------------------------------------- models/state.py | 3 - transient_chat.db | Bin 40960 -> 0 bytes 9 files changed, 2 insertions(+), 164 deletions(-) delete mode 100644 models/database.py delete mode 100644 transient_chat.db diff --git a/blueprints/admin.py b/blueprints/admin.py index 487f971..58c7714 100644 --- a/blueprints/admin.py +++ b/blueprints/admin.py @@ -110,8 +110,6 @@ def admin_ban_ip(): return jsonify({"error": "no ip"}), 400 banned_ips.add(ip_to_ban) - from models.database import db_add_ban - db_add_ban("ip", ip_to_ban) user_sessions.pop(username, None) @@ -131,8 +129,6 @@ def admin_ban_username(): return jsonify({"error": "no username"}), 400 banned_usernames.add(username) - from models.database import db_add_ban - db_add_ban("username", username) user_sessions.pop(username, None) @@ -152,8 +148,6 @@ def admin_ban_raw_ip(): return jsonify({"error": "no ip"}), 400 banned_ips.add(ip) - from models.database import db_add_ban - db_add_ban("ip", ip) return jsonify({"banned": ip}) @@ -177,8 +171,6 @@ def admin_release_user(): user_sessions.pop(username, None) user_ips.pop(username, None) - from models.database import db_delete_user_ip - db_delete_user_ip(username) for room in room_users: room_users[room].pop(username, None) @@ -220,14 +212,11 @@ def admin_nuke_room(): messages.pop(room, None) room_users.pop(room, None) room_owners.pop(room, None) - from models.database import db_delete_room_owner, db_remove_public_room - db_delete_room_owner(room) video_state.pop(room, None) if room in public_chat_rooms: public_chat_rooms.remove(room) - db_remove_public_room(room) return jsonify({"status": "nuked", "room": room}) @@ -244,12 +233,8 @@ def admin_unban(): if ban_type == "ip": banned_ips.discard(value) - from models.database import db_remove_ban - db_remove_ban("ip", value) elif ban_type == "username": banned_usernames.discard(value) - from models.database import db_remove_ban - db_remove_ban("username", value) else: return jsonify({"error": "Invalid ban type"}), 400 @@ -268,12 +253,8 @@ def admin_set_room_owner(): if owner: room_owners[room] = owner - from models.database import db_set_room_owner - db_set_room_owner(room, owner) else: room_owners.pop(room, None) - from models.database import db_delete_room_owner - db_delete_room_owner(room) return jsonify({"status": "updated", "room": room, "owner": owner}) diff --git a/blueprints/auth.py b/blueprints/auth.py index 89f0371..48eb901 100644 --- a/blueprints/auth.py +++ b/blueprints/auth.py @@ -44,9 +44,6 @@ def register(): } user_ips[username] = user_ip - from models.database import db_set_user_ip - db_set_user_ip(username, user_ip) - return jsonify({"username": username, "key": user_key}) @@ -65,6 +62,7 @@ def validate(): "key": key, "last_active": datetime.now(timezone.utc).isoformat(), } + user_ips[username] = request.remote_addr return jsonify({"status": "valid"}) return jsonify({"error": "Invalid credentials"}), 403 @@ -82,10 +80,8 @@ def logout(): if verify_user_key(username, key): # Remove from active sessions user_sessions.pop(username, None) - # Remove from user_ips in memory and SQLite to free up the username immediately + # Remove from user_ips in memory to free up the username immediately user_ips.pop(username, None) - from models.database import db_delete_user_ip - db_delete_user_ip(username) # Remove from all rooms from models.state import room_users diff --git a/blueprints/chat.py b/blueprints/chat.py index a0a7f32..784d71b 100644 --- a/blueprints/chat.py +++ b/blueprints/chat.py @@ -105,8 +105,6 @@ def room_user_list(room): room_users.setdefault(room, {}) if room not in room_owners and username not in room_users[room]: room_owners[room] = username # First user becomes the owner - from models.database import db_set_room_owner - db_set_room_owner(room, username) room_users[room][username] = datetime.now(timezone.utc).isoformat() @@ -159,8 +157,6 @@ def poll_room(room): room_users.setdefault(room, {}) if room not in room_owners and username not in room_users[room]: room_owners[room] = username - from models.database import db_set_room_owner - db_set_room_owner(room, username) room_users[room][username] = datetime.now(timezone.utc).isoformat() diff --git a/blueprints/rooms.py b/blueprints/rooms.py index 60d2f99..a7e5fb3 100644 --- a/blueprints/rooms.py +++ b/blueprints/rooms.py @@ -25,8 +25,6 @@ def get_or_create_rooms(): return jsonify({"error": "Room already exists"}), 400 public_chat_rooms.append(room_name) - from models.database import db_add_public_room - db_add_public_room(room_name) return jsonify({"name": room_name}) active_only = request.args.get("activeOnly") == "true" diff --git a/blueprints/watchparty.py b/blueprints/watchparty.py index 7a98500..b44bb51 100644 --- a/blueprints/watchparty.py +++ b/blueprints/watchparty.py @@ -13,8 +13,6 @@ def watchparty_video_control(room): # Ensure it's tracked as a public room if room not in public_chat_rooms: public_chat_rooms.append(room) - from models.database import db_add_public_room - db_add_public_room(room) # Ensure video_state storage is initialized video_state.setdefault(room, {}) diff --git a/models/cleanup.py b/models/cleanup.py index 51116bf..8307448 100644 --- a/models/cleanup.py +++ b/models/cleanup.py @@ -40,9 +40,6 @@ def cleanup_loop(): ): if room in public_chat_rooms: public_chat_rooms.remove(room) - from models.database import db_remove_public_room, db_delete_room_owner - db_remove_public_room(room) - db_delete_room_owner(room) messages.pop(room, None) room_users.pop(room, None) kicked_users.pop(room, None) diff --git a/models/database.py b/models/database.py deleted file mode 100644 index fa049d7..0000000 --- a/models/database.py +++ /dev/null @@ -1,125 +0,0 @@ -import sqlite3 -import os - -DB_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "transient_chat.db") - -def get_db_connection(): - conn = sqlite3.connect(DB_PATH) - conn.row_factory = sqlite3.Row - return conn - -def init_db(): - with get_db_connection() as conn: - conn.execute(""" - CREATE TABLE IF NOT EXISTS bans ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - type TEXT, - value TEXT UNIQUE - ) - """) - conn.execute(""" - CREATE TABLE IF NOT EXISTS room_owners ( - room TEXT PRIMARY KEY, - owner TEXT - ) - """) - conn.execute(""" - CREATE TABLE IF NOT EXISTS user_ips ( - username TEXT PRIMARY KEY, - ip TEXT - ) - """) - conn.execute(""" - CREATE TABLE IF NOT EXISTS public_rooms ( - room TEXT PRIMARY KEY - ) - """) - conn.commit() - -def load_persisted_state(state_module): - """Loads database state into the memory-based collections of state_module.""" - init_db() - with get_db_connection() as conn: - # Load bans - for row in conn.execute("SELECT type, value FROM bans"): - if row["type"] == "ip": - state_module.banned_ips.add(row["value"]) - elif row["type"] == "username": - state_module.banned_usernames.add(row["value"]) - - # Load room owners - for row in conn.execute("SELECT room, owner FROM room_owners"): - state_module.room_owners[row["room"]] = row["owner"] - - # Load user IPs - for row in conn.execute("SELECT username, ip FROM user_ips"): - state_module.user_ips[row["username"]] = row["ip"] - - # Load public rooms - for row in conn.execute("SELECT room FROM public_rooms"): - if row["room"] not in state_module.public_chat_rooms: - state_module.public_chat_rooms.append(row["room"]) - -def db_add_ban(ban_type, value): - try: - with get_db_connection() as conn: - conn.execute("INSERT OR IGNORE INTO bans (type, value) VALUES (?, ?)", (ban_type, value)) - conn.commit() - except Exception as e: - print(f"DB Error db_add_ban: {e}") - -def db_remove_ban(ban_type, value): - try: - with get_db_connection() as conn: - conn.execute("DELETE FROM bans WHERE type = ? AND value = ?", (ban_type, value)) - conn.commit() - except Exception as e: - print(f"DB Error db_remove_ban: {e}") - -def db_set_room_owner(room, owner): - try: - with get_db_connection() as conn: - conn.execute("INSERT OR REPLACE INTO room_owners (room, owner) VALUES (?, ?)", (room, owner)) - conn.commit() - except Exception as e: - print(f"DB Error db_set_room_owner: {e}") - -def db_delete_room_owner(room): - try: - with get_db_connection() as conn: - conn.execute("DELETE FROM room_owners WHERE room = ?", (room,)) - conn.commit() - except Exception as e: - print(f"DB Error db_delete_room_owner: {e}") - -def db_set_user_ip(username, ip): - try: - with get_db_connection() as conn: - conn.execute("INSERT OR REPLACE INTO user_ips (username, ip) VALUES (?, ?)", (username, ip)) - conn.commit() - except Exception as e: - print(f"DB Error db_set_user_ip: {e}") - -def db_delete_user_ip(username): - try: - with get_db_connection() as conn: - conn.execute("DELETE FROM user_ips WHERE username = ?", (username,)) - conn.commit() - except Exception as e: - print(f"DB Error db_delete_user_ip: {e}") - -def db_add_public_room(room): - try: - with get_db_connection() as conn: - conn.execute("INSERT OR IGNORE INTO public_rooms (room) VALUES (?)", (room,)) - conn.commit() - except Exception as e: - print(f"DB Error db_add_public_room: {e}") - -def db_remove_public_room(room): - try: - with get_db_connection() as conn: - conn.execute("DELETE FROM public_rooms WHERE room = ?", (room,)) - conn.commit() - except Exception as e: - print(f"DB Error db_remove_public_room: {e}") diff --git a/models/state.py b/models/state.py index fbee4f3..179f759 100644 --- a/models/state.py +++ b/models/state.py @@ -116,6 +116,3 @@ topical_chat_rooms = [ "Sustainable Living", ] -import sys -from models.database import load_persisted_state -load_persisted_state(sys.modules[__name__]) diff --git a/transient_chat.db b/transient_chat.db deleted file mode 100644 index b4a5b2b3aacb2973e216d4bf666db4e8b4cf0081..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 zcmeI*%Wu<29Kdl;CXY7M9Z-l2+O6tURY4VA0U-`t2*_GRK%re%q6cKT#Xv-wl-l8O z3|IaGj{Gb90cejs0S7KzI3OgrF*bE&w<*_Mh3`bRXFPt)?=x1UnMBDyOE)@EVBha= zZTOLW!8mLPVO+6o!!RtpRP~akqRzxinnarU#nv*;Jb7uVZ^q=*YeUVcx6}VlSEpZ@ zOXl{}YxCGI&ZG(f1Q0*~0R#|0009JkLxE_bV$IKsVI%U_y20jPt=n1mw)*{zFq@ye zw(PWA$97v+Z#Z_g#y(lK_oVo2+jZ`^_U+~N&DQd&ecf5j9kp<2vTR+S7e=RdFL)L{ z?&?93=MSQOQueaf_nO&KbyzA_teF`xyqmOl5C&UbXERKFNNIM@tzR|oqKkB(RKcIma~tV-CA*PwHNgTZaRzZ zev;_<=FfiLU-iWA4tAc2mBsebij(j46NR#Mxqc8@+(@q(ucdw?nTX#^%y@M;UXc($ z009ILKmY**5I_I{1Q0-ATm_O3_!<9?Yxfct0tg_000IagfB*srAb;%NX`mB+=;Ie>zW{?N_@fT=zk1@*>I@6{WfB_V(S0tg_0 z00IagfB*srAb`MsCGclao}P_@F!H+n2mL|h#edq+Jb&rTIsIz>A!TFMe-QK{FF7Vx zX%?iKb$5@|5geJ--vQX&QB~jd`2U@uzNmM4LqY%n1Q0*~0R#|0009ILKmdXNwZKt% zTI|jdRAf!)VSceD8)DDYfGkNR-1s{H6@5hd`+s^GfP?@72q1s}0tg_000IagfB*vH uCQuTE+G_u=^#A`D|BqV-6AuCiAb