From bbe4cc000b140054108185ad09c78745e297d2bb Mon Sep 17 00:00:00 2001 From: mwpn Date: Wed, 17 Dec 2025 13:43:59 +0700 Subject: [PATCH] Add: Complete API endpoints documentation - List all endpoints with request/response examples - Include authentication requirements - Document error responses - Add query parameters and access control info --- API_ENDPOINTS.md | 917 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 917 insertions(+) create mode 100644 API_ENDPOINTS.md diff --git a/API_ENDPOINTS.md b/API_ENDPOINTS.md new file mode 100644 index 0000000..952117a --- /dev/null +++ b/API_ENDPOINTS.md @@ -0,0 +1,917 @@ +# 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) +- [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 +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 + +--- + +#### 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", + "is_active": 1 + } + ], + "meta": { + "page": 1, + "limit": 20, + "total": 5, + "pages": 1 + }, + "timestamp": 1703123456 +} +``` + +**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", + "is_active": 1 +} +``` + +**Response (201):** +```json +{ + "success": true, + "data": { + "location_code": "kerkof_01", + "gate_code": "gate02", + "name": "Gate 02", + "direction": "out", + "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", + "is_active": 1 +} +``` + +**Response (200):** +```json +{ + "success": true, + "data": { + "location_code": "kerkof_01", + "gate_code": "gate02", + "name": "Gate 02 Updated", + "direction": "out", + "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 + +#### 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 + +--- + +## 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 +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 + +--- + +## 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 ` + +### Roles +- **viewer** - Read-only access +- **operator** - Read + Write (POST, PUT) +- **admin** - Full access (Read + Write + Delete) + +### API Key (Ingest) +- **Header:** `X-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 +