Skip to content

How the admin works

The admin pair is a React SPA and a Laravel API on one host, v3.togoactive.com, behind one nginx server block. The SPA is static files; anything under /api goes to PHP.

The shape of a request

Two details in that nginx config are load-bearing:

  • The whole /api URI reaches Laravel's front controller. The standard fastcgi-php.conf snippet assumes a .php in the path and 405s sub-path POSTs, so SCRIPT_FILENAME is pinned to index.php instead.
  • The SPA falls back to index.html, so client-side routes work on a hard refresh. index.html is served no-cache while hashed assets cache forever — that combination is what makes a deploy visible on a normal refresh.

The panel also carries a blanket X-Robots-Tag: noindex on every response. It is repeated inside the index.html location block, because nginx's add_header overrides rather than merges — without the repeat, the one page that most needed the header would be served without it.

Authentication and authorisation

Three middleware layers run in order, and they answer different questions:

MiddlewareQuestion
auth:adminIs this a signed-in admin at all?
admin.can:<permission>Does their role grant this capability — events.view, event_setup.edit?
admin.event (CheckEventAccess)Do they have access to this specific event?

Access is granted per event, through admin_user_events.role. There is no account that implicitly sees every event. Website content has a finer split again, per surface, so someone can edit pages without reaching settings.

The frontend

A Vite SPA. The conventions worth knowing before writing a screen:

  • src/utils/api.js wraps every call. api.get takes an endpoint, not a full URL.
  • Settings screens share a shape: SectionCard / ToggleCard / SaveBar / SetupHelpPanel, useState seeded by getInitialForm, and isDirty from a JSON comparison — which is what makes the save bar appear only on a real change.
  • Event context comes from useOutletContext via EventLayout.
  • Never use native alert/confirm/prompt — use the swal utility.
  • File uploads use POST with _method=PUT, not api.put. A real PUT does not carry FormData through PHP.

Help tips

Tooltips are <HelpTip id="...">, resolved from src/help/helptips.json, which is generated from this documentation site. See the help tip pipeline.

Deploying

Building the admin frontend publishes it

nginx serves admin-frontend/dist directly. npm run build is the deploy — there is no separate publish step, and the change is live the moment the build finishes.

To type-check without publishing, build somewhere else:

npx vite build --outDir /tmp/build-check --emptyOutDir

Two more traps on this box:

  • Never run artisan as root in admin-backend. It leaves root-owned cache files that php-fpm cannot read, and the API starts failing afterwards.
  • Run migrations one file at a time with --path. There are pending migrations, and the first of them fails.

The admin's own API quirks

Documented properly in Admin backend API; the ones that surprise people:

  • Six GET endpoints write rows. They are not safe to retry blindly.
  • Publishing validates nothing. A page can be published in a state the builder would not let you construct.

Organiser guide and developer documentation for the TogoActive platform.