Просмотр исходного кода

RHL-008-refactor(docs): update API documentation to include new /api/auth/me endpoint and frontend API usage guide

Code_Uwe 1 месяц назад
Родитель
Сommit
ced4c4e88c
4 измененных файлов с 563 добавлено и 33 удалено
  1. 47 16
      Docs/api.md
  2. 27 1
      Docs/auth.md
  3. 431 0
      Docs/frontend-api-usage.md
  4. 58 16
      Docs/runbook.md

+ 47 - 16
Docs/api.md

@@ -1,19 +1,11 @@
-<!-- --------------------------------------------------------------------------- -->
-
-<!-- Ordner: Docs -->
-
-<!-- Datei: api.md -->
-
-<!-- Relativer Pfad: Docs/api.md -->
-
-<!-- --------------------------------------------------------------------------- -->
-
 # API Overview
 # 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`).
 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.
 All routes below are served under the `/api` prefix.
 
 
+> Frontend developers: For practical usage examples and the small `apiClient` helper layer, read **`Docs/frontend-api-usage.md`**. That document is the frontend-oriented single source of truth.
+
 ---
 ---
 
 
 ## 1. Configuration Dependencies
 ## 1. Configuration Dependencies
@@ -315,7 +307,34 @@ Destroy the current session by clearing the cookie.
 
 
 ---
 ---
 
 
-### 4.4 `GET /api/branches`
+### 4.4 `GET /api/auth/me`
+
+**Purpose**
+
+Return the current session identity for frontend consumers.
+
+Rationale:
+
+- Frontends should not use `401` as basic control flow to determine “am I logged in?”.
+- This endpoint provides a stable, frontend-friendly session check.
+
+**Authentication**: not required.
+
+**Response 200 (unauthenticated)**
+
+```json
+{ "user": null }
+```
+
+**Response 200 (authenticated)**
+
+```json
+{ "user": { "userId": "...", "role": "branch|admin|dev", "branchId": "NL01" } }
+```
+
+---
+
+### 4.5 `GET /api/branches`
 
 
 Returns the list of branches (e.g. `["NL01", "NL02"]`).
 Returns the list of branches (e.g. `["NL01", "NL02"]`).
 
 
@@ -350,7 +369,7 @@ Returns the list of branches (e.g. `["NL01", "NL02"]`).
 
 
 ---
 ---
 
 
-### 4.5 `GET /api/branches/[branch]/years`
+### 4.6 `GET /api/branches/[branch]/years`
 
 
 Example: `/api/branches/NL01/years`
 Example: `/api/branches/NL01/years`
 
 
@@ -372,7 +391,7 @@ Example: `/api/branches/NL01/years`
 
 
 ---
 ---
 
 
-### 4.6 `GET /api/branches/[branch]/[year]/months`
+### 4.7 `GET /api/branches/[branch]/[year]/months`
 
 
 Example: `/api/branches/NL01/2024/months`
 Example: `/api/branches/NL01/2024/months`
 
 
@@ -394,7 +413,7 @@ Example: `/api/branches/NL01/2024/months`
 
 
 ---
 ---
 
 
-### 4.7 `GET /api/branches/[branch]/[year]/[month]/days`
+### 4.8 `GET /api/branches/[branch]/[year]/[month]/days`
 
 
 Example: `/api/branches/NL01/2024/10/days`
 Example: `/api/branches/NL01/2024/10/days`
 
 
@@ -416,7 +435,7 @@ Example: `/api/branches/NL01/2024/10/days`
 
 
 ---
 ---
 
 
-### 4.8 `GET /api/files?branch=&year=&month=&day=`
+### 4.9 `GET /api/files?branch=&year=&month=&day=`
 
 
 Example:
 Example:
 
 
@@ -448,7 +467,19 @@ Example:
 
 
 ---
 ---
 
 
