Skip to content

WL multi-version hosting — briefing for the WL server team

Purpose: we want to run more than one version of the WL product at the same time, and let the admin panel pick, per domain, which version serves it — so connecting/switching a domain also switches its version.

Who does what today:

  • 128.199.72.46 (this box) — TogoActive admin only (admin-frontend + admin-backend, v3.togoactive.com). We own this.
  • 178.128.113.107the WL host. Runs the public WL site, the WL API, and the deploy agent. We do not have this server's code or access. Everything below marked ❓ is a question for you.

Shared between both: one database (DigitalOcean togoactive).


Part A — What the admin side does today (verified in our code)

1. Domain connect flow

StepWhere
Admin enters domain in Setup → Domainadmin-frontend/src/components/event-manage/setup/SetupDomain.jsx
POST /api/v1/events/{id}/domain → creates event_domains row, status=pending, TXT token, a_record_ipDomainController::store
Admin adds TXT + A record at their DNS providercustomer side
POST .../domain/{domainId}/verify-txt then verify-dnsstatus=activeDomainController
On activate / disconnect → fire deploy webhookApp\Services\WlDeployService::syncDomains()

2. What we call on your server

POST https://wl-deploy.togoparts.com/sync-domains
Authorization: Bearer <WL_DEPLOY_TOKEN>
(no body)

Treated as best-effort: HTTP 2xx or 409 = success; anything else becomes a non-blocking warning in the admin UI with a "Retry sync" button. Timeouts: 5s connect / 20s total.

Our deploy.sh also uses: POST /deploy, POST /deploy/web, POST /deploy/api, GET /health. /health currently answers {"ok":true,"running":null}.

3. event_domains table (shared DB — you read this)

id, event_id, domain, status enum(pending|txt_verified|active|failed),
txt_record_name, txt_record_value, a_record_ip,
txt_verified_at, dns_verified_at, last_checked_at, created_at, updated_at
UNIQUE(domain)   -- one host → exactly one event
INDEX(event_id)  -- unique(event_id) was DROPPED: an event may have MANY domains

Live rows today (all status=active):

eventdomain
46cyclehome2027-v3.togoparts.com
49togosg61.togoparts.com
50strideforcare.sg
52moveforgood.togoparts.com

a_record_ip is written from admin-backend config SERVER_IP=178.128.113.107; verify-dns accepts any IP in SERVER_IPS=178.128.113.107,128.199.72.46.

4. How a request finds its event at runtime

frontend-wl-development/middleware.ts (Edge) reads the Host header → calls WL API GET /api/v1/resolve-event?host=<host> → looks up event_domains where status=active → forwards x-resolved-event-id to the app. EVENT_ID env, when set, overrides it.

Caching to be aware of when switching: resolve-event sends Cache-Control: public, max-age=60, and the middleware keeps its own 60s in-memory host→event cache.

5. What "a version" means for us

The candidate new version (v4, category-based paid registration) is the same two repos on a different branch:

repov3v4
togoparts/tga-v3-wl-web (Next.js)mainv4
togoparts/tga-v3-wl-api (Laravel)mainv4

So "version" ≈ "branch + its own running instance". Not a fork, not a different product.


Part B — What we can see about your server from outside (verified)

  • wl-api.togoparts.com, wl-deploy.togoparts.com, and every customer domain all resolve to 178.128.113.107.
  • Server: Apache/2.4.41 (Ubuntu), X-Powered-By: Next.js → Apache reverse-proxies into a Next.js process.
  • Our Next.js middleware comments say the app sits behind Apache and that Apache terminates TLS and does not send X-Forwarded-Proto.
  • One deployment appears to serve every customer domain (multi-tenant, host-resolved) — no per-event build.

Part C — Questions for you ❓

C1. The deploy agent (wl-deploy.togoparts.com)

  1. Where does its source live (repo/path)? Can we get read access, or would you make the changes yourself?
  2. What exactly does POST /sync-domains do — does it read event_domains from the shared DB, or does it get the list some other way?
  3. What does it generate per domain: an Apache vhost file from a template? Where do those files live? Is certbot run per domain?
  4. What do /deploy, /deploy/web, /deploy/api do step by step (git pull → build → restart what)?
  5. Is there any concurrency lock (the 409 we treat as "already running")?
  6. Is it possible to pass a body/params to these endpoints today, or are they strictly parameterless?

