# API Overview This document describes the HTTP API exposed by the application using Next.js **Route Handlers** in the App Router (`app/api/*/route.js`). All routes below are served under the `/api` prefix. --- ## 1. Configuration Dependencies The API expects a valid server configuration. Required environment variables: - `MONGODB_URI` — database connection string (used by `lib/db.js`). - `SESSION_SECRET` — JWT signing secret for session cookies. - `NAS_ROOT_PATH` — NAS mount root for storage operations. Optional environment variables: - `SESSION_COOKIE_SECURE` — override for the cookie `Secure` flag (`true`/`false`). The environment can be validated via: - `lib/config/validateEnv.js` - `scripts/validate-env.mjs` In Docker/production-like runs, execute `node scripts/validate-env.mjs` before starting the server to fail fast. --- ## 2. Authentication & Authorization ### 2.1 Sessions Authentication uses a signed JWT stored in an HTTP-only cookie (`auth_session`). To access protected endpoints: 1. `POST /api/auth/login` to obtain the cookie. 2. Send subsequent requests with that cookie. Notes: - In production-like setups, cookies should be `Secure` and the app should run behind HTTPS. - 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) RBAC is enforced on filesystem-related endpoints. - **401 Unauthorized**: no valid session - **403 Forbidden**: session exists but branch access is not allowed --- ## 3. Error Handling & Conventions ### 3.1 Standard error response format All endpoints return JSON. 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): - Auth: - `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. --- ## 4. Endpoints ### 4.1 `GET /api/health` **Purpose** Health check endpoint: - Verifies database connectivity (`db.command({ ping: 1 })`). - Verifies readability of `NAS_ROOT_PATH`. **Authentication**: not required. **Response 200 (example)** ```json { "db": "ok", "nas": { "path": "/mnt/niederlassungen", "entriesSample": ["@Recently-Snapshot", "NL01", "NL02"] } } ``` --- ### 4.2 `POST /api/auth/login` **Purpose** Authenticate a user and set the session cookie. **Authentication**: not required. **Request body (JSON)** ```json { "username": "example.user", "password": "plain-text-password" } ``` **Responses** - `200 { "ok": true }` - `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" } } ``` --- ### 4.3 `GET /api/auth/logout` **Purpose** Destroy the current session by clearing the cookie. **Authentication**: recommended (but endpoint is idempotent). **Response** - `200 { "ok": true }` **Error response (rare)** - `500` ```json { "error": { "message": "Internal server error", "code": "INTERNAL_SERVER_ERROR" } } ``` --- ### 4.4 `GET /api/branches` Returns the list of branches (e.g. `["NL01", "NL02"]`). **Authentication**: required. **RBAC behavior** - `branch` role → only own branch - `admin`/`dev` → all branches **Response 200** ```json { "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` Example: `/api/branches/NL01/years` **Authentication**: required. **Response 200** ```json { "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` Example: `/api/branches/NL01/2024/months` **Authentication**: required. **Response 200** ```json { "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` Example: `/api/branches/NL01/2024/10/days` **Authentication**: required. **Response 200** ```json { "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=` Example: ```text /api/files?branch=NL01&year=2024&month=10&day=23 ``` **Authentication**: required. **Response 200** ```json { "branch": "NL01", "year": "2024", "month": "10", "day": "23", "files": [{ "name": "test.pdf", "relativePath": "NL01/2024/10/23/test.pdf" }] } ``` **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 When adding new endpoints: 1. Define URL + method. 2. Implement a `route.js` under `app/api/...`. 3. Use `lib/storage` for filesystem access. 4. Enforce RBAC (`getSession()` + `canAccessBranch()` as needed). 5. Use the standardized error contract (prefer `withErrorHandling` + `ApiError` helpers). 6. Add route tests (Vitest). 7. Update this document.