# 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)
