siftlode/.env.example
npeter83 2add173760 feat(m5c): onboarding — DB invites, request-access, admin approval, email
Move the access whitelist from the ALLOWED_EMAILS env var into a DB Invite table
(env kept as bootstrap fallback), and add a self-service request + admin approval
flow with fail-soft email.

- models: Invite(email, status pending|approved|denied, requested_at, decided_*)
- migration 0008: invites table; seed env ALLOWED_EMAILS u ADMIN_EMAILS as approved
- auth: is_allowed() (DB-first, env fallback); a denied Google login records a pending
  request and bounces to /?access=requested instead of a raw 403; public POST
  /auth/request-access; upsert is idempotent so repeats don't re-spam admins
- routes/admin.py (admin-only): list/approve/deny invites + manual add
- email.py: smtplib + Gmail App Password, fail-soft (skips if SMTP unset)
- /api/me exposes pending_invites; config + .env.example gain SMTP_*
- UI: Login 'Request access' form + access=requested/denied handling; Settings ->
  Access requests (approve/deny + add); admin nudge toast on pending requests

Verified locally: request-access creates a pending invite and emails the admin;
seed approved npeter83; guinea-pig yt.trash2023 denied until approved.
2026-06-12 01:43:07 +02:00

60 lines
2.8 KiB
Text

# ---- Copy this file to .env and fill in the values ----
# Postgres (used by docker-compose for the db service and the DATABASE_URL)
POSTGRES_USER=subfeed
POSTGRES_PASSWORD=change-this-password
POSTGRES_DB=subfeed
# Host port the app is exposed on (http://localhost:<APP_PORT>)
APP_PORT=8080
# Session signing key. Generate with: python -c "import secrets;print(secrets.token_urlsafe(48))"
SECRET_KEY=change-me-session-key
# Fernet key for encrypting stored OAuth refresh tokens. Generate with:
# python -c "import base64,os;print(base64.urlsafe_b64encode(os.urandom(32)).decode())"
TOKEN_ENCRYPTION_KEY=change-me-fernet-key
# Google OAuth client (Google Cloud Console -> APIs & Services -> Credentials -> OAuth client ID, type "Web application").
# Authorized redirect URI must match OAUTH_REDIRECT_URL exactly.
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
OAUTH_REDIRECT_URL=http://localhost:8080/auth/callback
# Invite list: only these Google account emails may sign in (comma-separated).
ALLOWED_EMAILS=
# Admin emails (subset of the above) get the admin role.
ADMIN_EMAILS=
# Optional: origin of a separately-served frontend dev server (enables CORS). Leave empty in production.
FRONTEND_ORIGIN=
# Optional YouTube Data API key (Google Cloud Console -> Credentials -> Create API key).
# When set, all public reads (channels/videos/playlist backfill + enrichment) use the key
# instead of a user's OAuth token, so 24/7 backfill never depends on a refresh token that
# would otherwise expire (Google expires refresh tokens after 7 days while the OAuth consent
# screen is in "Testing"). Strongly recommended for the always-on server instance.
YOUTUBE_API_KEY=
# Optional: outbound email for onboarding (access-request + approval notices). Gmail SMTP +
# App Password (account needs 2FA; generate at https://myaccount.google.com/apppasswords under
# the SENDING account). All optional — if unset, email is skipped and onboarding still works
# via in-app notifications. SMTP_FROM falls back to SMTP_USER.
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=
# --- Deployment role ---
# DATABASE_URL is set automatically by docker-compose.yml / docker-compose.server.yml to the
# bundled `db` service. Override it only for local dev against the central server DB, e.g.:
# DATABASE_URL=postgresql+psycopg://subfeed:<password>@your-db-host:5432/subfeed
# Exactly one instance may run the background scheduler. The central server keeps it on; every
# other instance (local dev, etc.) must set SCHEDULER_ENABLED=false. The compose files set this
# for you (server=true via docker-compose.server.yml, local=false via docker-compose.localdev.yml).
SCHEDULER_ENABLED=true
# On the central server, set this so backup.sh / restore.sh and plain `docker compose` commands
# target the server stack automatically:
# COMPOSE_FILE=docker-compose.server.yml