# Database SIDES uses **PostgreSQL 16** running in a Docker container. Laravel connects via `pgsql` driver using the Docker service name as the host. ## Connection Details | Setting | Value | Source | |-----------------|------------------------------|---------------------------| | DB_CONNECTION | `pgsql` | `src/.env` | | DB_HOST | `postgres` (Docker service) | `src/.env` | | DB_PORT | `5432` | `src/.env` | | DB_DATABASE | `${POSTGRES_DB}` | `src/.env` | | DB_USERNAME | `${POSTGRES_USER}` | `src/.env` | | DB_PASSWORD | `${POSTGRES_PASSWORD}` | `src/.env` | Default database name: **sides_db** (set by `POSTGRES_DB` in the root `.env`). The container (`tckdev-db`) uses a named Docker volume **pgdata** for persistent storage. Data survives container restarts and rebuilds. ## Entity-Relationship Diagram ``` station (PK: stationid) |--------------| | stationid |---+----------------+----------------+ | name | | | | | district | | | | | lng, lat | | | | | mainriverbasin| | | | | subriverbasin| | | | | rainfall | | | | | waterlevel | | | | | siren | | | | | cctv_link | | | | |--------------| | | | v v v rainfall waterlevel siren (PK: id) (PK: id) (PK: id) |--------------| |--------------| |--------------| | id | | id | | id | | stationid | | stationid | | stationid | | timestamp | | datetime | | stationtype | | anncum | | waterlevel | | active_time | | daily | | alert | | level | | hourly | | warning | |--------------| | currentrf | | danger | | battery | |--------------| |--------------| | | v v notification (PK: id) |--------------| | id | | stationid | | timestamp | | stationtype | | level | | active_time | |--------------| ``` ```mermaid erDiagram station { int stationid string name string district float lng float lat string mainriverbasin string subriverbasin float rainfall float waterlevel boolean siren string cctv_link } rainfall { int id int stationid datetime timestamp float anncum float daily float hourly float currentrf float battery } waterlevel { int id int stationid datetime datetime float waterlevel string alert string warning string danger } siren { int id int stationid string stationtype datetime active_time string level } notification { int id int stationid datetime timestamp string stationtype string level datetime active_time } station ||--o{ rainfall : "has" station ||--o{ waterlevel : "has" station ||--o{ siren : "has" station ||--o{ notification : "has" ``` **Note:** There are no database-level foreign keys between `station` and the data tables. Relationships are enforced at the application level by matching `stationid` values. ## Application Tables ### station Primary reference table for all monitoring stations. | Column | Type | Nullable | Notes | |-----------------|---------------|----------|----------------------| | stationid | string (PK) | no | External station ID | | name | string | no | Station display name | | district | string | no | | | lng | float | no | Longitude | | lat | float | no | Latitude | | mainriverbasin | string | no | | | subriverbasin | string | no | | | rainfall | integer | no | Flag/counter | | waterlevel | integer | no | Flag/counter | | siren | integer | yes | Added by migration | | cctv_link | string | yes | Added by migration | Migration: `2025_11_06_071853_create_station_table.php` Alter migrations: `2025_11_08_004548`, `2025_11_25_113158` ### rainfall Rainfall sensor readings per station. | Column | Type | Nullable | |------------|-------------|----------| | id | bigint (PK) | no | | stationid | string | no | | timestamp | timestamp | no | | anncum | double | no | | daily | double | no | | hourly | double | no | | currentrf | double | no | | battery | double | no | Migration: `2025_11_06_072709_create_rainfall__table.php` ### waterlevel Water level sensor readings per station. | Column | Type | Nullable | |-------------|-------------|----------| | id | bigint (PK) | no | | stationid | string | no | | datetime | timestamp | no | | waterlevel | double | no | | alert | double | no | | warning | double | no | | danger | double | no | Migration: `2025_11_06_072738_create_waterlevel_table.php` ### siren Siren activation records. | Column | Type | Nullable | |--------------|-------------|----------| | id | bigint (PK) | no | | stationid | string | no | | stationtype | string | no | | active_time | timestamp | no | | level | string | no | Migration: `2025_11_07_031601_create_siren__table.php` The `stationtype` column is always `3` for siren inserts (see stationtype values below). ### notification Notification log for alerts across all station types. | Column | Type | Nullable | |--------------|-------------|----------| | id | bigint (PK) | no | | stationid | string | no | | timestamp | timestamp | no | | stationtype | integer | no | | level | string | no | | active_time | timestamp | yes | Migration: `2025_11_07_024940_create_notification_table.php` ### users Application users with access control and blocking support. | Column | Type | Nullable | Default | Notes | |--------------------|-------------|----------|---------|-------------------| | id | bigint (PK) | no | auto | | | name | string | no | | | | email | string | no | | unique | | email_verified_at | timestamp | yes | | | | password | string | no | | bcrypt hashed | | access_level | integer | no | 2 | 1=admin, 2=user | | is_blocked | boolean | no | false | | | login_attempts | integer | no | 0 | | | remember_token | string | yes | | | | created_at | timestamp | yes | | | | updated_at | timestamp | yes | | | Base: `0001_01_01_000000_create_users_table.php` Alter: `2025_11_07_063825`, `2025_11_07_065418` ## stationtype Values The `stationtype` field is used in the `notification` and `siren` tables to identify the source station type. | Value | Type | |-------|-------------| | 1 | Rainfall | | 2 | Water level | | 3 | Siren | ## Laravel Standard Tables These tables are created by Laravel's built-in migrations and support framework features (sessions, caching, queue jobs). | Table | Purpose | |-----------------------|-------------------------------| | password_reset_tokens | Password reset tokens | | sessions | Database-backed sessions | | cache | Application cache store | | cache_locks | Atomic cache locks | | jobs | Queue job queue | | job_batches | Batched job tracking | | failed_jobs | Failed job records | | migrations | Migration tracking | ## Default Admin User The `DatabaseSeeder` creates a default admin user on first run using `firstOrCreate`, making it idempotent -- safe to re-run without duplicates. ```php User::firstOrCreate( ['email' => env('ADMIN_EMAIL', 'admin@example.com')], [ 'name' => 'Admin', 'password' => Hash::make(env('ADMIN_PASSWORD', str()->random(32))), 'access_level' => 1, ] ); ``` - **Email**: set by `ADMIN_EMAIL` env var (default: `admin@example.com`) - **Password**: set by `ADMIN_PASSWORD` env var (default: random 32-char string) - **access_level**: `1` (admin) The seeder is triggered automatically when `RUN_SEEDER=true` in the Docker environment (see `docker/entrypoint.sh`). The migration `2025_12_11_124201_add_default_user_to_users_table.php` exists but is a no-op (empty `up()` and `down()`) -- admin creation is handled exclusively by the seeder. ## Seeding and Migrations Migrations and seeding run automatically at container startup via `docker/entrypoint.sh`: ```bash # Runs when RUN_MIGRATIONS=true php artisan migrate --force # Runs when RUN_SEEDER=true php artisan db:seed --force ``` Both `RUN_MIGRATIONS` and `RUN_SEEDER` default to `true` in `docker-compose.yml`. ## Design Notes **No foreign keys.** The `stationid` column in `rainfall`, `waterlevel`, `siren`, and `notification` references `station.stationid` at the application level only. There are no database-level `REFERENCES` constraints. This means orphaned data rows can exist if a station is deleted without cleaning associated records. **No additional indexes.** Beyond primary keys and Laravel's default indexes (on `sessions.user_id`, `sessions.last_activity`, `jobs.queue`), no custom indexes exist on the data tables. This is a potential performance concern as the `rainfall`, `waterlevel`, and `notification` tables grow -- queries filtering by `stationid` or ordering by timestamp will perform full table scans.