-## 5. Adding New Endpoints
+## 5. API v1 freeze (RHL-008)
+
+The endpoints and response shapes documented here (and in `Docs/frontend-api-usage.md`) are considered **API v1** for the first frontend implementation.
+
+Rules:
+
+- Avoid breaking changes to existing endpoints, parameters, or response shapes.
+- Prefer additive changes (new endpoints, new optional fields).
+- If a breaking change becomes necessary, introduce a new endpoint rather than modifying the v1 contract.
+
+---
+
+## 6. Adding New Endpoints
 
 
 When adding new endpoints:
 When adding new endpoints:
 
 

+ 27 - 1
Docs/auth.md

@@ -28,7 +28,7 @@ This document covers:
 - Environment variables related to auth.
 - Environment variables related to auth.
 - Roles and RBAC rules.
 - Roles and RBAC rules.
 - Session payload and cookie configuration.
 - Session payload and cookie configuration.
-- Login and logout endpoints.
+- Login/logout/me endpoints.
 
 
 ---
 ---
 
 
@@ -268,6 +268,32 @@ Clears the session cookie.
 - Returns `200 { "ok": true }` on success.
 - Returns `200 { "ok": true }` on success.
 - Logout is idempotent.
 - Logout is idempotent.
 
 
+### 6.3 `GET /api/auth/me`
+
+Return the current session identity for frontend consumers.
+
+Rationale:
+
+- Frontends should not use `401` as basic control flow to determine “am I logged in?”.
+- `/api/auth/me` provides a stable, low-friction session check.
+
+Response (unauthenticated):
+
+```json
+{ "user": null }
+```
+
+Response (authenticated):
+
+```json
+{ "user": { "userId": "...", "role": "branch|admin|dev", "branchId": "NL01" } }
+```
+
+Security note:
+
+- The endpoint intentionally returns only the minimal session identity.
+- It does not reveal password hashes or user database internals.
+
 ---
 ---
 
 
 ## 7. Security Notes
 ## 7. Security Notes

+ 431 - 0
Docs/frontend-api-usage.md

