# Architecture ## Technology Stack | Layer | Technology | Version | |-------|-----------|---------| | Backend | Laravel (PHP) | 12.x | | PHP | PHP-FPM | 8.2 | | Frontend | Blade Templates + Alpine.js | 3.x | | CSS | Tailwind CSS + Bootstrap 5.3 | 3.x / 5.3 | | Charts | Custom JS (Chart.js via `graph.js`) | — | | Maps | Leaflet.js | 1.9.4 | | Build Tool | Vite | 7.x | | Database | PostgreSQL | 15 | | Cache/Session | Database (via Laravel) | — | | Queue | Database (via Laravel) | — | | Containerization | Docker + Docker Compose | 3.9 | | Web Server | Nginx | stable-alpine | | PDF Generation | barryvdh/laravel-dompdf | 3.1 | | Excel Export | maatwebsite/excel | 3.1 | | Push Notifications | Firebase Cloud Messaging (FCM) via Google Auth | 1.49 | | Date Picker | Flatpickr | 4.6 | | Auth Scaffold | Laravel Breeze | 2.3 | | Data Pipeline | Python (psycopg2, ftplib) | 3.x | ## Container Architecture The application runs in 5 Docker containers defined in `docker-compose.yml`: ``` ┌──────────────────────────────────────────────────────────┐ │ Docker Network: tckdev_net │ │ │ │ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ │ │ web │ │ app │ │ postgres │ │ │ │ (nginx) │──>│ (PHP-FPM│──>│ (DB) │ │ │ │ :80 │ │ :9000) │ │ :5432 │ │ │ └─────────┘ └─────────┘ └──────────┘ │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ pgAdmin │ │ adminer │ │ │ │ :5050 │ │ :6060 │ (DB management tools) │ │ └─────────┘ └─────────┘ │ └──────────────────────────────────────────────────────────┘ ``` ### Container Details | Container | Image | Port | Purpose | |-----------|-------|------|---------| | `tckdev-app` | Custom (PHP 8.2-FPM + Composer 2.3 + Node.js) | 9000 (internal) | Laravel application | | `tckdev-web` | nginx:stable-alpine | 80 | Reverse proxy & static files | | `tckdev-db` | postgres:15 | 5432 | PostgreSQL database | | `tckdev-pgAdmin` | dpage/pgadmin4 | 5050 | Database management UI | | `tckdev-adminer` | adminer | 6060 | Lightweight DB management UI | ## Request Flow ``` Browser --> Nginx (:80) --> PHP-FPM (:9000) --> Laravel Router | ┌─────────────┼─────────────────┐ v v v Web Routes API Routes Auth Routes (web.php) (api.php) (auth.php) | | | v v v Controllers Api/Controllers Breeze Controllers | | v v DB::raw() SQL DB::raw() SQL | | v v PostgreSQL PostgreSQL | v Blade Views --> HTML Response ``` ## Middleware Stack | Middleware | Route Group | Purpose | |-----------|------------|---------| | `auth` | Web (protected) | Require authenticated session | | `admin` | Admin routes | Require `access_level = 1` | | `LocalizationMiddleware` | All web routes | Set locale from session (`en`/`bm`) | | `guest` | Auth routes (login/register) | Redirect away if already authenticated | ## Database Design ### Core Tables ``` ┌─────────────┐ ┌──────────────┐ │ station │ │ users │ │─────────────│ │──────────────│ │ stationid* │ │ id* │ │ name │ │ name │ │ district │ │ email │ │ lng, lat │ │ password │ │ mainriverbasin│ │ access_level │ │ subriverbasin│ │ is_blocked │ │ rainfall │ │ login_attempts│ │ waterlevel │ └──────────────┘ │ siren │ │ cctv_link │ └──────┬──────┘ │ │ (stationid FK via application logic, not DB constraint) │ ┌────┼────────────┬──────────────┐ v v v v ┌──────────┐ ┌────────────┐ ┌───────────┐ │ rainfall │ │ waterlevel │ │ siren │ │──────────│ │────────────│ │───────────│ │ id* │ │ id* │ │ id* │ │ stationid│ │ stationid │ │ stationid │ │ timestamp│ │ datetime │ │ stationtype│ │ anncum │ │ waterlevel │ │ active_time│ │ daily │ │ alert │ │ level │ │ hourly │ │ warning │ └───────────┘ │ currentrf│ │ danger │ │ battery │ └────────────┘ └──────────┘ │ v ┌──────────────┐ │ notification │ │──────────────│ │ id* │ │ stationid │ │ timestamp │ │ stationtype │ (1=rainfall, 2=waterlevel, 3=siren) │ level │ (Normal, Alert, Warning, Danger) │ active_time │ └──────────────┘ ``` ### Station Types The `station` table uses boolean flags to indicate which monitoring types each station supports: - `rainfall = 1` — Station has rainfall monitoring - `waterlevel = 1` — Station has water level monitoring - `siren = 1` — Station has a warning siren ### Foreign Key Note There are **no database-level foreign keys** between `station` and the data tables. Relationships are maintained at the application level via `stationid` column matching. ### Laravel Standard Tables The application also uses standard Laravel tables: - `users` — Authentication and authorization - `password_reset_tokens` — Password reset flow - `sessions` — Database-backed sessions - `cache`, `cache_locks` — Database cache store - `jobs`, `job_batches`, `failed_jobs` — Database queue ## Authentication & Authorization - **Web auth**: Laravel Breeze (session-based, Blade views) - **API auth**: Custom token-less login via `Api\AuthController` (returns user info, no token generation) - **Admin access**: Controlled by `AdminMiddleware` checking `access_level === 1` - **User blocking**: Users can be blocked via `is_blocked` flag (though no middleware enforces it currently)