et-lager — Operations Workbook

Warehouse management system for Ecotrans transport company.

Audited: 2026-07-03 | GitLab: gitlab.com/pinbox24/et-lager | Vercel: et-lager.vercel.app


Architecture

et-lager is a Next.js 15 internal dashboard that interfaces with the Pinbox24 w4 REST API and Intercars B2B invoice API. It acts as a thin BFF (Backend For Frontend) — almost all Next.js API routes are authenticated proxies that forward the user’s Pinbox24 JWT to the upstream APIs.

Browser
  └── Next.js (Vercel)
        ├── /api/auth/sign-in  →  POST https://api.w4.pinbox24.com/api/auth
        ├── /api/protocols/*   →  Pinbox24 protocol/warehouse endpoints
        ├── /api/invoices/*    →  Pinbox24 invoice endpoints
        ├── /api/stock/*       →  Pinbox24 stock/register endpoints
        ├── /api/workspace/*   →  Pinbox24 workspace-scoped variants
        └── /api/public/intercars-proxy  →  https://api.webapi.intercars.eu
              (unauthenticated — see Known Issues)

Persistent storage (secondary): MongoDB Atlas cluster nextjs-db-dev.ncgd33w.mongodb.net — stores processed invoice state, protocol positions, missing product tracking.

Functional Domains

DomainDescription
Warehouse Release ProtocolsCreate/process outgoing stock movements (pType=OUT) — multi-step: create → add items → review → release
Invoice ManagementView, filter, process, close invoices from Pinbox24 and Intercars; PDF download
Invoice ProtocolsLink invoices to warehouse protocols
Stock / Product LookupSearch by SKU, name, EAN against Pinbox24 register
Missing ProductsTrack products not found in stock
Workspace Access ControlGates features by user workspace memberships from Pinbox24 profile API

Developer Docs in GitLab (reference)

The et-lager repo contains AI-generated implementation notes. High-value references:

FileContent
API_INVENTORY.mdComplete inventory of 47+ API endpoints with method and purpose — best onboarding reference
PINBOX24_API_DOCUMENTATION.mdPinbox24 service function usage examples
WAREHOUSE_RELEASE_SYSTEM_DOCUMENTATION.mdFull architecture of warehouse release workflow
TOKEN_EXPIRATION_IMPLEMENTATION.mdAuth token lifecycle and known gaps
WORKSPACE_ACCESS_IMPLEMENTATION.mdWorkspace-based access control
INTERCARS_API_DOCUMENTATION.mdIntercars service usage
README-Docker.mdDocker setup guide

Note: README.md and doc.md are the unmodified NextAdmin template README — ignore them.


Tech Stack

LayerTechnology
FrameworkNext.js 15 (package.json says ^16.0.7 — Next.js 16 does not exist; resolves to 15.x)
React19.0.0
StylingTailwind CSS 3.4.16
AuthPinbox24 JWT (MD5 password hash, 8h TTL) — no Supabase, no NextAuth
DatabaseMongoDB Atlas (nextjs-db-dev.ncgd33w.mongodb.net)
External APIsPinbox24 w4 API + Intercars B2B API (OAuth2 client credentials)
Base templateNextAdmin free v1.2.1

Config Management

Vercel Project

PropertyValue
Project nameet-lager
Project IDprj_Ucdch6SHP4ZSsqBhsK2Jf2rg2AwX
Teamdevp24coms-projects (team_RuXLTWx5DXK6n0HUKoOUTGY3)
Production URLhttps://et-lager.vercel.app
Git sourcegitlab.com/pinbox24/et-lager (branch: master)
Node version24.x
Last deploy2026-04-30

Current Vercel env vars: MONGODB_URI configured (added 2026-07-05, #2703). Intercars INTERCARS_CLIENT_ID_SECRET remains hardcoded in source pending rotation (#2852) — see Known Issues.

GitLab Source

PropertyValue
URLhttps://gitlab.com/pinbox24/et-lager
Namespacepinbox24
Project ID77031715
Branchmaster
Access tokenGITLAB_ADMIN_PAT in secrets/administration.env.sops
CI/CDNone — no .gitlab-ci.yml

Deployment Process

There is no automated deployment pipeline. Deployments are triggered manually by pushing to master — Vercel auto-deploys on every push via the GitLab webhook. To trigger a deploy:

# Push from a local clone of gitlab.com/pinbox24/et-lager
git push origin master

To force a redeploy without code changes (e.g., after adding Vercel env vars):

npx vercel redeploy --scope devp24coms-projects et-lager

Required Environment Variables

These must be set in Vercel to replace the hardcoded values in source (see Known Issues):

VariablePurposeSource
MONGODB_URIMongoDB Atlas connection stringRotate from committed value in src/app/api/public/mongodb.ts
MONGODB_DBDatabase nameAudit code — currently defaults incorrectly in src/lib/mongodb.ts
INTERCARS_CLIENT_ID_SECRETBase64-encoded Intercars OAuth client credentialsRotate from src/constants/intercars.constants.ts

To add env vars to Vercel:

$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$env:V_TOK = (sops --decrypt --input-type dotenv --output-type dotenv "C:\code_2026\p24-infra\secrets\monitoring.env.sops" | Select-String "^VERCEL_TOKEN=").ToString().Split("=",2)[1]
# Use Vercel dashboard or CLI: npx vercel env add MONGODB_URI --scope devp24coms-projects
$env:V_TOK = ""

Backup

DataLocationBackup
Source codegitlab.com/pinbox24/et-lagerGit history; can be cloned locally
Processed invoice stateMongoDB Atlas nextjs-db-devNightly mongodump → Wasabi s3://p24-infra/et-lager/ (14-day retention). See below.
Vercel build artifactsVercel stores last N deploymentsRollback via vercel rollback or Vercel dashboard
Env varsCurrently none in VercelAfter migration: document in appropriate SOPS file

MongoDB Atlas — nightly external backup (#5752)

The Atlas cluster is on the free (M0) tier, which has automated backup / point-in-time restore disabled. Instead of upgrading to M10+ (the paid PITR tier), the interim mechanism is a nightly mongodump copied off-cluster to Wasabi, following the Error Notification Standard.

AttributeValue
Scriptscripts/et-lager-mongodb-backup.sh
Runs onvps-i1 (hosts the other application-data backup crons)
Schedule30 2 * * * (02:30 UTC nightly), root cron
Targets3://p24-infra/et-lager/et-lager-mongodump-YYYY-MM-DD.tar.gz
Retention14 daily dumps — objects older than 14 days under et-lager/ are pruned each run
RPO≤ 24 h (last nightly dump)
RTO~15–30 min (download + mongorestore)
AuthET_LAGER_MONGODB_BACKUP_URI — a read-only Atlas DB user (not the app’s read-write user), stored in secrets/monitoring.env.sops
On failureDiscord webhook (P24_DISCORD_INFRA_SCRIPTS_ERRORS_WEBHOOK_URL) + GH bug issue in radieu/p24-infra

Cron install (vps-i1, root):

# PLAYBOOK: server-operation.md
echo '30 2 * * *   /opt/p24-infra/scripts/et-lager-mongodb-backup.sh >> /var/log/et-lager-mongodb-backup.log 2>&1' \
  > /etc/cron.d/et-lager-mongodb-backup

Mitigation context: the Pinbox24 w4 API is the source of truth for all warehouse/invoice data; only the locally-tracked processing state (processed-invoice flags, protocol position overrides, missing-product tracking) lives solely in Atlas, and that is exactly what this dump protects.


Restore

Scenario 1: Bad Vercel deployment — rollback:

npx vercel rollback --scope devp24coms-projects
# Or: Vercel dashboard → et-lager → Deployments → select previous → Promote to Production

Scenario 2: et-lager unreachable (Vercel outage or build error):

  1. Check Vercel status: npx vercel inspect https://et-lager.vercel.app --scope devp24coms-projects
  2. Check latest deployment logs: Vercel dashboard → et-lager → Deployments → latest
  3. If build failed: fix code in GitLab and push to master

Scenario 3: MongoDB Atlas cluster lost or data corrupted:

  1. Pinbox24 API data is unaffected — warehouse/invoice records survive

  2. Lost: processed invoice status flags, protocol position overrides, missing product tracking

  3. Preferred recovery — restore the latest nightly dump (RPO ≤ 24 h, from #5752):

    # On vps-i1 (or any host with the Wasabi backup keys + mongorestore):
    DATE=$(date -u +%Y-%m-%d)   # or an earlier date within the 14-day window
    AWS_ACCESS_KEY_ID="$WASABI_ACCESS_KEY" AWS_SECRET_ACCESS_KEY="$WASABI_SECRET_KEY" \
      aws --endpoint-url https://s3.eu-central-2.wasabisys.com --region eu-central-2 \
      s3 cp "s3://p24-infra/et-lager/et-lager-mongodump-${DATE}.tar.gz" /tmp/
    mkdir -p /tmp/et-lager-restore && tar xzf "/tmp/et-lager-mongodump-${DATE}.tar.gz" -C /tmp/et-lager-restore
    # Restore into the Atlas cluster with a read-WRITE user URI (NOT the read-only backup user):
    mongorestore --uri "$ET_LAGER_MONGODB_RESTORE_URI" /tmp/et-lager-restore

    List available dumps: aws ... s3 ls s3://p24-infra/et-lager/ (14 daily retained).

  4. Fallback (if no usable dump): re-register processed invoices by re-processing them via the et-lager UI — the Pinbox24 API still holds the underlying records.

  5. Longer term: enable native Atlas backup (requires M10 cluster upgrade).

Scenario 4: Pinbox24 w4 API down:

et-lager has no offline mode. All pages requiring data will fail to load. No action possible on the et-lager side — escalate to Pinbox24 / check api.w4.pinbox24.com availability:

curl -s -o /dev/null -w "%{http_code}" https://api.w4.pinbox24.com/api/i18n/langs
# 200 = API up; 5xx/timeout = down

Healthcheck / Monitoring

Prometheus blackbox probes (added 2026-07-03):

  • https://et-lager.vercel.app/auth/sign-in — HTTP 2xx probe, 30s interval (blackbox job)
  • https://et-lager.vercel.app — SSL cert expiry probe, 5m interval (blackbox_ssl job)

Alerts: EndpointDown fires within 2 min of sign-in page returning non-2xx. CertExpiringSoon/CertExpiringCritical fire when Vercel TLS cert is near expiry (Vercel auto-renews; alert would indicate a renewal failure).

Manual health check:

curl -s -o /dev/null -w "%{http_code}" https://et-lager.vercel.app/auth/sign-in
# Expected: 200

Vercel deployment state:

npx vercel inspect https://et-lager.vercel.app --scope devp24coms-projects
# Check: status = ● Ready

Password / Credential Rotation

Pinbox24 Credentials (user accounts)

et-lager uses individual Pinbox24 user accounts — each operator logs in with their own Pinbox24 email+password. There are no shared service credentials for the Pinbox24 API in et-lager’s server-side code (the JWT is the user’s own token, forwarded from client to Pinbox24 API).

Rotation: handled by each user changing their Pinbox24 password. After password change, re-login at https://et-lager.vercel.app/auth/sign-in.

MongoDB Atlas Credentials ✅

Status: RESOLVED (2026-07-05, GitLab MR !1 — #2703, closed COMPLETED).

The previously-hardcoded connection string in src/app/api/public/mongodb.ts was replaced with process.env.MONGODB_URI, the MongoDB Atlas password for user radieu was rotated, and MONGODB_URI was added to the Vercel et-lager project env and stored in secrets/et-lager.env.sops (6 SOPS recipients, auto-synced to Vercel via secrets-sync.yml sync-et-lager). Source: CHANGELOG.md (2026-07-05 backfill entry + lines ~275; GitLab MR !1 merged). The rotation is recorded in the Supabase dev_r_rotation_log audit table (historical backfill, #5754).

Routine rotation going forward — Tier 3 / manual, no Atlas rotation API (see docs/playbooks/secret-rotation-access-matrix.md and docs/sops-templates/et-lager.keys):

  1. MongoDB Atlas dashboard → Database Access → edit user radieu → change password
  2. Rebuild the mongodb+srv:// URI with the new password
  3. Update MONGODB_URI in secrets/et-lager.env.sops (secret-manager role — never edit inline)
  4. Merge — secrets-sync.yml sync-et-lager pushes the new value to the Vercel et-lager project env
  5. Redeploy et-lager (npx vercel redeploy --scope devp24coms-projects et-lager) so a running deployment picks up the new value

Intercars OAuth Credentials ⚠️

Status: hardcoded in src/constants/intercars.constants.ts

The Intercars B2B CLIENT_ID_SECRET (Base64 client credentials) are stored in a committed TypeScript file.

Rotation:

  1. Intercars B2B partner portal — rotate client credentials
  2. Update src/constants/intercars.constants.ts to read from process.env.INTERCARS_CLIENT_ID_SECRET
  3. Add INTERCARS_CLIENT_ID_SECRET to Vercel env vars
  4. Push to master

Known Issues

P0 — Critical Security

IssueFileStatus
MongoDB credentials hardcoded in committed codesrc/app/api/public/mongodb.ts:9✅ Fixed 2026-07-05 (GitLab MR !1, #2703 closed) — replaced with process.env.MONGODB_URI; Atlas password for user radieu rotated; MONGODB_URI in Vercel env + secrets/et-lager.env.sops. See CHANGELOG.md.
Open unauthenticated SSRF proxy (no auth, no URL allowlist)src/app/api/public/intercars-proxy/route.ts✅ Fixed 2026-07-05 (GitLab MR !1, #2704 closed) — Intercars-only domain allowlist + method (GET/POST) + header filtering added. See CHANGELOG.md.
Intercars OAuth client credentials hardcodedsrc/constants/intercars.constants.ts:20 (INTERCARS_AUTH.CLIENT_ID_SECRET)Open — confirmed still hardcoded via live GitLab fetch 2026-08-06. Blocked on human rotation at Intercars partner portal (Tier 3, no self-service path) — see #2852. Env var name once available: INTERCARS_CLIENT_ID_SECRET.

P1 — High Risk

IssueDetail
No token refreshPinbox24 JWT expires ~8h; users lose in-progress work on expiry. Acknowledged in TOKEN_EXPIRATION_IMPLEMENTATION.md.
Cookie/header mismatch in middlewareSign-in sets cookie named session; middleware reads cookie authToken. Cookie-based auth in middleware never works — only header-based.
Build script broken on Linuxpackage.json build script uses set NODE_OPTIONS=... (Windows CMD). The Dockerfile sets ENV NODE_OPTIONS=... as a workaround, but npm run build directly would fail in Docker/CI.
next: "^16.0.7" in package.jsonNext.js 16 does not exist; resolves to 15.x. Lockfile deletion would upgrade unexpectedly.

P2 — Moderate Risk

IssueDetail
No CI/CD pipelineNo .gitlab-ci.yml; no automated testing, linting, or type-check before deploy
No .env.exampleNew developer has no reference for required env vars
Workspace route duplication/api/workspace/[workspaceId]/ duplicates non-workspace routes — bug fixes need to be applied in two places
Two MongoDB connection filessrc/app/api/public/mongodb.ts (hardcoded URI) vs src/lib/mongodb.ts (env var). Unclear which routes use which.
moment + dayjs both included~67KB gzipped overhead; moment is legacy — should consolidate to dayjs
Debug console.log in productionSeveral service files log request bodies to stdout (e.g., pinbox24.service.ts: "creting procotol with requestBody")
JWT not cryptographically verified/api/auth/validate only checks exp claim — tampered token with future expiry passes. Pinbox24 API rejects it, but et-lager-internal logic is spoofable.
Stub npm packageschild_process: "^1.0.2" and net: "^1.0.2" are no-op stub packages listed in dependencies

Compliance Status

ItemStatusNotes
Ops doc✅ yesThis file
Monitoring✅ yesBlackbox probe in Prometheus (added 2026-07-03)
Backup✅ yesVercel rollback + nightly MongoDB Atlas mongodump → Wasabi (14-day retention, #5752). Pending: cron deployment on vps-i1 + read-only Atlas backup user in SOPS (see Backup section).
Restore playbook✅ yesSee Restore section above
Healthcheck✅ yes/auth/sign-in HTTP + SSL cert probes
Credential rotation⚠️ partialMONGODB_URI migrated to env + Atlas password rotated (2026-07-05, MR !1, #2703). Intercars INTERCARS_CLIENT_ID_SECRET still hardcoded — blocked on human rotation at Intercars partner portal (#2852).
dev_r_services✅ registeredUpdated 2026-07-03
AI-poweredNoNo LLM calls in this application

GH Issues

IssueSeverityStatusTitle
#2703P0✅ Closed (COMPLETED)[Infra] et-lager — hardcoded MongoDB credentials committed to GitLab master
#2704P0✅ Closed (COMPLETED)[Infra] et-lager — unauthenticated SSRF proxy at /api/public/intercars-proxy
#2852P0⏳ Opensec(et-lager): INTERCARS_CLIENT_SECRET hardcoded in source — rotate + move to env var