# API Reference

All API endpoints are defined in `src/routes/api.php` and served under the `/api` prefix. There is no authentication middleware on any API route -- all endpoints are publicly accessible.

---

## Authentication

### POST `/api/login`

Validates user credentials against the `users` table. No token or session is created; this endpoint only confirms the username and password are correct.

**Request Body:**

```json
{
    "username": "admin",
    "password": "<your_password>"
}
```

**Success Response (200):**

```json
{
    "error": false,
    "id": 1,
    "username": "admin",
    "email": "admin@example.com",
    "acc_lvl": 1
}
```

**Error Response (200):**

```json
{
    "error": true,
    "message": "Wrong Password/Username"
}
```

**Implementation details:**

- Uses a parameterized query (`DB::select` with `?` placeholder) to look up the user by `name`
- Password verification uses `Hash::check()` against the bcrypt hash stored in the `password` column
- Returns generic "Wrong Password/Username" for both unknown user and wrong password (no user enumeration)

---

## Station Data

### GET `/api/station/current`

Returns all stations that have valid coordinates (`lat`/`lng` not null) joined with their latest rainfall reading, water level reading, and siren status. Each row is the most recent record from the `rainfall`, `waterlevel`, and `siren` tables per station.

**Response fields per station:**

| Field | Source | Description |
|-------|--------|-------------|
| `stationid` | `station` | Station identifier (e.g. `KBLG0026`) |
| `name` | `station` | Station name |
| `district` | `station` | District name |
| `lng`, `lat` | `station` | GPS coordinates |
| `rainfall_value` | `rainfall.hourly` | Latest hourly rainfall |
| `rainfall_time` | `rainfall.timestamp` | Timestamp of latest rainfall reading |
| `waterlevel_value` | `waterlevel.waterlevel` | Latest water level |
| `waterlevel_time` | `waterlevel.datetime` | Timestamp of latest water level reading |
| `siren_level` | `siren.level` | Latest siren level (`"N"` = Normal) |
| `siren_time` | `siren.active_time` | Timestamp of latest siren event |

Stations with no data across all three types (rainfall, waterlevel, siren) are excluded.

### GET `/api/station/rainfall`

Returns the latest rainfall record per station. Only includes stations where the `station.rainfall` flag is set and a matching `rainfall` record exists. Returns all columns from both `station` and `rainfall` tables.

### GET `/api/station/waterlevel`

Returns the latest water level record per station. Only includes stations where the `station.waterlevel` flag is set and a matching `waterlevel` record exists. Returns all columns from both `station` and `waterlevel` tables.

### GET `/api/station/notification`

Returns today's latest notification per station. Joins `station` with `notification` where `notification.timestamp::date = CURRENT_DATE`, returning only the most recent notification per station for the current day. Ordered by timestamp descending.

### GET `/api/station/history`

Returns notification history for the last 3 days. For each station, returns the single latest notification per day (one row per station per day). Ordered by timestamp descending.

### GET `/api/station/siren`

Returns current siren status for siren-equipped stations. Joins `station` with `siren` where `station.siren = 1`, returning only the most recent siren event per station within the last 3 days. Ordered by `active_time` descending.

### GET `/api/station/siren/history`

Returns siren history for the last 3 days, excluding Normal-level entries (`siren.level != "N"`). Only includes stations with `station.siren = 1`. Ordered by `active_time` descending.

---

## Alert (Push Notification)

### POST `/api/alert`

Sends an FCM push notification to a topic. Called by `sidesdecode.py` when sensor thresholds are triggered.

**Request Body:**

```json
{
    "stationid": "KBLG0026",
    "level": "Warning",
    "stationtype": 1
}
```

**Parameters:**

| Field | Type | Description |
|-------|------|-------------|
| `stationid` | string | Station identifier |
| `level` | string | Alert level (e.g. `"Alert"`, `"Warning"`, `"Danger"`) |
| `stationtype` | integer | Sensor type: `1` = Rainfall, `2` = Water Level, `3` = Siren |

**FCM topic routing:**

The controller maps `stationtype` and `level` to an FCM topic:

| stationtype | level | FCM Topic |
|-------------|-------|-----------|
| 1 (Rainfall) | any non-"Danger" | `FCM_TOPIC_RAINFALL_WARNING` |
| 1 (Rainfall) | "Danger" | `FCM_TOPIC_RAINFALL_DANGER` |
| 2 (Water Level) | any non-"Danger" | `FCM_TOPIC_WATERLEVEL_ALERT` |
| 2 (Water Level) | "Danger" | `FCM_TOPIC_WATERLEVEL_DANGER` |
| 3 (Siren) | any | `FCM_TOPIC_RAINFALL_WARNING` |

The notification title is formatted as `"{Type} {Level} Alert"` and the body as `"{stationid} : {Type} Have Triggered {Level}"`.

**Response:**

```json
{
    "status": 200
}
```

The `status` value is the HTTP status code returned by the FCM v1 API (`200` = delivered successfully).

**Implementation:**

- `FcmService` loads Firebase credentials from the path in `FIREBASE_CREDENTIALS` and uses `Google\Auth\Credentials\ServiceAccountCredentials` to obtain an OAuth2 access token
- Sends to `https://fcm.googleapis.com/v1/projects/{FIREBASE_PROJECT_ID}/messages:send`
- Android priority is set to `"high"`

**Security note:** This endpoint has no authentication. Anyone with network access can trigger push notifications.

---

## Public Web Routes

These are defined in `src/routes/web.php` (not under `/api`) but return JSON or perform redirects.

### GET `/stations`

Returns all stations with coordinates as JSON. Used by the public-facing map. No authentication required.

### GET `/dashboard`

Returns the same combined station data as `/api/station/current`. Served at the root URL `/` as well. No authentication required.

### GET `/locale/{locale}`

Switches the application language. Accepts `en` or `bm`. Redirects back to the previous page.
