Add Docker deployment and per-player secret-link viewers.
Each player gets an isolated SQLite viewer via a unique URL without login, with landing page warnings to save the link and compose-based hosting for sharing with others. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+63
@@ -0,0 +1,63 @@
|
||||
"""Per-viewer isolation via secret URL tokens (no login)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import secrets
|
||||
from pathlib import Path
|
||||
|
||||
from db import get_connection, init_db
|
||||
|
||||
VIEWER_ID_RE = re.compile(r"^[A-Za-z0-9_-]{16,64}$")
|
||||
LOCAL_VIEWER_ID = "local"
|
||||
|
||||
|
||||
def generate_viewer_id() -> str:
|
||||
return secrets.token_urlsafe(16)
|
||||
|
||||
|
||||
def is_valid_viewer_id(viewer_id: str) -> bool:
|
||||
if viewer_id == LOCAL_VIEWER_ID:
|
||||
return True
|
||||
return bool(viewer_id and VIEWER_ID_RE.match(viewer_id))
|
||||
|
||||
|
||||
def viewers_dir(data_dir: Path) -> Path:
|
||||
return data_dir / "viewers"
|
||||
|
||||
|
||||
def viewer_db_path(viewer_id: str, data_dir: Path) -> Path:
|
||||
return viewers_dir(data_dir) / f"{viewer_id}.db"
|
||||
|
||||
|
||||
def viewer_exists(viewer_id: str, data_dir: Path) -> bool:
|
||||
return viewer_db_path(viewer_id, data_dir).exists()
|
||||
|
||||
|
||||
def create_viewer(data_dir: Path) -> str:
|
||||
"""Create a new viewer with an empty SQLite database."""
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
viewers_dir(data_dir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for _ in range(10):
|
||||
viewer_id = generate_viewer_id()
|
||||
db_path = viewer_db_path(viewer_id, data_dir)
|
||||
if db_path.exists():
|
||||
continue
|
||||
conn = get_connection(db_path)
|
||||
init_db(conn)
|
||||
conn.close()
|
||||
return viewer_id
|
||||
|
||||
raise RuntimeError("Could not allocate a unique viewer id")
|
||||
|
||||
|
||||
def ensure_local_viewer(data_dir: Path) -> str:
|
||||
"""CLI default viewer – not secret, for local single-user use."""
|
||||
db_path = viewer_db_path(LOCAL_VIEWER_ID, data_dir)
|
||||
if db_path.exists():
|
||||
return LOCAL_VIEWER_ID
|
||||
conn = get_connection(db_path)
|
||||
init_db(conn)
|
||||
conn.close()
|
||||
return LOCAL_VIEWER_ID
|
||||
Reference in New Issue
Block a user