@@ -0,0 +1,431 @@
+# Frontend API Usage (v1)
+
+This document is the **frontend-facing** single source of truth for consuming the Lieferscheine backend APIs.
+
+Scope:
+
+- Stable **API v1** contracts (URLs, params, response shapes).
+- The minimal frontend `apiClient` helper layer (`lib/frontend/apiClient.js`).
+- Practical examples for building the first UI.
+
+Non-goals:
+
+- New major features.
+- PDF streaming/viewer implementation details (see “Out of scope / planned”).
+
+---
+
+## 1. Quickstart
+
+### 1.1 Recommended calling pattern
+
+For the first UI, prefer calling the backend from **browser/client-side code** so the HTTP-only session cookie is naturally sent with requests.
+
+Key fetch requirements:
+
+- Always send cookies: `credentials: "include"`
+- Always request fresh data: `cache: "no-store"`
+
+The project provides a tiny wrapper to enforce those defaults: `lib/frontend/apiClient.js`.
+
+### 1.2 Happy-path navigation flow
+
+The core UI flow is a simple drill-down:
+
+1. `login({ username, password })`
+2. `getMe()` (optional but recommended)
+3. `getBranches()`
+4. `getYears(branch)`
+5. `getMonths(branch, year)`
+6. `getDays(branch, year, month)`
+7. `getFiles(branch, year, month, day)`
+
+### 1.3 Example usage (client-side)
+
+```js
+import {
+	login,
+	getMe,
+	getBranches,
+	getYears,
+	getMonths,
+	getDays,
+	getFiles,
+	ApiClientError,
+} from "@/lib/frontend/apiClient";
+
+export async function runExampleFlow() {
+	try {
+		await login({ username: "nl01user", password: "secret" });
+
+		const me = await getMe();
+		if (!me.user) throw new Error("Expected to be logged in");
+
+		const { branches } = await getBranches();
+		const branch = branches[0];
+
+		const { years } = await getYears(branch);
+		const year = years[years.length - 1];
+
+		const { months } = await getMonths(branch, year);
+		const month = months[months.length - 1];
+
+		const { days } = await getDays(branch, year, month);
+		const day = days[days.length - 1];
+
+		const { files } = await getFiles(branch, year, month, day);
+		return { branch, year, month, day, files };
+	} catch (err) {
+		if (err instanceof ApiClientError) {
+			// Use err.code for UI decisions.
+			// Example: AUTH_UNAUTHENTICATED -> redirect to login
+			throw err;
+		}
+		throw err;
+	}
+}
+```
+
+---
+
+## 2. The `apiClient` helper
+
+File:
+
+- `lib/frontend/apiClient.js`
+
+Design goals:
+
+- Enforce `credentials: "include"` and `cache: "no-store"`.
+- Parse JSON automatically.
+- Convert standardized backend errors into a single error type: `ApiClientError`.
+
+### 2.1 `ApiClientError`
+
+When the backend returns the standardized error payload:
+
+```json
+{ "error": { "message": "...", "code": "...", "details": {} } }
+```
+
+…the client throws:
+
+- `name = "ApiClientError"`
+- `status` (HTTP status)
+- `code` (machine-readable error code)
+- `message` (safe human-readable message)
+- optional `details`
+
+### 2.2 Provided helpers
+
+Auth:
+
+- `login({ username, password })`
+- `logout()`
+- `getMe()`
+
+Navigation:
+
+- `getBranches()`
+- `getYears(branch)`
+- `getMonths(branch, year)`
+- `getDays(branch, year, month)`
+
+Files:
+
+- `getFiles(branch, year, month, day)`
+
+Low-level:
+
+- `apiFetch(path, options)`
+
+### 2.3 Server-side usage note (Node / scripts)
+
+Node’s `fetch` does **not** include a cookie jar automatically.
+
+For manual verification we provide a script that includes a minimal cookie jar:
+
+- `scripts/manual-api-client-flow.mjs`
+
+For server checks, run it **inside the app container**:
+
+```bash
+docker compose exec app node scripts/manual-api-client-flow.mjs \
+  --baseUrl=http://127.0.0.1:3000 \
+  --username=<user> \
+  --password=<pw> \
+  --branch=NL01
+```
+
+---
+
+## 3. API v1 conventions
+
+### 3.1 Identifiers
+
+- `branch`: `NL01`, `NL02`, ...
+- `year`: `"YYYY"` (4 digits)
+- `month`: `"MM"` (2 digits, `01`–`12`)
+- `day`: `"DD"` (2 digits, `01`–`31`)
+
+### 3.2 Sorting
+
+Current server responses are sorted **ascending**:
+
+- branches: ascending by branch number (`NL01`, `NL02`, ...)
+- years/months/days: numeric ascending
+- files: lexicographic ascending by file name
+
+If the UI needs “newest first”, reverse the arrays in the UI.
+
+### 3.3 File entries
+
+`getFiles()` returns:
+
+```json
+{
+	"files": [{ "name": "...pdf", "relativePath": "NL01/2025/12/19/...pdf" }]
+}
+```
+
+Notes:
+
+- `relativePath` is **relative to `NAS_ROOT_PATH`** inside the container.
+- Treat it as an opaque identifier for a future download/stream endpoint.
+
+---
+
+## 4. Endpoint contracts (v1)
+
+All routes are served under `/api`.
+
+### 4.1 Auth
+
+#### `POST /api/auth/login`
+
+Body:
+
+```json
+{ "username": "example.user", "password": "plain" }
+```
+
+Success:
+
+```json
+{ "ok": true }
+```
+
+Errors:
+
+- `400 VALIDATION_*`
+- `401 AUTH_INVALID_CREDENTIALS`
+
+#### `GET /api/auth/logout`
+
+Success:
+
+```json
+{ "ok": true }
+```
+
+#### `GET /api/auth/me`
+
+Success (authenticated):
+
+```json
+{ "user": { "userId": "...", "role": "branch|admin|dev", "branchId": "NL01" } }
+```
+
+Success (unauthenticated):
+
+```json
+{ "user": null }
+```
+
+### 4.2 Branch navigation
+
+All endpoints below require a valid session.
+
+#### `GET /api/branches`
+
+Success:
+
+```json
+{ "branches": ["NL01", "NL02"] }
+```
+
+RBAC:
+
+- `branch` role: returns only its own branch
+- `admin`/`dev`: returns all branches
+
+#### `GET /api/branches/:branch/years`
+
+Success:
+
+```json
+{ "branch": "NL01", "years": ["2024", "2025"] }
+```
+
+#### `GET /api/branches/:branch/:year/months`
+
+Success:
+
+```json
+{ "branch": "NL01", "year": "2025", "months": ["01", "02"] }
+```
+
+#### `GET /api/branches/:branch/:year/:month/days`
+
+Success:
+
+```json
+{ "branch": "NL01", "year": "2025", "month": "12", "days": ["18", "19"] }
+```
+
+### 4.3 Files
+
+#### `GET /api/files?branch=&year=&month=&day=`
+
+Success:
+
+```json
+{
+	"branch": "NL01",
+	"year": "2025",
+	"month": "12",
+	"day": "19",
+	"files": [{ "name": "test.pdf", "relativePath": "NL01/2025/12/19/test.pdf" }]
+}
+```
+
+### 4.4 Health
+
+#### `GET /api/health`
+
+Always returns `200` and reports partial system state:
+
+- database connectivity
+- NAS readability
+
+---
+
+## 5. Error handling
+
+### 5.1 Standard error payload
+
+All error responses use:
+
+```json
+{
+	"error": {
+		"message": "Human readable message",
+		"code": "SOME_MACHINE_CODE",
+		"details": {}
+	}
+}
+```
+
+### 5.2 Common codes used by the UI
+
+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`
+
+---
+
+## 6. Caching & freshness
+
+The backend reads from a NAS where new scans can appear at any time.
+
+Backend rules:
+
+- All route handlers are `dynamic = "force-dynamic"`.
+- All JSON responses include `Cache-Control: no-store`.
+- A small process-local TTL cache exists in `lib/storage.js`:
+
+  - branches/years: 60s
+  - months/days/files: 15s
+
+Frontend guidance:
+
+- Use `credentials: "include"` and `cache: "no-store"`.
+- Do not rely on Next.js ISR/revalidate for these endpoints.
+
+---
+
+## 7. Manual verification (RHL-008)
+
+The repository contains a manual smoke test script that exercises:
+
+- happy path drill-down
+- negative cases (401/403/400/404)
+
+Script:
+
+- `scripts/manual-api-client-flow.mjs`
+
+Local:
+
+```bash
+node scripts/manual-api-client-flow.mjs \
+  --baseUrl=http://localhost:3000 \
+  --username=<user> \
+  --password=<pw> \
+  --branch=NL01
+```
+
+Server (recommended from within container):
+
+```bash
+docker compose exec app node scripts/manual-api-client-flow.mjs \
+  --baseUrl=http://127.0.0.1:3000 \
+  --username=<user> \
+  --password=<pw> \
+  --branch=NL01
+```
+
+---
+
+## 8. API v1 freeze policy
+
+As of RHL-008, the endpoints and response shapes documented here are considered **API v1**.
+
+Rules:
+
+- Avoid breaking changes to existing URLs, parameters, or response fields.
+- Prefer additive changes:
+
+  - add new endpoints
+  - add optional fields
+
+- If a breaking change becomes necessary, introduce a new endpoint rather than modifying the existing contract.
+
+---
+
+## 9. Out of scope / planned additions
+
+PDF delivery (download/stream) is not part of the current v1 surface documented above.
+
+Planned as additive change:
+
+- a dedicated endpoint to stream or download a PDF while enforcing RBAC server-side.

+ 58 - 16
Docs/runbook.md

@@ -1,13 +1,3 @@
-<!-- --------------------------------------------------------------------------- -->
-
-<!-- Ordner: Docs -->
-
-<!-- Datei: runbook.md -->
-
-<!-- Relativer Pfad: Docs/runbook.md -->
-
-<!-- --------------------------------------------------------------------------- -->
-
 # Runbook: Local Development vs Server Deployment
 # Runbook: Local Development vs Server Deployment
 
 
 This runbook describes how to run the project locally (developer machine) and on the internal server.
 This runbook describes how to run the project locally (developer machine) and on the internal server.
@@ -95,6 +85,12 @@ Run with the local override:
 docker compose -f docker-compose.yml -f docker-compose.local.yml up --build
 docker compose -f docker-compose.yml -f docker-compose.local.yml up --build
 ```
 ```
 
 
+If you prefer running in the background:
+
+```bash
+docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build
+```
+
 ### 2.5 Verify health
 ### 2.5 Verify health
 
 
 ```bash
 ```bash
@@ -125,8 +121,6 @@ In `mongosh`:
 ```js
 ```js
 use rhl-lieferscheine
 use rhl-lieferscheine
 
 
-show collections
-
 db.users.insertOne({
 db.users.insertOne({
   username: "branchuser",
   username: "branchuser",
   email: "nl01@example.com",
   email: "nl01@example.com",
@@ -138,7 +132,7 @@ db.users.insertOne({
 })
 })
 ```
 ```
 
 
-### 2.7 Login + call protected endpoints
+### 2.7 Login + call protected endpoints (curl)
 
 
 Login (stores cookie in `cookies.txt`):
 Login (stores cookie in `cookies.txt`):
 
 
@@ -171,7 +165,42 @@ Logout:
 curl -i -b cookies.txt -c cookies.txt http://localhost:3000/api/auth/logout
 curl -i -b cookies.txt -c cookies.txt http://localhost:3000/api/auth/logout
 ```
 ```
 
 
-### 2.8 Notes for Git Bash on Windows
+### 2.8 Manual end-to-end flow using the apiClient (RHL-008)
+
+The repo contains a manual smoke-test script that exercises:
+
+- authentication
+- drill-down navigation (branches -> years -> months -> days -> files)
+- negative cases (401/403/400/404)
+
+Script:
+
+- `scripts/manual-api-client-flow.mjs`
+
+Run locally from your host machine:
+
+```bash
+node scripts/manual-api-client-flow.mjs \
+  --baseUrl=http://localhost:3000 \
+  --username=<user> \
+  --password=<pw> \
+  --branch=NL01
+```
+
+If your host machine does not have Node, you can also run it inside the app container:
+
+```bash
+docker compose exec app node scripts/manual-api-client-flow.mjs \
+  --baseUrl=http://127.0.0.1:3000 \
+  --username=<user> \
+  --password=<pw> \
+  --branch=NL01
+```
+
+> Note: You may see a Node warning about ESM detection (`MODULE_TYPELESS_PACKAGE_JSON`).
+> The script still works and the warning is harmless.
+
+### 2.9 Notes for Git Bash on Windows
 
 
 Git Bash may rewrite paths like `/mnt/niederlassungen`.
 Git Bash may rewrite paths like `/mnt/niederlassungen`.
 
 
@@ -266,9 +295,22 @@ Expected:
 - `db` is `ok`
 - `db` is `ok`
 - `nas.entriesSample` contains real branch folders (`NLxx`)
 - `nas.entriesSample` contains real branch folders (`NLxx`)
 
 
-> Note: On some Linux servers, `localhost` resolves to IPv6 (`::1`). If `curl http://localhost:3000` fails, use `127.0.0.1` or `curl -4`.
+> Note: On some Linux servers, `localhost` resolves to IPv6 (`::1`).
+> If `curl http://localhost:3000/api/health` fails, use `127.0.0.1` or `curl -4`.
+
+### 3.6 Manual end-to-end flow (recommended inside container)
+
+Run the manual smoke test inside the app container (no Node installation required on the host):
+
+```bash
+docker compose exec app node scripts/manual-api-client-flow.mjs \
+  --baseUrl=http://127.0.0.1:3000 \
+  --username=<user> \
+  --password=<pw> \
+  --branch=NL01
+```
 
 
-### 3.6 Logs and troubleshooting
+### 3.7 Logs and troubleshooting
 
 
 ```bash
 ```bash
 docker compose -f docker-compose.yml logs --tail=200 app
 docker compose -f docker-compose.yml logs --tail=200 app