C2. Current runtime layout

  1. How is the Next.js app run — pm2, systemd, docker? Which port? Which directory?
  2. How is the WL API run — php-fpm pool, its own vhost? Which directory, which .env?
  3. Is there exactly one web process and one API process serving all domains today? (We believe yes — please confirm.)
  4. How does the Apache vhost template look for one customer domain (a sanitised sample would help a lot)?

C3. Feasibility of the plan

  1. Can we run a second web instance on a second port (e.g. v3 :3000, v4 :3100) on the same server — RAM/CPU headroom OK?
  2. Can we run a second WL API (separate php-fpm pool + own .env/storage/config cache) alongside the current one?
  3. Can the vhost template become per-domain conditional — i.e. the proxy_pass upstream chosen from a version value we store on the event_domains row?
  4. Would you rather the agent read version from the DB itself, or have us send it in the /sync-domains request?
  5. Can /deploy/web and /deploy/api take a version (branch + target directory + which process to restart), so deploying v4 does not touch v3?
  6. Any constraint that forces a new version onto a different server instead of a different port? (Big deal for us — see the warning in Part E.)

C4. Operations

  1. How are SSL certs managed — certbot per domain, auto-renew cron? Does changing a vhost's upstream affect the cert at all?
  2. What is the rollback story if a vhost regenerates badly — is there a backup of the previous vhost set?
  3. Where do the agent's logs go? Can we see why a /sync-domains failed?
  4. Any staging/test hostname we can point at a v4 instance without touching a customer domain?

Part D — The design we have in mind (react to this, please)

Per-domain version, chosen in the admin, switchable without DNS changes.

  1. DB — new lookup table so a new version never needs a migration:

    wl_versions(id, key, label, branch, web_port, api_base_url, is_default, is_active)
        'v3' | main | 3000        'v4' | v4 | 3100
    event_domains.version_id  (FK, default = the is_default row)

    Existing 4 domains backfill to v3zero behaviour change on day one.

  2. Admin — version dropdown on the connect form; version badge per domain row; new PATCH /events/{id}/domain/{domainId}/version that changes the value and then fires the existing sync-domains webhook. That is the switch.

  3. Your agent — vhost generation becomes version-aware: each domain's proxy_pass points at its version's upstream port. Deploy endpoints take a version so each instance updates independently.

  4. Your server — one checkout + one port + one process per version, for both web and API. Each version's web .env points API_BASE_URL at its own API.

  5. Optional safety net/resolve-event also returns the version, so an instance that receives a request meant for another version can show a clear error instead of silently serving the wrong product.

Rollout order: DB columns (no-op) → stand up v4 instance with no domains → agent changes → admin UI → migrate one real domain. Rollback = switch the version back and re-sync.


Part E — Two things we need agreed

1. All versions on the SAME server, different ports.a_record_ip today is a single fixed IP (178.128.113.107) baked into the admin config, and it is what customers put in their DNS. If a version lives on a different server, switching a domain's version means the customer must change their DNS A record — no instant switch, no instant rollback, and SSL has to be re-issued. Different ports on one host keeps a switch to "rewrite vhost + reload", i.e. seconds.

2. Database compatibility rule. v3 and v4 share one database. Any v4 migration is immediately visible to v3 code. So v4 must be additive only — no column renames, drops, or type changes — otherwise every v3 domain breaks the moment v4 migrates. This needs to be a written rule before we run two versions in production.


Part F — What we would like out of the meeting

  • [ ] Access to (or ownership agreement on) the deploy agent's code
  • [ ] A sanitised sample Apache vhost for one customer domain
  • [ ] Confirmation that a second web + API instance can run on the same box
  • [ ] Agreement on where version is read (DB vs request payload)
  • [ ] A test hostname we can point at the v4 instance
  • [ ] Sign-off on the additive-only migration rule

Organiser guide and developer documentation for the TogoActive platform.