# 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: ```bash cp .env.docker.example .env.docker ``` Then edit `.env.docker`: - Set a strong `SESSION_SECRET` (>= 32 characters) - For local HTTP testing add: ```env SESSION_COOKIE_SECURE=false ``` ### 2.3 Create local NAS fixtures Create a minimal NAS tree: ```bash 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: ```bash 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 ```bash 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): ```bash node -e "const bcrypt=require('bcryptjs'); console.log(bcrypt.hashSync('secret-password', 10))" ``` Open Mongo shell inside the DB container: ```bash docker exec -it rhl-lieferscheine-db mongosh -u root -p supersecret --authenticationDatabase admin ``` In `mongosh`: ```js use rhl-lieferscheine db.users.insertOne({ username: "branchuser", email: "nl01@example.com", passwordHash: "", role: "branch", branchId: "NL01", createdAt: new Date(), updatedAt: new Date() }) ``` ### 2.7 Login + call protected endpoints (curl) Login (stores cookie in `cookies.txt`): ```bash 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: ```bash 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): ```bash curl -i -b cookies.txt http://localhost:3000/api/branches/NL02/years ``` Logout: ```bash 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: ```bash node scripts/manual-api-client-flow.mjs \ --baseUrl=http://localhost:3000 \ --username= \ --password= \ --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= \ --password= \ --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: ```bash MSYS_NO_PATHCONV=1 docker exec -it rhl-lieferscheine-app ls -la /mnt/niederlassungen ``` --- ## 3. Server Deployment (internal server) ### 3.1 Connect via SSH ```bash 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: ```bash 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: ```env SESSION_COOKIE_SECURE=false ``` This is required because most clients will not send `Secure` cookies over HTTP. > If the application is served over **plain HTTP** (no TLS), many clients will not send `Secure` cookies back. > In that case, logins will appear to “work” (Set-Cookie is present), but subsequent requests will still be unauthenticated. ### 3.4 Start the stack on the server Use the base compose file only (no local override): ```bash 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: > > ```bash > docker compose up -d --build > ``` Optional (recommended): validate env inside the container after updating env files: ```bash docker compose exec app node scripts/validate-env.mjs ``` ### 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 Tail app logs: ```bash docker compose -f docker-compose.yml logs --tail=200 app ``` Check container status: ```bash docker compose ps ``` Common healthy state: - `app` is `Up` - `db` is `Up (healthy)` > If you see the Node warning `MODULE_TYPELESS_PACKAGE_JSON`, it is currently expected and does not break runtime. --- ## 4. HTTPS Note (Future) For real users, the application should be served over **HTTPS** (reverse proxy / TLS termination). ### 4.1 Why HTTPS matters here - The session cookie (`auth_session`) is typically `Secure` in production. - Browsers and many clients will **not send Secure cookies over HTTP**. - Without HTTPS you may see: - Login response returns `200` and `Set-Cookie` appears - But subsequent API calls act unauthenticated (`/api/auth/me` returns `{ user: null }`) ### 4.2 Recommended setup Run the Next.js container behind a reverse proxy that terminates TLS. Common internal options: - Nginx - Caddy - Traefik Key requirements: - External URL is HTTPS - Proxy forwards requests to the app container (typically `http://127.0.0.1:3000`) ### 4.3 Cookie configuration rules - Preferred production behavior: - `NODE_ENV=production` - `SESSION_COOKIE_SECURE` unset (or `true`) - Temporary plain HTTP (not recommended): - set `SESSION_COOKIE_SECURE=false` > Security note: Disabling Secure cookies on real networks is a risk. > Use it only as a temporary workaround while HTTPS is being introduced. ### 4.4 Operational tip: host consistency for cookies Cookies are scoped to the **host**. - If you authenticate against `http://localhost:3000`, open links (including PDFs) on `http://localhost:3000`. - If you authenticate against `http://127.0.0.1:3000`, also use that host. Mixing hosts can look like “random 401s” because the browser will not send the cookie to a different host. --- ## 5. Appendix: Quick smoke checklist ### 5.1 Local ```bash docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build curl -s http://localhost:3000/api/health npx vitest run npm run build ``` ### 5.2 Server ```bash docker compose up -d --build curl -s http://127.0.0.1:3000/api/health docker compose exec app node scripts/validate-env.mjs ```