Appearance
TogoActive v3 — Architecture Overview
TogoActive is a virtual fundraising / activity-challenge platform ("powered by togoparts.com"). v3 is the new generation of the platform: a React admin console, a Laravel admin API, a Laravel white-label (WL) public API, and a Next.js white-label public site — all operating on the same live production database as the still-running legacy application, and integrating deeply with the Togoparts (TGP) platform for user accounts, Strava activity data, and leaderboard ranking.
This document is the map. Each subsystem has its own deep-dive doc — see README.md for the full index.
Related docs: 02-admin-frontend.md · 03-admin-backend-api.md · 04-wl-api.md · 05-wl-frontend.md · 06-database-schema.md · 07-legacy-app-and-tgp-integration.md · 08-event-setup-guide.md · 09-developer-guide.md
1. The five systems
| # | System | Stack | Location (dev box) | GitHub repo | Role |
|---|---|---|---|---|---|
| 1 | Admin Frontend | React 18 + Vite 5 SPA | /var/www/togoactive-development/admin-frontend | tga-v3-admin-web | The v3 admin console. Everything an event manager configures happens here. Served as a static dist/ build. |
| 2 | Admin Backend | Laravel 10 API | /var/www/togoactive-development/admin-backend | tga-v3-admin-api | REST API (/api/v1) behind the admin SPA. Sanctum auth, RBAC, per-event roles, feature settings, all event CRUD. Writes to the live shared DB. |
| 3 | WL API | Laravel API | /var/www/togoactive-development/wl-event/frontend-api-wl-development | tga-v3-wl-api | Public-facing API (/api/v1) for the white-label event sites: event resolution, registration, payments (Stripe), donations, profiles, leaderboards, emails, crons. |
| 4 | WL Frontend | Next.js 14 (App Router) + TypeScript | /var/www/togoactive-development/wl-event/frontend-wl-development | tga-v3-wl-web | The public white-label event site. One deployment serves many events — the event is resolved from the request's Host domain. Renders admin-built pages from blocks JSON. |
| 5 | Legacy app ("old admin") | Laravel | /var/www/togoactive | — (production) | The previous-generation production app. Still live. Still owns Strava activity ingestion, leaderboard sync, ranking, and achievement awarding (all events); achievement emails only for events < 49. Shares the same database. |
Plus the external Togoparts platform (togoparts.com): the original cycling-community site whose database (mysql_tgp) holds the real user accounts, Strava challenge activity data, denormalized leaderboards, and the rank stored procedures every system calls.
2. Big picture
The single most important architectural fact: all Laravel apps (admin API, WL API, legacy) point at the same live production database. There is no "dev database" separation by default.
- A
configurationrow edited from the new admin is instantly visible to the WL API, the WL site, and the legacy app. - Code, on the other hand, is deployed per-server — pushing to GitHub does not change anything until the code is pulled/deployed on each machine.
- This asymmetry explains most "works here, broken there" bugs (see §6 Deployment).
3. How an event exists across the systems
An "event" (e.g. event 49, TOGO SG61) is:
- A row in
eventswith amode(default_mode/sessionalmode/donation_mode) plus satellite rows:events_dates,event_images,registration_setup,social_seo,events_meta(key/value, incl.TGP_CHALLENGE_ID),event_domains, and JSON blobs inconfiguration(appearance, email branding, donation config, fundraiser config, default messages, payment gateways). - A set of feature flags —
FeatureRegistryin the admin backend defines ~130 features in 8 groups; the event's mode provides defaults, andevent_feature_settingsstores per-event overrides. Flags hide/show admin sidebar sections and gate WL behavior (e.g.registration.merchandiseenables the merch step). - A set of builder pages —
event_pagesrows whoseblocksJSON is rendered 1:1 by the WL site'sBlockRenderer(~80 block types). System page types (user_detail,team_detail,donation,my_profile, …) power dynamic routes like/user/{id}. - A registration form schema — the Form Builder writes the
event_registrationconfiguration blob (fields, merchandise copy, success page; the upgrade tab writes theevent_upgradekey). - Commerce data —
rewards(SKUs),coupons,multi_quantity_discount, Stripe credentials from the sharedpayment_gateway_configlibrary. - A mapping to a TGP challenge —
TgpChallenge::cid()resolves the TGA event to a Togoparts challenge id (theconfiguration/events_metaTGP_CHALLENGE_IDrows; the admin backend and legacy app also keep a static map for old events 8–37), which is where leaderboards and ranking live. - One or more public domains —
event_domainsrows; the WL site resolves every request's Host header through the WL API'sresolve-eventendpoint.
4. The main data flows
Admin edit → public site. Admin SPA calls the admin API, which writes the shared DB and pings the WL site's revalidation endpoint (WlCache::flush). The WL site otherwise caches event config/pages for 60 seconds per event; a hard browser reload bypasses the cache. Admin preview iframes (Page Builder /builder-preview, Form Builder /registration-preview, Appearance preview) talk to the WL site directly over pb-*/fb-* postMessage — so admins see unsaved changes rendered by the real public renderer.
Visitor page view. Browser → WL site (Next.js) → middleware resolves Host → event id (WL API resolve-event) → SSR fetches event config + page blocks (60s TTL cache) → BlockRenderer renders the block tree → client hooks fetch live data (stats, leaderboards, viewer profile).
Registration. Step 1 immediately "partial-registers" the user (POST /auth/register creates the Togoparts account via TgpAccountProvisioner, the event_users row, and a $0 payments row — abandoners still count). Optional merchandise and donation steps produce Stripe Checkout sessions (StripeService, per-event credentials, itemised line items). The Stripe webhook + a verify-polling return page finalize the payment; success is a shareable token URL; emails are rendered by EmailTemplateRenderer with event branding; donation totals flow into the TGP leaderboard tables via DonationLeaderboardService.
Activity & ranking. Strava data is ingested by the legacy cron fleet (Stage 2) into the TGP DB, synced to leaderboards (Stage 3), and ranked by calling TGP stored procedures (UpdateRanks, UpdateDonationRanks, UpdateTeamRanks, …). The v3 WL API runs its own donations:recalc every 10 minutes for donation totals and exposes token-checked internal recalc endpoints. Achievement ownership split: the legacy AchievementMasterCron still awards achievements for all events with open windows (including ≥ 49), but achievement notification emails split at event 49 — legacy ChallengeNotification for events < 49, the v3 WL API's achievements:notify for events ≥ 49 (the challenge_achievement_winners.notified flag is the handshake; the legacy per-event SG61 cron is deliberately disabled).
5. Servers & environments
| Machine | What runs there |
|---|---|
| Dev/staging box — 128.199.72.46 (this machine) | All four v3 working copies; admin SPA served from dist/ (v3.togoactive.com via nginx+php-fpm; DNS/certbot were pending); dev WL API (:8000) and WL site (:3000); admin API (:8001). |
| Production WL server — wl-api.togoparts.com / 178.128.113.107 | Production WL API + WL site (wl-web.service systemd unit, safe-build.sh zero-downtime deploys behind Apache). |
| DigitalOcean managed MySQL | The shared togoactive DB (+ togoactive-stage) and the TGP production DB. Shared by every app on both servers. |
A cross-server dependency worth memorizing: the admin SPA's email-template preview/test-send and donation picker call the production WL API (VITE_WL_API_URL=https://wl-api.togoparts.com). If the prod WL API deploy is stale, those admin features break while everything else works — a preview-only bug signature.
6. Deployment model
- Code lives in four private GitHub repos (
mainbranch):tga-v3-admin-api,tga-v3-admin-web,tga-v3-wl-api,tga-v3-wl-web. - Developers commit + push per coherent change set; deployment to each server is a separate manual step (the team runs its own
run.sh/ deploy scripts — see 09-developer-guide.md). - The admin frontend has no HMR in its served form — it must be rebuilt (
npm run build) after every change for the staticdist/to update. - The WL site deploys via
safe-build.sh: build into.next-build, atomic swap, systemd restart, health checks, automatic rollback. - Database changes (migrations run against the live DB — v3 migrations are applied there) and
configurationedits are instantly global. Treat the DB as production at all times.
7. Security & access model (summary)
- Admin API: Sanctum bearer tokens (24h default), custom
adminguard onadmin_users. Global RBAC (roles → permissions,super_adminbypass) plus per-event roles onadmin_user_events.role(owner/admin/editor/viewer) enforced byCheckEventAccess— viewers are read-only server-side. - WL API: public endpoints throttled (60/min default, 300/min for read-heavy GETs, 3/min OTP); participant auth via Sanctum after a layered password verify against Togoparts
users.passwd(crypt()handles both bcrypt and legacy hashes; strictly read-only — password columns are never rewritten); Google/Apple login via a JWT bridge to togopartsauth_v2.php. - Internal bridges: admin API → WL API calls (automation test-send, team/individual recalc) are token-checked with
WL_INTERNAL_TOKEN; WL cache revalidation usesWL_REVALIDATE_SECRET; custom-domain provisioning usesWL_DEPLOY_TOKEN. - Payments: per-event Stripe credentials in the shared
payment_gateway_configlibrary; new events silently fall back to a shared TEST credential (togopart-test) — replacing it is a launch-checklist item.
8. Where to read next
- Setting up or operating an event → 08-event-setup-guide.md (every admin menu, step-by-step setup, WL impact).
- Working on the admin UI → 02-admin-frontend.md; its API → 03-admin-backend-api.md.
- Working on the public site → 05-wl-frontend.md; its API → 04-wl-api.md.
- Understanding tables → 06-database-schema.md; legacy crons/TGP → 07-legacy-app-and-tgp-integration.md.
- Joining the team / environment setup / conventions → 09-developer-guide.md.