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.
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.jsscripts/validate-env.mjsIn Docker/production-like runs, execute node scripts/validate-env.mjs before starting the server to fail fast.
Authentication uses a signed JWT stored in an HTTP-only cookie (auth_session).
To access protected endpoints:
POST /api/auth/login to obtain the cookie.Notes:
Secure and the app should run behind HTTPS.http://localhost:3000), you may set SESSION_COOKIE_SECURE=false in your local docker env file.RBAC is enforced on filesystem-related endpoints.
All endpoints return JSON.
Success responses keep their existing shapes (unchanged).
Error responses always use this standardized shape:
{
"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).The API uses the following status codes consistently:
400 — invalid/missing parameters, validation errors401 — unauthenticated (missing/invalid session) or invalid login credentials403 — 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)The API uses these machine-readable codes (non-exhaustive list):
Auth:
AUTH_UNAUTHENTICATEDAUTH_INVALID_CREDENTIALSAUTH_FORBIDDEN_BRANCHValidation:
VALIDATION_MISSING_PARAMVALIDATION_MISSING_QUERYVALIDATION_INVALID_JSONVALIDATION_INVALID_BODYVALIDATION_MISSING_FIELDStorage:
FS_NOT_FOUNDFS_STORAGE_ERRORInternal:
INTERNAL_SERVER_ERRORRoute 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)Secure cookie behavior, prefer HTTPS.http://localhost, many tools/browsers treat localhost as a special-case “secure context”. Behavior may vary between environments.GET /api/healthPurpose
Health check endpoint:
db.command({ ping: 1 })).NAS_ROOT_PATH.Authentication: not required.
Response 200 (example)
{
"db": "ok",
"nas": {
"path": "/mnt/niederlassungen",
"entriesSample": ["@Recently-Snapshot", "NL01", "NL02"]
}
}
POST /api/auth/loginPurpose
Authenticate a user and set the session cookie.
Authentication: not required.
Request body (JSON)
{ "username": "example.user", "password": "plain-text-password" }
Responses
200 { "ok": true }
400 (invalid JSON/body)
{
"error": {
"message": "Invalid request body",
"code": "VALIDATION_INVALID_JSON"
}
}
400 (missing username/password)
{
"error": {
"message": "Missing username or password",
"code": "VALIDATION_MISSING_FIELD",
"details": { "fields": ["username", "password"] }
}
}
401 (invalid credentials)
{
"error": {
"message": "Invalid credentials",
"code": "AUTH_INVALID_CREDENTIALS"
}
}
500
{
"error": {
"message": "Internal server error",
"code": "INTERNAL_SERVER_ERROR"
}
}
GET /api/auth/logoutPurpose
Destroy the current session by clearing the cookie.
Authentication: recommended (but endpoint is idempotent).
Response
200 { "ok": true }Error response (rare)
500
{
"error": {
"message": "Internal server error",
"code": "INTERNAL_SERVER_ERROR"
}
}
GET /api/branchesReturns the list of branches (e.g. ["NL01", "NL02"]).
Authentication: required.
RBAC behavior
branch role → only own branchadmin/dev → all branchesResponse 200
{ "branches": ["NL01", "NL02"] }
Error responses
401
{ "error": { "message": "Unauthorized", "code": "AUTH_UNAUTHENTICATED" } }
500
{
"error": { "message": "Internal server error", "code": "FS_STORAGE_ERROR" }
}
GET /api/branches/[branch]/yearsExample: /api/branches/NL01/years
Authentication: required.
Response 200
{ "branch": "NL01", "years": ["2023", "2024"] }
Error responses (common)
401 → AUTH_UNAUTHENTICATED403 → AUTH_FORBIDDEN_BRANCH400 → VALIDATION_MISSING_PARAM404 → FS_NOT_FOUND500 → FS_STORAGE_ERROR / INTERNAL_SERVER_ERRORGET /api/branches/[branch]/[year]/monthsExample: /api/branches/NL01/2024/months
Authentication: required.
Response 200
{ "branch": "NL01", "year": "2024", "months": ["01", "02", "10"] }
Error responses (common)
401 → AUTH_UNAUTHENTICATED403 → AUTH_FORBIDDEN_BRANCH400 → VALIDATION_MISSING_PARAM404 → FS_NOT_FOUND500 → FS_STORAGE_ERROR / INTERNAL_SERVER_ERRORGET /api/branches/[branch]/[year]/[month]/daysExample: /api/branches/NL01/2024/10/days
Authentication: required.
Response 200
{ "branch": "NL01", "year": "2024", "month": "10", "days": ["01", "23"] }
Error responses (common)
401 → AUTH_UNAUTHENTICATED403 → AUTH_FORBIDDEN_BRANCH400 → VALIDATION_MISSING_PARAM404 → FS_NOT_FOUND500 → FS_STORAGE_ERROR / INTERNAL_SERVER_ERRORGET /api/files?branch=&year=&month=&day=Example:
/api/files?branch=NL01&year=2024&month=10&day=23
Authentication: required.
Response 200
{
"branch": "NL01",
"year": "2024",
"month": "10",
"day": "23",
"files": [{ "name": "test.pdf", "relativePath": "NL01/2024/10/23/test.pdf" }]
}
Error responses (common)
401 → AUTH_UNAUTHENTICATED403 → AUTH_FORBIDDEN_BRANCH400 → VALIDATION_MISSING_QUERY404 → FS_NOT_FOUND500 → FS_STORAGE_ERROR / INTERNAL_SERVER_ERRORWhen adding new endpoints:
route.js under app/api/....lib/storage for filesystem access.getSession() + canAccessBranch() as needed).withErrorHandling + ApiError helpers).