runbook.md 8.4 KB

Runbook: Local Development vs Server Deployment

This runbook describes how to run the project locally (developer machine) and on the internal server.

The goal is a clean separation between:

  • Local development (uses local NAS fixtures)
  • Server deployment (uses the real NAS mount at /mnt/niederlassungen)

1. Repository Files and Separation

1.1 Compose files

  • docker-compose.yml

    • Base compose file (server-like)
    • Mounts the real NAS path: /mnt/niederlassungen:/mnt/niederlassungen:ro
  • docker-compose.local.yml

    • Local override
    • Mounts local fixtures: ./.local_nas:/mnt/niederlassungen:ro
  • docker-compose.server-tools.yml (optional)

    • Server-only tooling/override compose file.
    • Intended for additional server services or operational tooling.
    • Usually enabled on the server via COMPOSE_FILE or -f flags.

1.2 Env files

  • Committed templates:

    • .env.docker.example
    • .env.local.example
  • Local runtime env (not committed):

    • .env.docker
  • Server runtime env (not committed):

    • .env.server

The compose setup uses:

  • ENV_FILE to select which env file is loaded into the app container.

2. Local Development (Windows/macOS/Linux)

2.1 Prerequisites

  • Docker Desktop installed and running.
  • Project checked out.

2.2 Create local env file

Copy the template:

cp .env.docker.example .env.docker

Then edit .env.docker:

  • Set a strong SESSION_SECRET (>= 32 characters)
  • For local HTTP testing add:

    SESSION_COOKIE_SECURE=false
    

2.3 Create local NAS fixtures

Create a minimal NAS tree:

mkdir -p ./.local_nas/NL01/2024/10/23
printf "dummy" > ./.local_nas/NL01/2024/10/23/test.pdf

2.4 Start the stack (local)

Run with the local override:

docker compose -f docker-compose.yml -f docker-compose.local.yml up --build

If you prefer running in the background:

docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build

2.5 Verify health

curl -s http://localhost:3000/api/health

Expected (example):

  • db is ok
  • nas.entriesSample contains NL01

2.6 Create a test user in Mongo (manual)

Generate a bcrypt hash (local):

node -e "const bcrypt=require('bcryptjs'); console.log(bcrypt.hashSync('secret-password', 10))"

Open Mongo shell inside the DB container:

docker exec -it rhl-lieferscheine-db mongosh -u root -p supersecret --authenticationDatabase admin

In mongosh:

use rhl-lieferscheine

db.users.insertOne({
  username: "branchuser",
  email: "nl01@example.com",
  passwordHash: "<PASTE_HASH_HERE>",
  role: "branch",
  branchId: "NL01",
  createdAt: new Date(),
  updatedAt: new Date()
})

2.7 Login + call protected endpoints (curl)

Login (stores cookie in cookies.txt):

curl -i -c cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"username":"BranchUser","password":"secret-password"}' \
  http://localhost:3000/api/auth/login

Call endpoints with cookie:

curl -i -b cookies.txt http://localhost:3000/api/branches
curl -i -b cookies.txt http://localhost:3000/api/branches/NL01/years
curl -i -b cookies.txt http://localhost:3000/api/branches/NL01/2024/months
curl -i -b cookies.txt http://localhost:3000/api/branches/NL01/2024/10/days
curl -i -b cookies.txt "http://localhost:3000/api/files?branch=NL01&year=2024&month=10&day=23"

RBAC negative test (expected 403):

curl -i -b cookies.txt http://localhost:3000/api/branches/NL02/years

Logout:

curl -i -b cookies.txt -c cookies.txt http://localhost:3000/api/auth/logout

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:

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:

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.

If you need to run docker exec ... ls /mnt/niederlassungen, disable MSYS path conversion:

MSYS_NO_PATHCONV=1 docker exec -it rhl-lieferscheine-app ls -la /mnt/niederlassungen

3. Server Deployment (internal server)

3.1 Connect via SSH

ssh administrator@192.168.0.23

3.2 Prerequisites on the server

  • Docker and Docker Compose installed.

  • The real NAS share is mounted at:

    • /mnt/niederlassungen
  • The mount is readable by Docker.

3.3 Create server env file

On the server (in the project folder), create .env.server based on the template:

cp .env.docker.example .env.server

Edit .env.server:

  • Set a strong SESSION_SECRET.
  • Keep NODE_ENV=production.
  • If the app is served behind HTTPS (recommended): keep SESSION_COOKIE_SECURE unset (or true).

If the app is currently served over plain HTTP (no TLS), you may temporarily set:

SESSION_COOKIE_SECURE=false

This is required because most clients will not send Secure cookies over HTTP.

3.4 Start the stack on the server

Use the base compose file only (no local override):

ENV_FILE=.env.server docker compose -f docker-compose.yml up -d --build

If you configured a server-local .env file (see 3.4.1 / 3.4.2), you can use the short form:

> docker compose up -d --build
> ```

### 3.4.1 Optional: Persist ENV_FILE selection via `.env`

If you want a simpler startup command (and to avoid forgetting `ENV_FILE=...`), you can create a small `.env` file **on the server only** that defines which env file Compose should use.

Create `./.env` in the project root:

bash printf "ENV_FILE=.env.server\n" > .env


After that, you can start the stack with:

bash docker compose -f docker-compose.yml up -d --build


Notes:

- Keep `.env` server-local (do not commit it).
- `.env.server` still contains secrets and must not be committed.
- Always run `docker compose` from the project root so Compose picks up the correct `.env` file.

### 3.4.2 Optional: Persist ENV_FILE + COMPOSE_FILE selection via `.env`

If your server setup uses multiple compose files (e.g. base + server tooling), you can also persist the compose file selection in the same **server-local** `.env` file.

Example `./.env` (server only, do not commit):

env ENV_FILE=.env.server COMPOSE_FILE=docker-compose.yml:docker-compose.server-tools.yml


Notes:

- `COMPOSE_FILE` supports multiple files separated by `:` (common on Linux servers).
- Keep `.env` and `.env.server` server-local (do not commit).

After that, you can start/update the stack with:

bash docker compose up -d --build


Optional: verify which configuration Compose is actually using:

bash docker compose config


### 3.5 Verify

On the server:

bash curl -s http://127.0.0.1:3000/api/health


Expected:

- `db` is `ok`
- `nas.entriesSample` contains real branch folders (`NLxx`)

> 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= \ --password= \ --branch=NL01


### 3.7 Logs and troubleshooting

bash docker compose -f docker-compose.yml logs --tail=200 app ```


4. HTTPS Note (Future)

For real users, the application should be served over HTTPS (reverse proxy / TLS termination).

If HTTPS is enabl