Lieferscheine.md 11 KB

Storage Module (lib/storage)

The lib/storage module is the single source of truth for reading files from the network file share that contains scanned delivery notes.

All code that needs to read from the NAS must go through this module instead of using Node.js fs directly. This keeps filesystem logic centralized and makes it easier to change paths or conventions later.

High-Level Responsibilities

  • Resolve paths under the NAS root (NAS_ROOT_PATH).
  • Provide high-level, intention-revealing helpers:

    • listBranches()['NL01', 'NL02', ...]
    • listYears(branch)['2023', '2024', ...]
    • listMonths(branch, year)['01', '02', ...]
    • listDays(branch, year, month)['01', '02', ...]
    • listFiles(branch, year, month, day)[{ name, relativePath }, ...]
  • Enforce read-only access from the filesystem (no delete/move/write logic here).

  • Use asynchronous filesystem APIs (fs/promises) to avoid blocking the event loop when reading from a network filesystem (SMB). Using async I/O is a recommended best practice in Node.js for scalability and performance.

Environment Configuration

The storage module depends on a single environment variable:

  • NAS_ROOT_PATH

Absolute path where the NAS share is mounted on the host.

Typical values:

  • Production (Linux server):

    NAS_ROOT_PATH=/mnt/niederlassungen
    
  • Local development (optional):

    # Example: local test folder
    NAS_ROOT_PATH=/Users/<username>/dev/test/niederlassungen
    

or, if the NAS is mounted locally (e.g. on macOS):

  NAS_ROOT_PATH=/Volumes/Niederlassungen

If NAS_ROOT_PATH is not set, the helpers will throw when called. This is intentional: configuration issues should fail fast instead of causing confusing downstream errors.

Directory Layout Assumptions

The helpers assume the following structure under NAS_ROOT_PATH:

NAS_ROOT_PATH/
  @Recently-Snapshot/   # ignored
  NL01/
    2024/
      10/
        23/
          file1.pdf
          file2.pdf
  NL02/
    2023/
      12/
        01/
          ...
  ...

Rules:

  • Branch directories follow the pattern NL<Number>, e.g. NL01, NL23.
  • Year directories are 4-digit numeric (2023, 2024, ...).
  • Month and day directories are numeric; the helpers normalize them to two‑digit strings for consistent display in the UI:

    • Months: "01""12"
    • Days: "01""31"
  • Only .pdf files are returned by listFiles.

If the on-disk structure changes, update the logic in lib/storage only. API routes and UI components should not need to know about the exact layout.

Helper Functions

All helper functions are asynchronous and return Promises.

listBranches(): Promise<string[]>

Returns the list of branch directories (NLxx) under NAS_ROOT_PATH.

  • Ignores @Recently-Snapshot.
  • Filters for names matching ^NL\d+$ (case-insensitive).
  • Sorts branches numerically by their suffix (NL1, NL2, …, NL10).

Example result:

["NL01", "NL02", "NL03"]

listYears(branch: string): Promise<string[]>

Reads the year directories for a given branch.

  • Path: ${NAS_ROOT_PATH}/${branch}
  • Filters for directories matching ^\d{4}$.
  • Returns sorted year strings as ['2023', '2024', ...].

listMonths(branch: string, year: string): Promise<string[]>

Reads the month directories for the given branch and year.

  • Path: ${NAS_ROOT_PATH}/${branch}/${year}
  • Filters for directories matching ^\d{1,2}$.
  • Normalizes month names to two digits (e.g. '1' → '01').
  • Returns sorted month strings.

Example result:

["01", "02", "03", "10"]

listDays(branch: string, year: string, month: string): Promise<string[]>

Reads the day directories for the given branch, year, and month.

  • Path: ${NAS_ROOT_PATH}/${branch}/${year}/${month}.
  • Filters for directories matching ^\d{1,2}$.
  • Normalizes day names to two digits (e.g. '3' → '03').
  • Returns sorted day strings.

Example result:

["01", "02", "03", "23"]

listFiles(branch: string, year: string, month: string, day: string): Promise<{ name: string; relativePath: string }[]>

Reads all PDF files for the given branch, year, month, and day.

  • Path: ${NAS_ROOT_PATH}/${branch}/${year}/${month}/${day}.
  • Filters for files whose names end with .pdf (case-insensitive).
  • Sorts filenames alphabetically.
  • Returns an array of objects with:

    • name: the raw filename (e.g. "Stapel-1_Seiten-1_Zeit-1048.pdf").
    • relativePath: the relative path from NAS_ROOT_PATH (e.g. "NL01/2024/10/23/Stapel-1_Seiten-1_Zeit-1048.pdf").

Example result:

[
	{
		"name": "Stapel-1_Seiten-1_Zeit-1048.pdf",
		"relativePath": "NL01/2024/10/23/Stapel-1_Seiten-1_Zeit-1048.pdf"
	},
	{
		"name": "Stapel-1_Seiten-2_Zeit-1032.pdf",
		"relativePath": "NL01/2024/10/23/Stapel-1_Seiten-2_Zeit-1032.pdf"
	}
]

Error Handling

lib/storage does not swallow errors:

  • If a folder does not exist or is not accessible, the underlying fs.promises.readdir call will throw (e.g. ENOENT, EACCES).
  • Callers (API routes, services) are responsible for catching these errors and converting them into appropriate HTTP responses.

