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.
2026-03-29 00:14:46 -05:00
|
|
|
"""
|
|
|
|
|
Platform Gateway — Auth handlers (login, logout, register).
|
|
|
|
|
"""
|
|
|
|
|
|
2026-03-29 07:02:09 -05:00
|
|
|
"""
|
|
|
|
|
NOTE: Passwords are hashed with bcrypt. Any existing SHA-256 hashed passwords
|
|
|
|
|
in the database will no longer work. The admin user is re-seeded on first boot
|
|
|
|
|
if no users exist. Other users need manual password reset.
|
|
|
|
|
"""
|
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.
2026-03-29 00:14:46 -05:00
|
|
|
import json
|
|
|
|
|
import sqlite3
|
|
|
|
|
|
2026-03-29 07:02:09 -05:00
|
|
|
import bcrypt
|
|
|
|
|
|
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.
2026-03-29 00:14:46 -05:00
|
|
|
from database import get_db
|
|
|
|
|
from sessions import create_session, delete_session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def handle_login(handler, body):
|
|
|
|
|
try:
|
|
|
|
|
data = json.loads(body)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
handler._send_json({"error": "Invalid JSON"}, 400)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
username = data.get("username", "").strip().lower()
|
|
|
|
|
password = data.get("password", "")
|
|
|
|
|
|
|
|
|
|
if not username or not password:
|
|
|
|
|
handler._send_json({"error": "Username and password required"}, 400)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
conn = get_db()
|
2026-03-29 07:02:09 -05:00
|
|
|
user = conn.execute("SELECT * FROM users WHERE username = ?",
|
|
|
|
|
(username,)).fetchone()
|
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.
2026-03-29 00:14:46 -05:00
|
|
|
conn.close()
|
|
|
|
|
|
2026-03-29 07:02:09 -05:00
|
|
|
if not user or not bcrypt.checkpw(password.encode(), user["password_hash"].encode()):
|
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.
2026-03-29 00:14:46 -05:00
|
|
|
handler._send_json({"error": "Invalid credentials"}, 401)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
token = create_session(user["id"])
|
|
|
|
|
|
|
|
|
|
handler.send_response(200)
|
|
|
|
|
handler.send_header("Content-Type", "application/json")
|
|
|
|
|
handler._set_session_cookie(token)
|
|
|
|
|
resp = json.dumps({
|
|
|
|
|
"success": True,
|
|
|
|
|
"user": {"id": user["id"], "username": user["username"], "display_name": user["display_name"]}
|
|
|
|
|
}).encode()
|
|
|
|
|
handler.send_header("Content-Length", len(resp))
|
|
|
|
|
handler.end_headers()
|
|
|
|
|
handler.wfile.write(resp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def handle_logout(handler):
|
|
|
|
|
token = handler._get_session_token()
|
|
|
|
|
delete_session(token)
|
|
|
|
|
handler.send_response(200)
|
|
|
|
|
handler.send_header("Content-Type", "application/json")
|
|
|
|
|
handler.send_header("Set-Cookie", "platform_session=; Path=/; Max-Age=0")
|
|
|
|
|
resp = b'{"success": true}'
|
|
|
|
|
handler.send_header("Content-Length", len(resp))
|
|
|
|
|
handler.end_headers()
|
|
|
|
|
handler.wfile.write(resp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def handle_register(handler, body):
|
|
|
|
|
try:
|
|
|
|
|
data = json.loads(body)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
handler._send_json({"error": "Invalid JSON"}, 400)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
username = data.get("username", "").strip().lower()
|
|
|
|
|
password = data.get("password", "")
|
|
|
|
|
display_name = data.get("display_name", username)
|
|
|
|
|
|
|
|
|
|
if not username or not password:
|
|
|
|
|
handler._send_json({"error": "Username and password required"}, 400)
|
|
|
|
|
return
|
|
|
|
|
|
2026-03-29 07:02:09 -05:00
|
|
|
pw_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
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.
2026-03-29 00:14:46 -05:00
|
|
|
|
|
|
|
|
conn = get_db()
|
|
|
|
|
try:
|
|
|
|
|
conn.execute("INSERT INTO users (username, password_hash, display_name) VALUES (?, ?, ?)",
|
|
|
|
|
(username, pw_hash, display_name))
|
|
|
|
|
conn.commit()
|
|
|
|
|
user_id = conn.execute("SELECT id FROM users WHERE username = ?", (username,)).fetchone()["id"]
|
|
|
|
|
conn.close()
|
|
|
|
|
handler._send_json({"success": True, "user_id": user_id})
|
|
|
|
|
except sqlite3.IntegrityError:
|
|
|
|
|
conn.close()
|
|
|
|
|
handler._send_json({"error": "Username already exists"}, 409)
|