Architecture overview
Frontend
The frontend is a React 19 single-page application built with Vite 6. All routing is client-side, using React Router v7. Components are loaded lazily withReact.lazy and Suspense to minimize initial bundle size.
The application is organized into three layout regions, each with its own layout component and route guard:
Route structure
- Public routes
- Client routes
- Admin routes
Public routes are rendered inside
PublicLayout and require no authentication. They are accessible to all visitors.| Path | Page |
|---|---|
/ | Home |
/login | Login |
/register | Register |
/forgot-password | Forgot password |
/quienes-somos/informacion-institucional | Institutional information |
/quienes-somos/historia | Park history |
/quienes-somos/equipo | Team |
/quienes-somos/transparencia-institucional | Institutional transparency |
/exhibiciones-y-servicios/exhibiciones | Exhibits |
/exhibiciones-y-servicios/servicios-educativos | Educational services |
/exhibiciones-y-servicios/visitas-guiadas | Guided visits |
/investigacion-y-conservacion/acuicultura-y-biotecnologia-marina | Aquaculture & marine biotechnology |
/investigacion-y-conservacion/centro-de-rescate-y-rehabilitacion | Rescue & rehabilitation center |
/investigacion-y-conservacion/investigacion | Research |
/investigacion-y-conservacion/proyectos | Projects |
/apoyo/voluntariado | Volunteering |
/apoyo/donaciones | Donations |
/purchase-form/ticketera | Ticket purchase |
/terminos-y-condiciones/terminos | Terms and conditions |
/privacidad/politica-de-privicidad | Privacy policy |
All page components are code-split using
React.lazy. A shared <Loading /> fallback component is shown during lazy load via <Suspense>.Backend
The backend is a Django 5.2 application exposing a REST API at/api/. Each functional domain is its own Django app under the api/ package, with its own models.py, serializers.py, views.py, and urls.py.
API modules
Animals
/api/animals/ — Individual animal records: name, species, habitat assignment, photos, and section placement within the park.Species
/api/species/ — Scientific classification of marine species, linked to animals and conservation status records.Habitats
/api/habitats/ — Habitat definitions (e.g., reef, open ocean, mangrove) assigned to species and animal records.Conservation status
/api/conservation_status/ — IUCN-style conservation categories (e.g., Endangered, Vulnerable) linked to species.Exhibits
/api/exhibiciones/ — Park exhibit definitions with images, descriptions, and section assignments.Educational services
/api/servicios_educativos/ — Educational service offerings available to the public and registered users.Educational programs
/api/programas_educativos/ — Structured programs (workshops, tours) with scheduling and registration.Tickets
/api/tickets/ — Ticket types and pricing configuration for park entry.Visits
/api/visits/ — Individual visit records linked to purchase orders, including date, visitor count, and QR codes.Purchase orders
/api/purchase_orders/ — Purchase order records tracking buyer information, total, and payment status.Ticket purchases
/api/tickets_purchase_orders/ — Junction table linking individual ticket line items to their parent purchase orders.Payments
/api/payments/ — Payment records linked to purchase orders, capturing PayPal transaction IDs and amounts.Sections
/api/sections/ — Physical sections of the park, used to organize exhibits and animal placements.Provinces
/api/provinces/ — Costa Rican province data used in visitor registration and reporting.Documents
/api/documents/ — Institutional documents (reports, transparency files) accessible through the public portal.Audit log
/api/audit_log/ — Admin-only log of all significant API actions for compliance and debugging.Module structure
Every backend module follows a consistent file layout:Module layout
Authentication
The platform uses JWT (JSON Web Tokens) viadjangorestframework-simplejwt. Token lifetimes are:
- Access token: 5 minutes
- Refresh token: 1 day, with automatic rotation enabled
Frontend stores tokens
The React app stores tokens in cookies via
js-cookie and attaches the access token to every subsequent Axios request as an Authorization: Bearer header.Token refresh
When the access token expires, the frontend posts the refresh token to
/api/auth/token/refresh/ to obtain a new access token without re-authentication.Role-based access
| Role | Access |
|---|---|
| Public | All public routes, anonymous ticket purchase, donations, volunteering |
| Client | All public routes + /Client_Dashboard + /profile |
| Admin | All routes + /admin/dashboard, audit logs, full CRUD on all resources |
Database
The backend supports two database engines controlled by environment variables:- SQLite (development)
- MySQL (production)
SQLite is the default configuration for local development. No external service is required.
Media storage
User-uploaded and system-generated files are stored in Supabase Storage, replacing Django’s default localmedia/ directory in production. Supabase provides a global CDN, Row Level Security policies, and an S3-compatible REST API.
The following buckets are used:
| Bucket path | Contents |
|---|---|
media/species/ | Species and animal images |
media/exhibitions/ | Exhibit photos |
media/profile_pics/ | User profile pictures |
media/programas/ | Educational program files |
media/servicios-educativos/ | Educational service files |
media/documentos/ | Institutional documents |
media/qr_codes/ | Generated QR code images |
CORS configuration
The backend usesdjango-cors-headers to allow cross-origin requests from the frontend development server. The following origins are allowed by default:
