Files
api-btekno/API_ENDPOINTS.md
mwpn 820a0600ed docs: update API_ENDPOINTS.md dengan endpoint baru dan field camera
- Tambah dokumentasi GET /locations/{code} (detail location)
- Tambah dokumentasi GET /gates/{location_code}/{gate_code} (detail gate)
- Tambah dokumentasi GET /tariffs (list tariffs)
- Tambah dokumentasi GET /tariffs/{location_code}/{gate_code}/{category} (detail tariff)
- Tambah dokumentasi GET /audit-logs (audit trail)
- Tambah dokumentasi GET /entry-events (raw entry events)
- Tambah dokumentasi GET /realtime/events (realtime events list)
- Update response examples untuk include field camera di gates
- Tambah note tentang format camera (HLS, RTSP, HTTP, Camera ID)
- Update daftar isi dengan struktur endpoint baru
2025-12-18 06:42:27 +07:00

1217 lines
21 KiB
Markdown

# API Endpoints Documentation
Dokumentasi lengkap semua endpoint yang tersedia di API Btekno.
**Base URL**: `https://api.btekno.cloud`
---
## 📋 Daftar Isi
- [Public Endpoints](#public-endpoints)
- [Authentication](#authentication)
- [Retribusi - Ingest](#retribusi---ingest)
- [Retribusi - Frontend CRUD](#retribusi---frontend-crud)
- [Locations](#locations)
- [Gates](#gates)
- [Tariffs](#tariffs)
- [Streams](#streams)
- [Audit Logs](#audit-logs)
- [Entry Events](#entry-events)
- [Retribusi - Summary](#retribusi---summary)
- [Retribusi - Dashboard](#retribusi---dashboard)
- [Retribusi - Realtime](#retribusi---realtime)
---
## Public Endpoints
### Health Check
**GET** `/health`
Health check endpoint untuk monitoring.
**Response:**
```json
{
"status": "ok",
"time": 1703123456
}
```
---
### Documentation
**GET** `/`
Redirect ke `/docs` (Swagger UI).
**GET** `/docs`
Swagger UI documentation (public access).
**GET** `/docs/openapi.json`
OpenAPI 3.0 specification JSON.
---
## Authentication
### Login
**POST** `/auth/v1/login`
Login untuk mendapatkan JWT token.
**Request Body:**
```json
{
"username": "admin",
"password": "password123"
}
```
**Response (200):**
```json
{
"success": true,
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"expires_in": 3600,
"user": {
"id": 1,
"username": "admin",
"role": "admin"
}
},
"timestamp": 1703123456
}
```
**Error Responses:**
- `401` - Invalid credentials
- `403` - User inactive
- `422` - Validation error
**Authentication:** None (public endpoint)
---
## Retribusi - Ingest
### Ingest Event
**POST** `/retribusi/v1/ingest`
Endpoint untuk mesin/IoT mengirim event data.
**Headers:**
```
X-API-KEY: your-api-key-here
Content-Type: application/json
```
**Request Body:**
```json
{
"timestamp": 1703123456,
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor"
}
```
**Response (200):**
```json
{
"success": true,
"data": {
"stored": true
},
"timestamp": 1703123456
}
```
**Error Responses:**
- `401` - Invalid API key
- `404` - Location/Gate/Tariff not found
- `422` - Validation error
**Authentication:** `X-API-KEY` header
**Category Values:**
- `person_walk`
- `motor`
- `car`
---
## Retribusi - Frontend CRUD
Semua endpoint di bawah ini memerlukan **JWT Authentication**.
**Headers:**
```
Authorization: Bearer <jwt-token>
Content-Type: application/json
```
### Locations
#### List Locations
**GET** `/retribusi/v1/frontend/locations`
Get list of locations dengan pagination.
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `location_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": [
{
"code": "kerkof_01",
"name": "Kerkof 01",
"type": "kerkof",
"is_active": 1
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 10,
"pages": 1
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
#### Get Location Detail
**GET** `/retribusi/v1/frontend/locations/{code}`
Get detail location berdasarkan code.
**Response (200):**
```json
{
"success": true,
"data": {
"code": "kerkof_01",
"name": "Kerkof 01",
"type": "kerkof",
"is_active": 1
},
"timestamp": 1703123456
}
```
**Error Responses:**
- `404` - Location not found
- `422` - Invalid location code
**Access:** Viewer, Operator, Admin
---
#### Create Location
**POST** `/retribusi/v1/frontend/locations`
Create new location.
**Request Body:**
```json
{
"code": "kerkof_02",
"name": "Kerkof 02",
"type": "kerkof",
"is_active": 1
}
```
**Response (201):**
```json
{
"success": true,
"data": {
"code": "kerkof_02",
"name": "Kerkof 02",
"type": "kerkof",
"is_active": 1
},
"timestamp": 1703123456
}
```
**Access:** Operator, Admin
---
#### Update Location
**PUT** `/retribusi/v1/frontend/locations/{code}`
Update location (code tidak bisa diubah).
**Request Body:**
```json
{
"name": "Kerkof 02 Updated",
"type": "kerkof",
"is_active": 1
}
```
**Response (200):**
```json
{
"success": true,
"data": {
"code": "kerkof_02",
"name": "Kerkof 02 Updated",
"type": "kerkof",
"is_active": 1
},
"timestamp": 1703123456
}
```
**Access:** Operator, Admin
---
#### Delete Location
**DELETE** `/retribusi/v1/frontend/locations/{code}`
Soft delete location (set `is_active = 0`).
**Response (200):**
```json
{
"success": true,
"data": {
"deleted": true
},
"timestamp": 1703123456
}
```
**Access:** Admin only
---
### Gates
#### List Gates
**GET** `/retribusi/v1/frontend/gates`
Get list of gates dengan pagination.
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `location_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": [
{
"location_code": "kerkof_01",
"gate_code": "gate01",
"name": "Gate 01",
"direction": "in",
"camera": "https://example.com/stream.m3u8",
"is_active": 1
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"pages": 1
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
#### Get Gate Detail
**GET** `/retribusi/v1/frontend/gates/{location_code}/{gate_code}`
Get detail gate berdasarkan location_code dan gate_code.
**Response (200):**
```json
{
"success": true,
"data": {
"location_code": "kerkof_01",
"gate_code": "gate01",
"name": "Gate 01",
"direction": "in",
"camera": "https://example.com/stream.m3u8",
"is_active": 1
},
"timestamp": 1703123456
}
```
**Error Responses:**
- `404` - Gate not found
- `422` - Invalid location_code or gate_code
**Access:** Viewer, Operator, Admin
---
#### Create Gate
**POST** `/retribusi/v1/frontend/gates`
Create new gate.
**Request Body:**
```json
{
"location_code": "kerkof_01",
"gate_code": "gate02",
"name": "Gate 02",
"direction": "out",
"camera": "rtsp://192.168.1.100:554/stream1",
"is_active": 1
}
```
**Note:** Field `camera` optional, bisa diisi dengan:
- HLS URL: `https://example.com/stream.m3u8`
- RTSP URL: `rtsp://192.168.1.100:554/stream1`
- HTTP URL: `http://192.168.1.100:8080/camera1`
- Camera ID: `camera_001`
**Response (201):**
```json
{
"success": true,
"data": {
"location_code": "kerkof_01",
"gate_code": "gate02",
"name": "Gate 02",
"direction": "out",
"camera": "rtsp://192.168.1.100:554/stream1",
"is_active": 1
},
"timestamp": 1703123456
}
```
**Access:** Operator, Admin
---
#### Update Gate
**PUT** `/retribusi/v1/frontend/gates/{location_code}/{gate_code}`
Update gate (location_code dan gate_code tidak bisa diubah).
**Request Body:**
```json
{
"name": "Gate 02 Updated",
"direction": "out",
"camera": "http://192.168.1.100:8080/hls/stream.m3u8",
"is_active": 1
}
```
**Note:** Field `camera` optional, bisa diisi dengan:
- HLS URL: `https://example.com/stream.m3u8`
- RTSP URL: `rtsp://192.168.1.100:554/stream1`
- HTTP URL: `http://192.168.1.100:8080/camera1`
- Camera ID: `camera_001`
**Response (200):**
```json
{
"success": true,
"data": {
"location_code": "kerkof_01",
"gate_code": "gate02",
"name": "Gate 02 Updated",
"direction": "out",
"camera": "http://192.168.1.100:8080/hls/stream.m3u8",
"is_active": 1
},
"timestamp": 1703123456
}
```
**Access:** Operator, Admin
---
#### Delete Gate
**DELETE** `/retribusi/v1/frontend/gates/{location_code}/{gate_code}`
Soft delete gate (set `is_active = 0`).
**Response (200):**
```json
{
"success": true,
"data": {
"deleted": true
},
"timestamp": 1703123456
}
```
**Access:** Admin only
---
### Tariffs
#### List Tariffs
**GET** `/retribusi/v1/frontend/tariffs`
Get list of tariffs dengan pagination.
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": [
{
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"price": 5000,
"location_name": "Kerkof 01",
"gate_name": "Gate 01"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 10,
"pages": 1
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
#### Get Tariff Detail
**GET** `/retribusi/v1/frontend/tariffs/{location_code}/{gate_code}/{category}`
Get detail tariff berdasarkan location_code, gate_code, dan category.
**Response (200):**
```json
{
"success": true,
"data": {
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"price": 5000
},
"timestamp": 1703123456
}
```
**Error Responses:**
- `404` - Tariff not found
- `422` - Invalid location_code, gate_code, or category
**Access:** Viewer, Operator, Admin
---
#### Create Tariff
**POST** `/retribusi/v1/frontend/tariffs`
Create new tariff.
**Request Body:**
```json
{
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"price": 5000
}
```
**Response (201):**
```json
{
"success": true,
"data": {
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"price": 5000
},
"timestamp": 1703123456
}
```
**Access:** Operator, Admin
---
#### Update Tariff
**PUT** `/retribusi/v1/frontend/tariffs/{location_code}/{gate_code}/{category}`
Update tariff price (location_code, gate_code, category tidak bisa diubah).
**Request Body:**
```json
{
"price": 6000
}
```
**Response (200):**
```json
{
"success": true,
"data": {
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"price": 6000
},
"timestamp": 1703123456
}
```
**Access:** Operator, Admin
---
#### Delete Tariff
**DELETE** `/retribusi/v1/frontend/tariffs/{location_code}/{gate_code}/{category}`
Hard delete tariff (permanent delete).
**Response (200):**
```json
{
"success": true,
"data": {
"deleted": true
},
"timestamp": 1703123456
}
```
**Access:** Admin only
---
### Streams
#### List Streams
**GET** `/retribusi/v1/frontend/streams`
Get list of streams (alias untuk gates).
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `location_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": [
{
"location_code": "kerkof_01",
"gate_code": "gate01",
"name": "Gate 01",
"direction": "in",
"is_active": 1
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"pages": 1
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
### Audit Logs
#### List Audit Logs
**GET** `/retribusi/v1/frontend/audit-logs`
Get audit trail history dengan pagination dan filter.
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `entity` (optional, filter: `locations|gates|tariffs`)
- `action` (optional, filter: `create|update|delete`)
- `entity_key` (optional, filter by specific entity key)
- `start_date` (optional, format: YYYY-MM-DD)
- `end_date` (optional, format: YYYY-MM-DD)
**Response (200):**
```json
{
"success": true,
"data": [
{
"id": 123,
"actor_user_id": 1,
"actor_username": "admin",
"actor_role": "admin",
"action": "update",
"entity": "locations",
"entity_key": "kerkof_01",
"before_json": {
"code": "kerkof_01",
"name": "Kerkof 01",
"type": "kerkof",
"is_active": 1
},
"after_json": {
"code": "kerkof_01",
"name": "Kerkof 01 Updated",
"type": "kerkof",
"is_active": 1
},
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"created_at": "2025-01-17 10:30:00"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 150,
"pages": 8
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
### Entry Events
#### List Entry Events
**GET** `/retribusi/v1/frontend/entry-events`
Get list of raw entry events (data mentah dari mesin) dengan pagination dan filter.
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
- `category` (optional, filter)
- `start_date` (optional, format: YYYY-MM-DD)
- `end_date` (optional, format: YYYY-MM-DD)
**Response (200):**
```json
{
"success": true,
"data": [
{
"id": 12345,
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"event_time": "2025-01-17 10:30:00",
"source_ip": "192.168.1.100",
"created_at": "2025-01-17 10:30:01"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 5000,
"pages": 250
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
**Note:** Endpoint ini berguna untuk debugging dan admin untuk melihat raw event data.
---
## Retribusi - Summary
Semua endpoint di bawah ini memerlukan **JWT Authentication**.
### Daily Summary
#### Trigger Daily Aggregation
**POST** `/retribusi/v1/admin/summary/daily`
Trigger manual aggregation untuk daily summary.
**Request Body:**
```json
{
"date": "2025-12-16"
}
```
**Response (200):**
```json
{
"success": true,
"data": {
"rows_processed": 150,
"date": "2025-12-16"
},
"timestamp": 1703123456
}
```
**Access:** Admin only
---
#### Get Daily Summary
**GET** `/retribusi/v1/summary/daily`
Get daily summary data.
**Query Parameters:**
- `date` (required, format: YYYY-MM-DD)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": [
{
"summary_date": "2025-12-16",
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"total_count": 100,
"total_amount": 500000
}
],
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
### Hourly Summary
#### Get Hourly Summary
**GET** `/retribusi/v1/summary/hourly`
Get hourly summary data untuk chart (24 jam).
**Query Parameters:**
- `date` (required, format: YYYY-MM-DD)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": {
"labels": ["00", "01", "02", ..., "23"],
"series": {
"total_count": [10, 15, 20, ..., 5],
"total_amount": [50000, 75000, 100000, ..., 25000]
}
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
## Retribusi - Dashboard
Semua endpoint di bawah ini memerlukan **JWT Authentication**.
### Daily Chart
**GET** `/retribusi/v1/dashboard/daily`
Get daily chart data (line chart).
**Query Parameters:**
- `start_date` (required, format: YYYY-MM-DD)
- `end_date` (required, format: YYYY-MM-DD)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": {
"labels": ["2025-12-01", "2025-12-02", "2025-12-03"],
"series": {
"total_count": [100, 150, 200],
"total_amount": [500000, 750000, 1000000]
}
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
### By Category Chart
**GET** `/retribusi/v1/dashboard/by-category`
Get chart data grouped by category (bar/donut chart).
**Query Parameters:**
- `date` (required, format: YYYY-MM-DD)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": {
"labels": ["person_walk", "motor", "car"],
"series": {
"total_count": [50, 100, 30],
"total_amount": [100000, 500000, 300000]
}
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
### Summary Statistics
**GET** `/retribusi/v1/dashboard/summary`
Get summary statistics (stat cards).
**Query Parameters:**
- `date` (required, format: YYYY-MM-DD)
- `location_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": {
"total_count_today": 180,
"total_amount_today": 900000,
"by_gate": [
{
"gate_code": "gate01",
"total_count": 100,
"total_amount": 500000
}
],
"by_category": [
{
"category": "motor",
"total_count": 100,
"total_amount": 500000
}
]
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
## Retribusi - Realtime
Semua endpoint di bawah ini memerlukan **JWT Authentication**.
### SSE Stream
**GET** `/retribusi/v1/realtime/stream`
Server-Sent Events (SSE) stream untuk real-time updates.
**Headers:**
```
Authorization: Bearer <jwt-token>
Accept: text/event-stream
```
**Query Parameters:**
- `last_id` (optional, untuk resume dari event tertentu)
- `location_code` (optional, filter)
**Response (200):**
```
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
id: 12345
event: ingest
data: {"location_code":"kerkof_01","gate_code":"gate01","category":"motor","event_time":1703123456}
id: 12346
event: ingest
data: {"location_code":"kerkof_01","gate_code":"gate01","category":"car","event_time":1703123457}
...
```
**Access:** Viewer, Operator, Admin
**Note:** Connection akan tetap terbuka dan mengirim event setiap ada ingest baru. Ping dikirim setiap 10 detik jika tidak ada data.
---
### Snapshot
**GET** `/retribusi/v1/realtime/snapshot`
Get snapshot data untuk real-time dashboard cards.
**Query Parameters:**
- `date` (optional, default: today, format: YYYY-MM-DD)
- `location_code` (optional, filter)
**Response (200):**
```json
{
"success": true,
"data": {
"total_count_today": 180,
"total_amount_today": 900000,
"by_gate": [
{
"gate_code": "gate01",
"total_count": 100,
"total_amount": 500000
}
],
"by_category": [
{
"category": "motor",
"total_count": 100,
"total_amount": 500000
}
]
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
---
### Realtime Events
#### List Realtime Events
**GET** `/retribusi/v1/realtime/events`
Get list of realtime events (history events untuk SSE) dengan pagination dan filter.
**Query Parameters:**
- `page` (optional, default: 1)
- `limit` (optional, default: 20, max: 100)
- `location_code` (optional, filter)
- `gate_code` (optional, filter)
- `category` (optional, filter)
- `start_date` (optional, format: YYYY-MM-DD)
- `end_date` (optional, format: YYYY-MM-DD)
**Response (200):**
```json
{
"success": true,
"data": [
{
"id": 12345,
"location_code": "kerkof_01",
"gate_code": "gate01",
"category": "motor",
"event_time": 1703123456,
"total_count_delta": 1,
"created_at": "2025-01-17 10:30:01"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 1000,
"pages": 50
},
"timestamp": 1703123456
}
```
**Access:** Viewer, Operator, Admin
**Note:** `event_time` adalah Unix timestamp (integer). Endpoint ini untuk melihat history events yang sudah masuk ke realtime_events table.
---
## Error Responses
Semua endpoint mengembalikan error dalam format konsisten:
### 401 Unauthorized
```json
{
"error": "unauthorized",
"message": "Invalid or expired token"
}
```
### 403 Forbidden
```json
{
"error": "forbidden",
"message": "Insufficient permissions"
}
```
### 404 Not Found
```json
{
"error": "not_found",
"message": "Resource not found"
}
```
### 409 Conflict
```json
{
"error": "conflict",
"message": "Resource already exists"
}
```
### 422 Validation Error
```json
{
"error": "validation_error",
"fields": {
"location_code": "Field is required",
"price": "Must be >= 0"
}
}
```
### 500 Server Error
```json
{
"error": "server_error",
"message": "Internal server error"
}
```
---
## Authentication & Authorization
### JWT Token
- **Algorithm:** HS256
- **TTL:** Configurable via `JWT_TTL_SECONDS` (default: 3600 seconds)
- **Header:** `Authorization: Bearer <token>`
### Roles
- **viewer** - Read-only access
- **operator** - Read + Write (POST, PUT)
- **admin** - Full access (Read + Write + Delete)
### API Key (Ingest)
- **Header:** `X-API-KEY: <api-key>`
- **Config:** `RETRIBUSI_API_KEY` in `.env`
---
## Rate Limiting
Saat ini tidak ada rate limiting. Untuk production, pertimbangkan untuk menambahkan rate limiting middleware.
---
## Pagination
Semua list endpoints mendukung pagination:
- `page` - Page number (default: 1)
- `limit` - Items per page (default: 20, max: 100)
Response selalu include `meta` object dengan:
- `page` - Current page
- `limit` - Items per page
- `total` - Total items
- `pages` - Total pages
---
## Timestamps
Semua response include `timestamp` field (Unix timestamp).
Untuk date parameters, gunakan format: `YYYY-MM-DD`
Untuk datetime, gunakan format: `YYYY-MM-DD HH:MM:SS` atau Unix timestamp (integer).
---
## Support
Untuk pertanyaan atau issue, silakan hubungi tim development.
**Last Updated:** 2025-12-17