|
@@ -1,23 +1,3 @@
|
|
|
-<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
-
|
|
|
|
|
-<!-- Ordner: Docs -->
|
|
|
|
|
-
|
|
|
|
|
-<!-- Datei: api.md -->
|
|
|
|
|
-
|
|
|
|
|
-<!-- Relativer Pfad: Docs/api.md -->
|
|
|
|
|
-
|
|
|
|
|
-<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
-
|
|
|
|
|
-<!-- --------------------------------------------------------------------------- -->
|
|
|
|
|
-
|
|
|
|
|
-<!-- 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`).
|
|
@@ -47,19 +27,16 @@ Optional environment variables:
|
|
|
The Search API can run with different provider backends.
|
|
The Search API can run with different provider backends.
|
|
|
|
|
|
|
|
- `SEARCH_PROVIDER` (optional)
|
|
- `SEARCH_PROVIDER` (optional)
|
|
|
-
|
|
|
|
|
- Allowed values: `fs` | `qsirch`
|
|
- Allowed values: `fs` | `qsirch`
|
|
|
- Default: `fs`
|
|
- Default: `fs`
|
|
|
|
|
|
|
|
Notes:
|
|
Notes:
|
|
|
-
|
|
|
|
|
- `fs` is a local/test fallback that traverses the NAS-like folder structure directly.
|
|
- `fs` is a local/test fallback that traverses the NAS-like folder structure directly.
|
|
|
- `qsirch` is the intended production provider (indexed search on QNAP).
|
|
- `qsirch` is the intended production provider (indexed search on QNAP).
|
|
|
|
|
|
|
|
If `SEARCH_PROVIDER=qsirch`, these variables are required:
|
|
If `SEARCH_PROVIDER=qsirch`, these variables are required:
|
|
|
|
|
|
|
|
- `QSIRCH_BASE_URL` — base URL of the Qsirch service (must be reachable from inside the app container).
|
|
- `QSIRCH_BASE_URL` — base URL of the Qsirch service (must be reachable from inside the app container).
|
|
|
-
|
|
|
|
|
- Example: `http://192.168.0.22:8080`
|
|
- Example: `http://192.168.0.22:8080`
|
|
|
|
|
|
|
|
- `QSIRCH_ACCOUNT` — QTS/Qsirch account used for server-to-server search.
|
|
- `QSIRCH_ACCOUNT` — QTS/Qsirch account used for server-to-server search.
|
|
@@ -67,23 +44,19 @@ If `SEARCH_PROVIDER=qsirch`, these variables are required:
|
|
|
- `QSIRCH_PASSWORD` — password for the account.
|
|
- `QSIRCH_PASSWORD` — password for the account.
|
|
|
|
|
|
|
|
- `QSIRCH_PATH_PREFIX` — path prefix that contains the branch folders.
|
|
- `QSIRCH_PATH_PREFIX` — path prefix that contains the branch folders.
|
|
|
-
|
|
|
|
|
- Example: `/Niederlassungen`
|
|
- Example: `/Niederlassungen`
|
|
|
|
|
|
|
|
Optional Qsirch tuning:
|
|
Optional Qsirch tuning:
|
|
|
|
|
|
|
|
- `QSIRCH_DATE_FIELD`
|
|
- `QSIRCH_DATE_FIELD`
|
|
|
-
|
|
|
|
|
- Allowed values: `modified` | `created` (case-insensitive)
|
|
- Allowed values: `modified` | `created` (case-insensitive)
|
|
|
- Default: `modified`
|
|
- Default: `modified`
|
|
|
|
|
|
|
|
- `QSIRCH_MODE`
|
|
- `QSIRCH_MODE`
|
|
|
-
|
|
|
|
|
- Allowed values: `sync` | `async` | `auto` (case-insensitive)
|
|
- Allowed values: `sync` | `async` | `auto` (case-insensitive)
|
|
|
- Default: `sync`
|
|
- Default: `sync`
|
|
|
|
|
|
|
|
Notes:
|
|
Notes:
|
|
|
-
|
|
|
|
|
- The current implementation is **sync-first**.
|
|
- The current implementation is **sync-first**.
|
|
|
- `auto` currently behaves like `sync` (placeholder for a later async implementation).
|
|
- `auto` currently behaves like `sync` (placeholder for a later async implementation).
|
|
|
|
|
|
|
@@ -169,7 +142,7 @@ Rules for such endpoints:
|
|
|
The API uses the following status codes consistently:
|
|
The API uses the following status codes consistently:
|
|
|
|
|
|
|
|
- `400` — invalid/missing parameters, validation errors
|
|
- `400` — invalid/missing parameters, validation errors
|
|
|
-- `401` — unauthenticated (missing/invalid session) or invalid login credentials
|
|
|
|
|
|
|
+- `401` — unauthenticated (missing/invalid session) or invalid credentials
|
|
|
- `403` — authenticated but not allowed (RBAC / branch mismatch)
|
|
- `403` — authenticated but not allowed (RBAC / branch mismatch)
|
|
|
- `404` — resource not found (branch/year/month/day/file does not exist)
|
|
- `404` — resource not found (branch/year/month/day/file does not exist)
|
|
|
- `500` — unexpected server errors (internal failures)
|
|
- `500` — unexpected server errors (internal failures)
|
|
@@ -179,21 +152,21 @@ The API uses the following status codes consistently:
|
|
|
The API uses these machine-readable codes (non-exhaustive list):
|
|
The API uses these machine-readable codes (non-exhaustive list):
|
|
|
|
|
|
|
|
- Auth:
|
|
- Auth:
|
|
|
-
|
|
|
|
|
- `AUTH_UNAUTHENTICATED`
|
|
- `AUTH_UNAUTHENTICATED`
|
|
|
- `AUTH_INVALID_CREDENTIALS`
|
|
- `AUTH_INVALID_CREDENTIALS`
|
|
|
- `AUTH_FORBIDDEN_BRANCH`
|
|
- `AUTH_FORBIDDEN_BRANCH`
|
|
|
|
|
|
|
|
- Validation (generic):
|
|
- Validation (generic):
|
|
|
-
|
|
|
|
|
- `VALIDATION_MISSING_PARAM`
|
|
- `VALIDATION_MISSING_PARAM`
|
|
|
- `VALIDATION_MISSING_QUERY`
|
|
- `VALIDATION_MISSING_QUERY`
|
|
|
- `VALIDATION_INVALID_JSON`
|
|
- `VALIDATION_INVALID_JSON`
|
|
|
- `VALIDATION_INVALID_BODY`
|
|
- `VALIDATION_INVALID_BODY`
|
|
|
- `VALIDATION_MISSING_FIELD`
|
|
- `VALIDATION_MISSING_FIELD`
|
|
|
|
|
|
|
|
-- Validation (filesystem route params):
|
|
|
|
|
|
|
+- Validation (password management):
|
|
|
|
|
+ - `VALIDATION_WEAK_PASSWORD`
|
|
|
|
|
|
|
|
|
|
+- Validation (filesystem route params):
|
|
|
- `VALIDATION_BRANCH`
|
|
- `VALIDATION_BRANCH`
|
|
|
- `VALIDATION_YEAR`
|
|
- `VALIDATION_YEAR`
|
|
|
- `VALIDATION_MONTH`
|
|
- `VALIDATION_MONTH`
|
|
@@ -203,7 +176,6 @@ The API uses these machine-readable codes (non-exhaustive list):
|
|
|
- `VALIDATION_PATH_TRAVERSAL`
|
|
- `VALIDATION_PATH_TRAVERSAL`
|
|
|
|
|
|
|
|
- Validation (search):
|
|
- Validation (search):
|
|
|
-
|
|
|
|
|
- `VALIDATION_SEARCH_SCOPE`
|
|
- `VALIDATION_SEARCH_SCOPE`
|
|
|
- `VALIDATION_SEARCH_BRANCH`
|
|
- `VALIDATION_SEARCH_BRANCH`
|
|
|
- `VALIDATION_SEARCH_BRANCHES`
|
|
- `VALIDATION_SEARCH_BRANCHES`
|
|
@@ -214,16 +186,13 @@ The API uses these machine-readable codes (non-exhaustive list):
|
|
|
- `VALIDATION_SEARCH_MISSING_FILTER`
|
|
- `VALIDATION_SEARCH_MISSING_FILTER`
|
|
|
|
|
|
|
|
- Storage:
|
|
- Storage:
|
|
|
-
|
|
|
|
|
- `FS_NOT_FOUND`
|
|
- `FS_NOT_FOUND`
|
|
|
- `FS_STORAGE_ERROR`
|
|
- `FS_STORAGE_ERROR`
|
|
|
|
|
|
|
|
- Search (backend/provider):
|
|
- Search (backend/provider):
|
|
|
-
|
|
|
|
|
- `SEARCH_BACKEND_UNAVAILABLE` (provider misconfiguration/unavailability)
|
|
- `SEARCH_BACKEND_UNAVAILABLE` (provider misconfiguration/unavailability)
|
|
|
|
|
|
|
|
- Internal:
|
|
- Internal:
|
|
|
-
|
|
|
|
|
- `INTERNAL_SERVER_ERROR`
|
|
- `INTERNAL_SERVER_ERROR`
|
|
|
|
|
|
|
|
### 3.4 Implementation notes
|
|
### 3.4 Implementation notes
|
|
@@ -293,7 +262,6 @@ Frontend code should call these endpoints with explicit “fresh data” setting
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
- Do not rely on `next: { revalidate: ... }` for these endpoints. Freshness is controlled via:
|
|
- Do not rely on `next: { revalidate: ... }` for these endpoints. Freshness is controlled via:
|
|
|
-
|
|
|
|
|
- `Cache-Control: no-store` (HTTP)
|
|
- `Cache-Control: no-store` (HTTP)
|
|
|
- server-side storage TTL micro-cache
|
|
- server-side storage TTL micro-cache
|
|
|
|
|
|
|
@@ -433,7 +401,113 @@ This avoids using 401 as control-flow for basic "am I logged in?" checks.
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.5 `GET /api/branches`
|
|
|
|
|
|
|
+### 4.5 `POST /api/auth/change-password` (RHL-009)
|
|
|
|
|
+
|
|
|
|
|
+**Purpose**
|
|
|
|
|
+
|
|
|
|
|
+Change the password for the currently authenticated user.
|
|
|
|
|
+
|
|
|
|
|
+**Authentication**: required.
|
|
|
|
|
+
|
|
|
|
|
+**Request body (JSON)**
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "currentPassword": "<string>",
|
|
|
|
|
+ "newPassword": "<string>"
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Response 200**
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{ "ok": true }
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Behavior notes**
|
|
|
|
|
+
|
|
|
|
|
+- The endpoint validates JSON and required fields.
|
|
|
|
|
+- The endpoint verifies `currentPassword` against the stored `passwordHash`.
|
|
|
|
|
+- The endpoint enforces an explicit password policy (see `docs/auth.md`).
|
|
|
|
|
+- On success, the endpoint also clears password-related flags:
|
|
|
|
|
+ - `mustChangePassword = false`
|
|
|
|
|
+ - `passwordResetToken = null`
|
|
|
|
|
+ - `passwordResetExpiresAt = null`
|
|
|
|
|
+
|
|
|
|
|
+**Error responses**
|
|
|
|
|
+
|
|
|
|
|
+- `401` when no session exists or the session is invalid:
|
|
|
|
|
+
|
|
|
|
|
+ ```json
|
|
|
|
|
+ { "error": { "message": "Unauthorized", "code": "AUTH_UNAUTHENTICATED" } }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+- `401` when `currentPassword` does not match:
|
|
|
|
|
+
|
|
|
|
|
+ ```json
|
|
|
|
|
+ {
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "message": "Invalid credentials",
|
|
|
|
|
+ "code": "AUTH_INVALID_CREDENTIALS"
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+- `400` when the JSON body is invalid:
|
|
|
|
|
+
|
|
|
|
|
+ ```json
|
|
|
|
|
+ {
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "message": "Invalid request body",
|
|
|
|
|
+ "code": "VALIDATION_INVALID_JSON"
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+- `400` when required fields are missing:
|
|
|
|
|
+
|
|
|
|
|
+ ```json
|
|
|
|
|
+ {
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "message": "Missing currentPassword or newPassword",
|
|
|
|
|
+ "code": "VALIDATION_MISSING_FIELD",
|
|
|
|
|
+ "details": { "fields": ["currentPassword", "newPassword"] }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+- `400` when `newPassword` violates the password policy:
|
|
|
|
|
+
|
|
|
|
|
+ ```json
|
|
|
|
|
+ {
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "message": "Weak password",
|
|
|
|
|
+ "code": "VALIDATION_WEAK_PASSWORD",
|
|
|
|
|
+ "details": {
|
|
|
|
|
+ "minLength": 8,
|
|
|
|
|
+ "requireLetter": true,
|
|
|
|
|
+ "requireNumber": true,
|
|
|
|
|
+ "disallowSameAsCurrent": true,
|
|
|
|
|
+ "reasons": ["MIN_LENGTH", "MISSING_NUMBER"]
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+- `500` for unexpected errors:
|
|
|
|
|
+
|
|
|
|
|
+ ```json
|
|
|
|
|
+ {
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "message": "Internal server error",
|
|
|
|
|
+ "code": "INTERNAL_SERVER_ERROR"
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### 4.6 `GET /api/branches`
|
|
|
|
|
|
|
|
Returns the list of branches (e.g. `["NL01", "NL02"]`).
|
|
Returns the list of branches (e.g. `["NL01", "NL02"]`).
|
|
|
|
|
|
|
@@ -468,7 +542,7 @@ Returns the list of branches (e.g. `["NL01", "NL02"]`).
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.6 `GET /api/branches/[branch]/years`
|
|
|
|
|
|
|
+### 4.7 `GET /api/branches/[branch]/years`
|
|
|
|
|
|
|
|
Example: `/api/branches/NL01/years`
|
|
Example: `/api/branches/NL01/years`
|
|
|
|
|
|
|
@@ -482,7 +556,7 @@ Example: `/api/branches/NL01/years`
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.7 `GET /api/branches/[branch]/[year]/months`
|
|
|
|
|
|
|
+### 4.8 `GET /api/branches/[branch]/[year]/months`
|
|
|
|
|
|
|
|
Example: `/api/branches/NL01/2024/months`
|
|
Example: `/api/branches/NL01/2024/months`
|
|
|
|
|
|
|
@@ -496,7 +570,7 @@ Example: `/api/branches/NL01/2024/months`
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.8 `GET /api/branches/[branch]/[year]/[month]/days`
|
|
|
|
|
|
|
+### 4.9 `GET /api/branches/[branch]/[year]/[month]/days`
|
|
|
|
|
|
|
|
Example: `/api/branches/NL01/2024/10/days`
|
|
Example: `/api/branches/NL01/2024/10/days`
|
|
|
|
|
|
|
@@ -510,7 +584,7 @@ Example: `/api/branches/NL01/2024/10/days`
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.9 `GET /api/files?branch=&year=&month=&day=`
|
|
|
|
|
|
|
+### 4.10 `GET /api/files?branch=&year=&month=&day=`
|
|
|
|
|
|
|
|
Example:
|
|
Example:
|
|
|
|
|
|
|
@@ -534,7 +608,7 @@ Example:
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.10 `GET /api/files/:branch/:year/:month/:day/:filename`
|
|
|
|
|
|
|
+### 4.11 `GET /api/files/:branch/:year/:month/:day/:filename`
|
|
|
|
|
|
|
|
**Purpose**
|
|
**Purpose**
|
|
|
|
|
|
|
@@ -558,7 +632,6 @@ Stream (or download) a single PDF file from the NAS while enforcing authenticati
|
|
|
**Query params (optional)**
|
|
**Query params (optional)**
|
|
|
|
|
|
|
|
- `download=1` or `download=true`
|
|
- `download=1` or `download=true`
|
|
|
-
|
|
|
|
|
- Forces `Content-Disposition: attachment` (download)
|
|
- Forces `Content-Disposition: attachment` (download)
|
|
|
- Default is `inline` (open in browser)
|
|
- Default is `inline` (open in browser)
|
|
|
|
|
|
|
@@ -566,91 +639,22 @@ Stream (or download) a single PDF file from the NAS while enforcing authenticati
|
|
|
|
|
|
|
|
- Body: raw PDF bytes (not JSON)
|
|
- Body: raw PDF bytes (not JSON)
|
|
|
- Headers (example):
|
|
- Headers (example):
|
|
|
-
|
|
|
|
|
- `Content-Type: application/pdf`
|
|
- `Content-Type: application/pdf`
|
|
|
-
|
|
|
|
|
- `Cache-Control: no-store`
|
|
- `Cache-Control: no-store`
|
|
|
|
|
|
|
|
- - `Content-Disposition`:
|
|
|
|
|
-
|
|
|
|
|
- - Default: `inline`
|
|
|
|
|
- - When `download=1`: `attachment`
|
|
|
|
|
-
|
|
|
|
|
- For filename handling (Unicode-safe):
|
|
|
|
|
-
|
|
|
|
|
- - `filename="..."` is an ASCII fallback (safe for header values)
|
|
|
|
|
- - `filename*=UTF-8''...` contains the UTF-8 encoded original name
|
|
|
|
|
-
|
|
|
|
|
- Example:
|
|
|
|
|
-
|
|
|
|
|
- ```text
|
|
|
|
|
- Content-Disposition: inline; filename="Euro _.pdf"; filename*=UTF-8''Euro%20%E2%82%AC.pdf
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
**Error responses (JSON)**
|
|
**Error responses (JSON)**
|
|
|
|
|
|
|
|
Even though the happy path is binary, error responses remain standardized JSON.
|
|
Even though the happy path is binary, error responses remain standardized JSON.
|
|
|
|
|
|
|
|
-Common error codes:
|
|
|
|
|
-
|
|
|
|
|
-- `400` validation errors:
|
|
|
|
|
-
|
|
|
|
|
- - `VALIDATION_MISSING_PARAM`
|
|
|
|
|
- - `VALIDATION_BRANCH`
|
|
|
|
|
- - `VALIDATION_YEAR`
|
|
|
|
|
- - `VALIDATION_MONTH`
|
|
|
|
|
- - `VALIDATION_DAY`
|
|
|
|
|
- - `VALIDATION_FILENAME`
|
|
|
|
|
- - `VALIDATION_FILE_EXTENSION`
|
|
|
|
|
- - `VALIDATION_PATH_TRAVERSAL`
|
|
|
|
|
-
|
|
|
|
|
-- `401`
|
|
|
|
|
-
|
|
|
|
|
- ```json
|
|
|
|
|
- { "error": { "message": "Unauthorized", "code": "AUTH_UNAUTHENTICATED" } }
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
-- `403`
|
|
|
|
|
-
|
|
|
|
|
- ```json
|
|
|
|
|
- { "error": { "message": "Forbidden", "code": "AUTH_FORBIDDEN_BRANCH" } }
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
-- `404` (file not found)
|
|
|
|
|
-
|
|
|
|
|
- ```json
|
|
|
|
|
- {
|
|
|
|
|
- "error": {
|
|
|
|
|
- "message": "Not found",
|
|
|
|
|
- "code": "FS_NOT_FOUND",
|
|
|
|
|
- "details": {
|
|
|
|
|
- "branch": "NL01",
|
|
|
|
|
- "year": "2024",
|
|
|
|
|
- "month": "10",
|
|
|
|
|
- "day": "23",
|
|
|
|
|
- "filename": "example.pdf"
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
-- `500`
|
|
|
|
|
-
|
|
|
|
|
- ```json
|
|
|
|
|
- {
|
|
|
|
|
- "error": { "message": "Internal server error", "code": "FS_STORAGE_ERROR" }
|
|
|
|
|
- }
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-### 4.11 `GET /api/search`
|
|
|
|
|
|
|
+### 4.12 `GET /api/search`
|
|
|
|
|
|
|
|
**Purpose**
|
|
**Purpose**
|
|
|
|
|
|
|
|
Search delivery note content across PDFs.
|
|
Search delivery note content across PDFs.
|
|
|
|
|
|
|
|
-The endpoint returns _search hits_ with enough metadata to:
|
|
|
|
|
|
|
+The endpoint returns search hits with enough metadata to:
|
|
|
|
|
|
|
|
- navigate to the correct Explorer day folder
|
|
- navigate to the correct Explorer day folder
|
|
|
- open the PDF via the binary file endpoint
|
|
- open the PDF via the binary file endpoint
|
|
@@ -660,59 +664,48 @@ The endpoint returns _search hits_ with enough metadata to:
|
|
|
**RBAC behavior**
|
|
**RBAC behavior**
|
|
|
|
|
|
|
|
- `branch` role:
|
|
- `branch` role:
|
|
|
-
|
|
|
|
|
- results are limited to the user’s `branchId`
|
|
- results are limited to the user’s `branchId`
|
|
|
- attempting to query other branches may return `403 AUTH_FORBIDDEN_BRANCH`
|
|
- attempting to query other branches may return `403 AUTH_FORBIDDEN_BRANCH`
|
|
|
|
|
|
|
|
- `admin`/`dev` role:
|
|
- `admin`/`dev` role:
|
|
|
-
|
|
|
|
|
- can search across branches using explicit scope parameters
|
|
- can search across branches using explicit scope parameters
|
|
|
|
|
|
|
|
**Query params**
|
|
**Query params**
|
|
|
|
|
|
|
|
- `q` (optional)
|
|
- `q` (optional)
|
|
|
-
|
|
|
|
|
- Text query string.
|
|
- Text query string.
|
|
|
|
|
|
|
|
- `scope` (optional)
|
|
- `scope` (optional)
|
|
|
-
|
|
|
|
|
- `branch` — single branch scope (admin/dev only; branch users are forced to their own branch)
|
|
- `branch` — single branch scope (admin/dev only; branch users are forced to their own branch)
|
|
|
- `all` — all branches (admin/dev only)
|
|
- `all` — all branches (admin/dev only)
|
|
|
- `multi` — only the branches listed in `branches` (admin/dev only)
|
|
- `multi` — only the branches listed in `branches` (admin/dev only)
|
|
|
|
|
|
|
|
- `branch` (optional)
|
|
- `branch` (optional)
|
|
|
-
|
|
|
|
|
- Single branch for `scope=branch`.
|
|
- Single branch for `scope=branch`.
|
|
|
- Branch users may omit `branch` and are forced to their own branch.
|
|
- Branch users may omit `branch` and are forced to their own branch.
|
|
|
|
|
|
|
|
- `branches` (optional)
|
|
- `branches` (optional)
|
|
|
-
|
|
|
|
|
- Comma-separated branch list for `scope=multi`.
|
|
- Comma-separated branch list for `scope=multi`.
|
|
|
|
|
|
|
|
- `from`, `to` (optional)
|
|
- `from`, `to` (optional)
|
|
|
-
|
|
|
|
|
- Inclusive date filter in `YYYY-MM-DD` format.
|
|
- Inclusive date filter in `YYYY-MM-DD` format.
|
|
|
|
|
|
|
|
- `limit` (optional)
|
|
- `limit` (optional)
|
|
|
-
|
|
|
|
|
- Page size.
|
|
- Page size.
|
|
|
- Default: **100**
|
|
- Default: **100**
|
|
|
- Allowed: **50..200**
|
|
- Allowed: **50..200**
|
|
|
|
|
|
|
|
- `cursor` (optional)
|
|
- `cursor` (optional)
|
|
|
-
|
|
|
|
|
- Pagination cursor returned by the previous response.
|
|
- Pagination cursor returned by the previous response.
|
|
|
- Treat as **opaque**.
|
|
- Treat as **opaque**.
|
|
|
|
|
|
|
|
**Filter rule (important)**
|
|
**Filter rule (important)**
|
|
|
|
|
|
|
|
-To avoid accidental "match everything" queries (especially dangerous in `scope=all`), the backend requires:
|
|
|
|
|
|
|
+To avoid accidental "match everything" queries (especially dangerous in `scope=all`), the backend requires at least one of:
|
|
|
|
|
|
|
|
-- at least one of:
|
|
|
|
|
-
|
|
|
|
|
- - `q`
|
|
|
|
|
- - `from`
|
|
|
|
|
- - `to`
|
|
|
|
|
|
|
+- `q`
|
|
|
|
|
+- `from`
|
|
|
|
|
+- `to`
|
|
|
|
|
|
|
|
If all three are missing, the API returns:
|
|
If all three are missing, the API returns:
|
|
|
|
|
|
|
@@ -739,19 +732,6 @@ If all three are missing, the API returns:
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-Notes:
|
|
|
|
|
-
|
|
|
|
|
-- Result order is typically relevance-based.
|
|
|
|
|
-- `nextCursor` is `null` when there are no more results.
|
|
|
|
|
-- `total` is the total number of matching items for the current query. It may be `null` if the backend/provider cannot provide a reliable total.
|
|
|
|
|
-
|
|
|
|
|
-**Error responses**
|
|
|
|
|
-
|
|
|
|
|
-- `400` for missing/invalid parameters (returns a `VALIDATION_*` code)
|
|
|
|
|
-- `401` for unauthenticated access
|
|
|
|
|
-- `403` for branch violations (`AUTH_FORBIDDEN_BRANCH`)
|
|
|
|
|
-- `500` for internal search/index failures (`INTERNAL_SERVER_ERROR` or `SEARCH_BACKEND_UNAVAILABLE`)
|
|
|
|
|
-
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
## 5. API v1 freeze (RHL-008)
|
|
## 5. API v1 freeze (RHL-008)
|