The app had no cleanup of any kind: in multi-user mode every first visit mints a users row, so the demo has been accumulating one permanent account per visitor along with everything they generated. cleanup.py sweeps guests idle for AIDND_GUEST_RETENTION_DAYS (default 5), once at startup and then every few hours. Startup is the load-bearing trigger — the free tier sleeps after ~15 minutes, so a long timer rarely gets to fire. Idle is COALESCE(last_seen_at, created_at), not last_seen_at: _touch only writes that column hourly, and a guest minted by /auth/me has it NULL until its second request, so the simpler query would have deleted brand-new visitors mid-session. It's one Core DELETE rather than db.delete(user), which would SELECT every adventure, action and memory into Python purely to delete them — the same egress pattern as the 189x fix. Every FK from users down is ON DELETE CASCADE, so the database does the whole graph and returns a count. The filter requires is_guest AND email IS NULL, so registered users (who upgrade in place) and local mode's implicit user are both out of reach, and is_public is output-only so a guest can never own content another user can see. Session cookies have no expiry and can outlive a swept row; that path 401s and the frontend's existing retry re-mints a session. Guests are told: /auth/me serves guest_retention_days and the signup modal states the window, sourced from the server so it can't drift from what is enforced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CYEJKobJ2Re4Dv7qUoSA7
62 lines
2.4 KiB
YAML
62 lines
2.4 KiB
YAML
# Render Blueprint — https://render.com/docs/blueprint-spec
|
|
#
|
|
# One web service: the multi-stage Dockerfile builds the React SPA and the
|
|
# FastAPI backend into a single image that serves both same-origin. Database
|
|
# is external Neon Postgres (set AIDND_DATABASE_URL in the dashboard); the
|
|
# free tier has no persistent disk, which is why the DB lives off-box.
|
|
#
|
|
# First deploy: create a Blueprint from this repo in the Render dashboard,
|
|
# then fill the `sync: false` secrets (Neon URL + demo key). Pushes to `main`
|
|
# auto-deploy thereafter.
|
|
|
|
services:
|
|
- type: web
|
|
name: ai-dnd
|
|
runtime: docker
|
|
dockerfilePath: ./Dockerfile
|
|
dockerContext: .
|
|
plan: free # sleeps after ~15 min idle; first wake takes ~30-60s
|
|
region: virginia # us-east, closest to the Neon us-east-1 database
|
|
healthCheckPath: /api/health
|
|
autoDeploy: true
|
|
envVars:
|
|
# Multi-user hardening on (guest sessions, per-user data, rate limits).
|
|
- key: AIDND_MULTI_USER
|
|
value: "1"
|
|
|
|
# Signs session cookies + encrypts stored API keys. Render generates a
|
|
# strong value once and keeps it stable across deploys (a regenerated
|
|
# secret would log out every user on each deploy).
|
|
- key: AIDND_SECRET_KEY
|
|
generateValue: true
|
|
|
|
# Neon Postgres connection string (pooled, sslmode=require). Secret —
|
|
# set it in the dashboard; never commit it.
|
|
- key: AIDND_DATABASE_URL
|
|
sync: false
|
|
|
|
# --- Shared demo key (optional): lets first-time visitors play without
|
|
# bringing their own API key. Set these in the dashboard to enable;
|
|
# leave unset to require BYOK. ---
|
|
- key: AIDND_DEMO_API_KEY
|
|
sync: false
|
|
- key: AIDND_DEMO_MODELS
|
|
sync: false
|
|
- key: AIDND_DEMO_TURNS_PER_DAY
|
|
value: "20"
|
|
|
|
# Comma-separated emails of trusted testers: unmetered demo turns, plus
|
|
# the AI Chat page (hidden from everyone else). Set in the dashboard;
|
|
# leave unset and nobody gets it. Registered accounts only.
|
|
- key: AIDND_POWER_USERS
|
|
sync: false
|
|
|
|
# Guest retention: one account is minted per first-time visitor, so idle
|
|
# ones are collected (with their adventures) to keep the free-tier
|
|
# Postgres from filling with abandoned demo data. Registered accounts are
|
|
# never touched. 0 would disable it.
|
|
- key: AIDND_GUEST_RETENTION_DAYS
|
|
value: "5"
|
|
|
|
# CORS is unset on purpose: the SPA is served same-origin by FastAPI.
|