Projects BibleWeb Navigation UX Beta activation system
Done

Beta activation system

Area: Navigation UX

1. Context

BibleWeb is in private beta. Rather than open sign-up, access to higher-tier features is granted by distributing short activation codes. This system lets invited users unlock their tier — with or without a Supabase account — by entering a code on the /activate page.

2. Functional

  • User visits /activate, enters a code, and submits the form.
  • The code is validated against a static lookup table of known codes.
  • On success:
    • Authenticated users: the beta_tier field is written to Supabase app_metadata via the admin service-role client. This persists across sessions and devices.
    • Unauthenticated users: a bibleweb_beta_tier cookie is set (1-year expiry, samesite=lax, readable client-side). On the client, the same cookie is also set directly via document.cookie as a backup.
    • The page reloads after 1.5 seconds to pick up the new session/cookie state.
  • If the user already has an active beta tier (beta_high, beta_low, or admin), an "already active" message is shown without re-processing.
  • Invalid codes show an error message in place; the form stays open for retry.

Status states: idle | success | invalid | already_active | error

Known codes (from beta-codes.ts):

Code Tier
vriend beta_high
friend beta_high
beta beta_low
uitproberen beta_low

Code matching is case-insensitive, trimmed.

AI daily limits per tier:

Tier Daily AI queries
admin 999
beta_high 20
beta_low 5
free 0

3. UX & Design

  • Centered card layout (max-width: 26rem), dark popup background, rounded corners.
  • Code input: centered text, letter-spacing, autocomplete="off" / autocorrect="off" — styled for entering codes, not typing prose.
  • Submit button: full-width, disabled when empty or loading; shows while loading.
  • "Request a code" mailto link at the bottom pointing to musicforhours@gmail.com.
  • Status messages use color-coded containers: green (success), blue (already active), red (invalid/error).
  • Page title set to activate.title from i18n.

4. Technical

Route: apps/web/src/routes/(app)/activate/+page.svelte

API: POST /api/beta/activate (apps/web/src/routes/api/beta/activate/+server.ts)

  • Rate limited: 5 attempts per minute per IP (brute-force prevention). Returns 429 with Retry-After on breach.
  • Reads current tier from getBetaTier(session, cookies.get('bibleweb_beta_tier')).
  • Validates code via validateBetaCode(code) — returns 'beta_high' | 'beta_low' | null.
  • For authenticated users, calls adminClient.auth.admin.updateUserById() to write app_metadata.beta_tier.
  • For unauthenticated users, calls cookies.set('bibleweb_beta_tier', tier, ...).

Core logic: apps/web/src/lib/server/beta-codes.ts

  • BETA_CODES — static Record<string, 'beta_high' | 'beta_low'> lookup.
  • getBetaTier(session, cookieBetaTier) — priority order: admin email check → app_metadata.beta_tier → cookie fallback → 'free'.
  • getAiDailyLimit(betaTier) — returns the daily AI query limit for a given tier.
  • validateBetaCode(code) — normalises and looks up a code.

Admin detection: compares session.user.email to process.env.ADMIN_EMAIL. Returns 'admin' tier if matched.

5. Status

Fully implemented. Code table, API endpoint, Supabase metadata write, cookie fallback, and the activation UI are all in place. To add new beta codes, update BETA_CODES in beta-codes.ts. To extend tier logic, update getBetaTier and getAiDailyLimit in the same file.