Skip to content

Backend

Joël Deffner edited this page Jul 17, 2026 · 1 revision

Backend

The backend is a CodeIgniter 4.7 application (PHP 8.2) in app/. It is a JSON-only API: every controller returns $this->response->setJSON(...), never HTML. Authentication is handled by CodeIgniter Shield (session-based), the database is MariaDB.

Routes

All routes live in app/Config/Routes.php, entirely inside one api group. Summary (full request/response shapes in API Reference):

Area Routes Auth
Utility GET api/ping, POST api/echo public
Weather GET api/weather, GET api/weather/reports, POST api/weather/reports/{id}/refresh public
Activity GET api/activity public
Pilot profiles GET api/users/{username} public
Auth api/auth/me, register, login, logout public/guest
Meets list/detail/weather public; create/update/join/leave apiauth filter on mutations
Groups list/detail public; create/update/join/leave apiauth filter on mutations
Chat api/chat/messages (GET/POST/DELETE) apiauth (whole group)
Own profile api/profile (GET/PUT), api/profile/subscription (PUT) apiauth
Admin api/admin/users CRUD adminapi filter

Controllers (app/Controllers/)

Controller Responsibility
Api.php Smoke-test endpoints: ping and echo.
Weather.php Open-Meteo proxy: city search + forecast, cached weather reports, refresh. See Weather Integration.
Api/AuthController.php Session login/logout, me (session status + CSRF token), and registration. Registration creates a Shield account in group user, logs the session in, and returns the same { user, csrf } shape as login.
Api/MeetsController.php The meets domain: list, detail, create (with geocoding fallback), edit (creator or admin only), join/leave with full/duplicate conflict handling, and per-meet weather.
Api/GroupsController.php Flight groups: list, detail, create (creator auto-joins), edit (founder or admin only), join/leave.
Api/ChatController.php Global and per-group chat: incremental listing (after cursor), sending, and deleting (author or admin). Group channels require membership.
Api/ProfileController.php The logged-in user's own profile: view, update (no group/active/tier changes here), and subscription tier switching (pilot / club / school, no payment flow, switching is free).
Api/UsersController.php Public pilot profile by username: public fields only plus the pilot's groups and meets.
Api/ActivityController.php The landing page activity feed: latest meet creations, meet joins, and global chat messages, merged and capped at 10.
Api/Admin/UsersController.php Admin user CRUD with server-side paging, search, filtering, and sorting. Guarded by the adminapi filter plus per-action permission checks.

Filters (app/Filters/)

  • ApiAuthFilter (alias apiauth): rejects requests without a logged-in session with 401 { "error": ... }. Applied to all mutating domain routes, chat, and the own-profile group.
  • AdminApiFilter (alias adminapi): additionally requires the admin.access permission; guards everything under /api/admin/*. Each admin action then checks its concrete permission (users.view / users.create / users.edit / users.delete).

The backend check is authoritative; the frontend's RequireAuth / RequireAdmin wrappers are UX only. See Authentication and Roles.

Libraries (app/Libraries/)

  • OpenMeteo.php: thin wrapper around Open-Meteo's geocoding (v1/search) and forecast APIs, shared by the Weather controller and the meets weather endpoint. Failures return null and are logged, never thrown to the client as a 500. Details in Weather Integration.
  • Participant.php: builds the { id, username, name } participant shape used across meets, groups, chat, and the activity feed (name = "Vorname Nachname", falling back to the username).

Conventions

  • Response contract: success is 2xx with the documented body; validation failures are 422 { errors: { field: message } }; auth errors 401/403 { error }; missing resources 404; conflicts (meet full, duplicate join, missing coordinates) 409.
  • Derived, never stored: participant counts, member counts, and meet status are computed from meet_participants / group_members at read time.
  • CSRF is global (session mode). Mutating requests must send the X-CSRF-TOKEN header; the SPA handles this automatically.
  • Dates are YYYY-MM-DD, times HH:MM, timestamps ISO-8601.
  • Configuration that isn't committed lives in .env (copied from the env template).

Notable behaviors

  • Meet create/edit geocoding: when a meet is created without coordinates, the backend geocodes the spot (fallback: region) via Open-Meteo. An unresolvable spot leaves coordinates null, and the weather panel then shows a friendly "no forecast" note rather than an error. On edit, stored coordinates are kept when both spot and region are unchanged; otherwise re-geocoded.
  • Edit rules: maxParticipants may not drop below the current participant count; a past date is only rejected when the date actually changed.
  • Chat channels: messages.group_id NULL = the global "All pilots" channel; a non-null group_id is that group's channel and requires membership.
  • Admin self-protection: an admin cannot delete, deactivate, or de-admin their own account.

Clone this wiki locally