This separation keeps responsibilities clear:

  • lib/storageHow do we read data from the filesystem?
  • API layer (app/api/.../route.js) → How do we map errors to HTTP responses?

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.

Note: Authentication and authorization are not implemented yet. In the final system, branch users should only see their own branch, while admins can access all branches.

General Conventions

  • All endpoints return JSON.

  • Successful responses use HTTP status 200.

  • Error responses use 4xx or 5xx and have the shape:

    { "error": "Human-readable error message" }
    
  • Route Handlers are implemented in app/api/.../route.js using the standard Web Request / Response primitives as described in the Next.js documentation.

  • Filesystem access must use lib/storage (no direct fs calls inside route handlers).


Health Check

GET /api/health

Purpose

Check whether:

  • The database is reachable.
  • The NAS root path (NAS_ROOT_PATH) is readable from the app container.

Response 200 (example)

{
	"db": "OK",
	"nas": {
		"path": "/mnt/niederlassungen",
		"entriesSample": ["@Recently-Snapshot", "NL01", "NL02", "NL03", "NL04"]
	}
}

Error cases

  • If the database is not reachable, the db field contains an error message.
  • If the NAS path cannot be read, the nas field contains an error string, e.g. "error: ENOENT: no such file or directory, scandir '/mnt/niederlassungen'".

This endpoint is intended for operations/monitoring and quick manual checks.


Delivery Notes Hierarchy

The following endpoints reflect the filesystem hierarchy:

NAS_ROOT_PATH → Branch → Year → Month → Day → PDF files

GET /api/branches

List all branch directories based on the names under NAS_ROOT_PATH.

Response 200

{
	"branches": ["NL01", "NL02", "NL03"]
}

Errors

  • 500 – Internal error (e.g. filesystem error, missing NAS_ROOT_PATH).

GET /api/branches/[branch]/years

Example: /api/branches/NL01/years

Return all year folders for a given branch.

Response 200

{
	"branch": "NL01",
	"years": ["2023", "2024"]
}

Errors

  • 400branch parameter is missing (indicates a route/handler bug).
  • 500 – Error while reading year directories.

GET /api/branches/[branch]/[year]/months

Example: /api/branches/NL01/2024/months

Return all month folders for the given branch and year.

Response 200

{
	"branch": "NL01",
	"year": "2024",
	"months": ["01", "02", "03", "10"]
}

Notes

  • Months are returned as two‑digit strings ("01""12") so that UI code does not need to handle formatting.

Errors

  • 400branch or year parameter is missing.
  • 500 – Filesystem or configuration error.

GET /api/branches/[branch]/[year]/[month]/days

Example: /api/NL01/2024/10/days

Return all day folders for the given branch, year, and month.

Response 200

{
	"branch": "NL01",
	"year": "2024",
	"month": "10",
	"days": ["01", "02", "03", "23"]
}

Notes

  • Days are returned as two‑digit strings ("01""31").

Errors

  • 400branch, year, or month parameter is missing.
  • 500 – Filesystem or configuration error.

GET /api/files?branch=&year=&month=&day=

Example:

/api/files?branch=NL01&year=2024&month=10&day=23

Return the list of PDF files for a specific branch and date.

Query parameters

  • branch – branch identifier (e.g. NL01).
  • year – four‑digit year (e.g. 2024).
  • month – month (e.g. 10).
  • day – day (e.g. 23).

Response 200

{
	"branch": "NL01",
	"year": "2024",
	"month": "10",
	"day": "23",
	"files": [
		{
			"name": "Stapel-1_Seiten-1_Zeit-1048.pdf",
			"relativePath": "NL01/2024/10/23/Stapel-1_Seiten-1_Zeit-1048.pdf"
		},
		{
			"name": "Stapel-1_Seiten-2_Zeit-1032.pdf",
			"relativePath": "NL01/2024/10/23/Stapel-1_Seiten-2_Zeit-1032.pdf"
		}
	]
}

Errors

  • 400 – one or more required query parameters are missing.
  • 500 – filesystem error while reading the day directory or files.

Adding New Endpoints

When adding new endpoints:

  1. Define the URL and method first, e.g.:

    • GET /api/file?path=... (download a single PDF)
    • GET /api/search?branch=&query=... (full‑text search via Qsirch)
  2. Create a route.js file in app/api/... following Next.js 16 Route Handler conventions. For dynamic routes, use the (request, ctx) signature and resolve parameters via const params = await ctx.params.

  3. Use lib/storage for filesystem access instead of calling fs directly inside route handlers. If needed, add new helpers to lib/storage.

  4. Handle errors explicitly with try/catch in the handler and return 4xx/5xx responses with clear error messages.

  5. Update this document to describe the new endpoint (URL, purpose, parameters, sample responses, error cases).


Future Extensions

  • Authentication & Authorization

    • Enforce branch‑level access control (branch user vs. admin).
    • Likely implemented using JWT stored in cookies and a shared helper (e.g. lib/auth) plus a middleware.js or per‑route checks.
  • Search Endpoints (Qsirch)

    • Integrate with QNAP Qsirch via its HTTP API.
    • Provide endpoints like GET /api/search?branch=&query=&from=&to=.
  • File Download / Preview

    • Add endpoints for streaming PDF content from the NAS to the browser with appropriate Content-Type and Content-Disposition headers.