Phase 8: optional accounts, per-user data, shared demo key

Guest-first multi-user mode behind AIDND_MULTI_USER (local installs
unchanged): signed-cookie guest sessions bootstrapped by /api/auth/me,
register upgrades the guest in place, login/logout, per-IP rate limits.
Every router scoped by user_id; Settings become per-user with the API
key Fernet-encrypted at rest and write-only through the API. Users
without a key get a server-funded demo key (OpenRouter free models,
20 turns/day, memory bank disabled on demo turns). Public read-only
demo scenarios (seed_demo.py); debug log restricted to local mode.
Frontend: auth modal + guest nudge, 401 re-establish/retry, demo
banner and key management in Settings.

Migrations 13-23 adopt existing data under a local user and encrypt
stored keys. Verified: migration on a copy of real data.db, two-session
isolation + register/login via curl and Chrome, demo cap 429, live
OpenRouter turn through the encrypted-key path, vite build + oxlint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFsGHju9szibJJa2YJcdbg
This commit is contained in:
parththakkar106
2026-07-06 23:04:03 +05:30
co-authored by Claude Fable 5
parent 253b533d3b
commit de4db373f2
26 changed files with 1247 additions and 189 deletions
+11 -1
View File
@@ -66,6 +66,7 @@ class ScenarioUpdate(BaseModel):
class ScenarioOut(ORMModel, ScenarioBase):
id: int
is_public: bool = False # shared demo content — read-only for everyone
created_at: datetime
updated_at: datetime
story_cards: list[StoryCardOut] = []
@@ -77,6 +78,7 @@ class ScenarioListItem(ORMModel):
title: str
description: str
tags: str
is_public: bool = False
updated_at: datetime
@@ -226,11 +228,19 @@ class AdventureScriptUpdate(BaseModel):
output_js: str | None = None
# ---------- Auth (Phase 8) ----------
class AuthCredentials(BaseModel):
email: str
password: str
# ---------- Settings ----------
class SettingsOut(ORMModel):
endpoint_url: str
api_key: str
# The key itself is never echoed back (encrypted at rest, write-only).
has_api_key: bool
model: str
api_mode: str
temperature: float