|
|
@@ -1,10 +1,10 @@
|
|
|
<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
|
-<!-- Ordner: Docs -->
|
|
|
+<!-- Folder: Docs -->
|
|
|
|
|
|
-<!-- Datei: api.md -->
|
|
|
+<!-- File: api.md -->
|
|
|
|
|
|
-<!-- Relativer Pfad: Docs/api.md -->
|
|
|
+<!-- Relative Path: Docs/api.md -->
|
|
|
|
|
|
<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
|
@@ -53,7 +53,7 @@ To access protected endpoints:
|
|
|
Notes:
|
|
|
|
|
|
- In production-like setups, cookies should be `Secure` and the app should run behind HTTPS.
|
|
|
-- For local HTTP testing (`http://localhost:3000`), set `SESSION_COOKIE_SECURE=false` in your local docker env file.
|
|
|
+- For local HTTP testing (`http://localhost:3000`), you may set `SESSION_COOKIE_SECURE=false` in your local docker env file.
|
|
|
|
|
|
### 2.2 RBAC (Branch-Level)
|
|
|
|
|
|
@@ -64,19 +64,80 @@ RBAC is enforced on filesystem-related endpoints.
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 3. General Conventions
|
|
|
+## 3. Error Handling & Conventions
|
|
|
|
|
|
-- All endpoints return JSON.
|
|
|
+### 3.1 Standard error response format
|
|
|
|
|
|
-- Error responses use:
|
|
|
+All endpoints return JSON.
|
|
|
|
|
|
- ```json
|
|
|
- { "error": "Human-readable error message" }
|
|
|
- ```
|
|
|
+Success responses keep their existing shapes (**unchanged**).
|
|
|
+
|
|
|
+Error responses always use this standardized shape:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "error": {
|
|
|
+ "message": "Human readable message",
|
|
|
+ "code": "SOME_MACHINE_CODE",
|
|
|
+ "details": {}
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Notes:
|
|
|
+
|
|
|
+- `error.message` is intended for humans (UI, logs).
|
|
|
+- `error.code` is a stable machine-readable identifier (frontend handling, tests, monitoring).
|
|
|
+- `error.details` is optional. When present, it must be a JSON object (e.g. validation info).
|
|
|
+
|
|
|
+### 3.2 Status code rules
|
|
|
+
|
|
|
+The API uses the following status codes consistently:
|
|
|
+
|
|
|
+- `400` — invalid/missing parameters, validation errors
|
|
|
+- `401` — unauthenticated (missing/invalid session) or invalid login credentials
|
|
|
+- `403` — authenticated but not allowed (RBAC / branch mismatch)
|
|
|
+- `404` — resource not found (branch/year/month/day/file does not exist)
|
|
|
+- `500` — unexpected server errors (internal failures)
|
|
|
+
|
|
|
+### 3.3 Common error codes
|
|
|
+
|
|
|
+The API uses these machine-readable codes (non-exhaustive list):
|
|
|
|
|
|
-- Route handlers use Web `Request` / `Response` primitives.
|
|
|
+- Auth:
|
|
|
|
|
|
-- For dynamic routes, Next.js 16 resolves parameters asynchronously via `ctx.params`.
|
|
|
+ - `AUTH_UNAUTHENTICATED`
|
|
|
+ - `AUTH_INVALID_CREDENTIALS`
|
|
|
+ - `AUTH_FORBIDDEN_BRANCH`
|
|
|
+
|
|
|
+- Validation:
|
|
|
+
|
|
|
+ - `VALIDATION_MISSING_PARAM`
|
|
|
+ - `VALIDATION_MISSING_QUERY`
|
|
|
+ - `VALIDATION_INVALID_JSON`
|
|
|
+ - `VALIDATION_INVALID_BODY`
|
|
|
+ - `VALIDATION_MISSING_FIELD`
|
|
|
+
|
|
|
+- Storage:
|
|
|
+
|
|
|
+ - `FS_NOT_FOUND`
|
|
|
+ - `FS_STORAGE_ERROR`
|
|
|
+
|
|
|
+- Internal:
|
|
|
+
|
|
|
+ - `INTERNAL_SERVER_ERROR`
|
|
|
+
|
|
|
+### 3.4 Implementation notes
|
|
|
+
|
|
|
+Route handlers use shared helpers:
|
|
|
+
|
|
|
+- `lib/api/errors.js` (standard error payloads + `withErrorHandling`)
|
|
|
+- `lib/api/storageErrors.js` (maps filesystem errors like ENOENT to 404 vs 500)
|
|
|
+
|
|
|
+### 3.5 Testing note
|
|
|
+
|
|
|
+- For realistic `Secure` cookie behavior, prefer HTTPS.
|
|
|
+- For local testing on `http://localhost`, many tools/browsers treat localhost as a special-case “secure context”. Behavior may vary between environments.
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -124,10 +185,51 @@ Authenticate a user and set the session cookie.
|
|
|
**Responses**
|
|
|
|
|
|
- `200 { "ok": true }`
|
|
|
-- `400 { "error": "Invalid request body" }`
|
|
|
-- `400 { "error": "Missing username or password" }`
|
|
|
-- `401 { "error": "Invalid credentials" }`
|
|
|
-- `500 { "error": "Internal server error" }`
|
|
|
+
|
|
|
+- `400` (invalid JSON/body)
|
|
|
+
|
|
|
+ ```json
|
|
|
+ {
|
|
|
+ "error": {
|
|
|
+ "message": "Invalid request body",
|
|
|
+ "code": "VALIDATION_INVALID_JSON"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+- `400` (missing username/password)
|
|
|
+
|
|
|
+ ```json
|
|
|
+ {
|
|
|
+ "error": {
|
|
|
+ "message": "Missing username or password",
|
|
|
+ "code": "VALIDATION_MISSING_FIELD",
|
|
|
+ "details": { "fields": ["username", "password"] }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+- `401` (invalid credentials)
|
|
|
+
|
|
|
+ ```json
|
|
|
+ {
|
|
|
+ "error": {
|
|
|
+ "message": "Invalid credentials",
|
|
|
+ "code": "AUTH_INVALID_CREDENTIALS"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+- `500`
|
|
|
+
|
|
|
+ ```json
|
|
|
+ {
|
|
|
+ "error": {
|
|
|
+ "message": "Internal server error",
|
|
|
+ "code": "INTERNAL_SERVER_ERROR"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -143,6 +245,19 @@ Destroy the current session by clearing the cookie.
|
|
|
|
|
|
- `200 { "ok": true }`
|
|
|
|
|
|
+**Error response (rare)**
|
|
|
+
|
|
|
+- `500`
|
|
|
+
|
|
|
+ ```json
|
|
|
+ {
|
|
|
+ "error": {
|
|
|
+ "message": "Internal server error",
|
|
|
+ "code": "INTERNAL_SERVER_ERROR"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
---
|
|
|
|
|
|
### 4.4 `GET /api/branches`
|
|
|
@@ -162,6 +277,22 @@ Returns the list of branches (e.g. `["NL01", "NL02"]`).
|
|
|
{ "branches": ["NL01", "NL02"] }
|
|
|
```
|
|
|
|
|
|
+**Error responses**
|
|
|
+
|
|
|
+- `401`
|
|
|
+
|
|
|
+ ```json
|
|
|
+ { "error": { "message": "Unauthorized", "code": "AUTH_UNAUTHENTICATED" } }
|
|
|
+ ```
|
|
|
+
|
|
|
+- `500`
|
|
|
+
|
|
|
+ ```json
|
|
|
+ {
|
|
|
+ "error": { "message": "Internal server error", "code": "FS_STORAGE_ERROR" }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
---
|
|
|
|
|
|
### 4.5 `GET /api/branches/[branch]/years`
|
|
|
@@ -176,6 +307,14 @@ Example: `/api/branches/NL01/years`
|
|
|
{ "branch": "NL01", "years": ["2023", "2024"] }
|
|
|
```
|
|
|
|
|
|
+**Error responses (common)**
|
|
|
+
|
|
|
+- `401` → `AUTH_UNAUTHENTICATED`
|
|
|
+- `403` → `AUTH_FORBIDDEN_BRANCH`
|
|
|
+- `400` → `VALIDATION_MISSING_PARAM`
|
|
|
+- `404` → `FS_NOT_FOUND`
|
|
|
+- `500` → `FS_STORAGE_ERROR` / `INTERNAL_SERVER_ERROR`
|
|
|
+
|
|
|
---
|
|
|
|
|
|
### 4.6 `GET /api/branches/[branch]/[year]/months`
|
|
|
@@ -190,6 +329,14 @@ Example: `/api/branches/NL01/2024/months`
|
|
|
{ "branch": "NL01", "year": "2024", "months": ["01", "02", "10"] }
|
|
|
```
|
|
|
|
|
|
+**Error responses (common)**
|
|
|
+
|
|
|
+- `401` → `AUTH_UNAUTHENTICATED`
|
|
|
+- `403` → `AUTH_FORBIDDEN_BRANCH`
|
|
|
+- `400` → `VALIDATION_MISSING_PARAM`
|
|
|
+- `404` → `FS_NOT_FOUND`
|
|
|
+- `500` → `FS_STORAGE_ERROR` / `INTERNAL_SERVER_ERROR`
|
|
|
+
|
|
|
---
|
|
|
|
|
|
### 4.7 `GET /api/branches/[branch]/[year]/[month]/days`
|
|
|
@@ -204,6 +351,14 @@ Example: `/api/branches/NL01/2024/10/days`
|
|
|
{ "branch": "NL01", "year": "2024", "month": "10", "days": ["01", "23"] }
|
|
|
```
|
|
|
|
|
|
+**Error responses (common)**
|
|
|
+
|
|
|
+- `401` → `AUTH_UNAUTHENTICATED`
|
|
|
+- `403` → `AUTH_FORBIDDEN_BRANCH`
|
|
|
+- `400` → `VALIDATION_MISSING_PARAM`
|
|
|
+- `404` → `FS_NOT_FOUND`
|
|
|
+- `500` → `FS_STORAGE_ERROR` / `INTERNAL_SERVER_ERROR`
|
|
|
+
|
|
|
---
|
|
|
|
|
|
### 4.8 `GET /api/files?branch=&year=&month=&day=`
|
|
|
@@ -228,6 +383,14 @@ Example:
|
|
|
}
|
|
|
```
|
|
|
|
|
|
+**Error responses (common)**
|
|
|
+
|
|
|
+- `401` → `AUTH_UNAUTHENTICATED`
|
|
|
+- `403` → `AUTH_FORBIDDEN_BRANCH`
|
|
|
+- `400` → `VALIDATION_MISSING_QUERY`
|
|
|
+- `404` → `FS_NOT_FOUND`
|
|
|
+- `500` → `FS_STORAGE_ERROR` / `INTERNAL_SERVER_ERROR`
|
|
|
+
|
|
|
---
|
|
|
|
|
|
## 5. Adding New Endpoints
|
|
|
@@ -238,5 +401,6 @@ When adding new endpoints:
|
|
|
2. Implement a `route.js` under `app/api/...`.
|
|
|
3. Use `lib/storage` for filesystem access.
|
|
|
4. Enforce RBAC (`getSession()` + `canAccessBranch()` as needed).
|
|
|
-5. Add route tests (Vitest).
|
|
|
-6. Update this document.
|
|
|
+5. Use the standardized error contract (prefer `withErrorHandling` + `ApiError` helpers).
|
|
|
+6. Add route tests (Vitest).
|
|
|
+7. Update this document.
|