- Remove adminer references (service was removed) - Remove mermaid diagrams (ASCII only) - Remove hardcoded credentials (use env var references) - Update all Docker references to 4-container setup (app, postgres, web, pgadmin) - Document env-based admin credentials (ADMIN_EMAIL/ADMIN_PASSWORD) - Document parameterized queries (SQL injection fixed) - Document FCM topic routing by stationtype+level - Document siren stationtype=3 fix in sidesdecode.py - Document idempotent seeder (firstOrCreate) - Document reverse proxy setup in deployment guide - Remove Makefile references (Docker Compose only)
261 lines
10 KiB
Markdown
261 lines
10 KiB
Markdown
# 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 |
|
|
|--------------|
|
|
```
|
|
|
|
**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.
|