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
| Domain | Description |
|---|---|
| Warehouse Release Protocols | Create/process outgoing stock movements (pType=OUT) — multi-step: create → add items → review → release |
| Invoice Management | View, filter, process, close invoices from Pinbox24 and Intercars; PDF download |
| Invoice Protocols | Link invoices to warehouse protocols |
| Stock / Product Lookup | Search by SKU, name, EAN against Pinbox24 register |
| Missing Products | Track products not found in stock |
| Workspace Access Control | Gates 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:
| File | Content |
|---|---|
API_INVENTORY.md | Complete inventory of 47+ API endpoints with method and purpose — best onboarding reference |
PINBOX24_API_DOCUMENTATION.md | Pinbox24 service function usage examples |
WAREHOUSE_RELEASE_SYSTEM_DOCUMENTATION.md | Full architecture of warehouse release workflow |
TOKEN_EXPIRATION_IMPLEMENTATION.md | Auth token lifecycle and known gaps |
WORKSPACE_ACCESS_IMPLEMENTATION.md | Workspace-based access control |
INTERCARS_API_DOCUMENTATION.md | Intercars service usage |
README-Docker.md | Docker setup guide |
Note: README.md and doc.md are the unmodified NextAdmin template README — ignore them.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (package.json says ^16.0.7 — Next.js 16 does not exist; resolves to 15.x) |
| React | 19.0.0 |
| Styling | Tailwind CSS 3.4.16 |
| Auth | Pinbox24 JWT (MD5 password hash, 8h TTL) — no Supabase, no NextAuth |
| Database | MongoDB Atlas (nextjs-db-dev.ncgd33w.mongodb.net) |
| External APIs | Pinbox24 w4 API + Intercars B2B API (OAuth2 client credentials) |
| Base template | NextAdmin free v1.2.1 |
Config Management
Vercel Project
| Property | Value |
|---|---|
| Project name | et-lager |
| Project ID | prj_Ucdch6SHP4ZSsqBhsK2Jf2rg2AwX |
| Team | devp24coms-projects (team_RuXLTWx5DXK6n0HUKoOUTGY3) |
| Production URL | https://et-lager.vercel.app |
| Git source | gitlab.com/pinbox24/et-lager (branch: master) |
| Node version | 24.x |
| Last deploy | 2026-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
| Property | Value |
|---|---|
| URL | https://gitlab.com/pinbox24/et-lager |
| Namespace | pinbox24 |
| Project ID | 77031715 |
| Branch | master |
| Access token | GITLAB_ADMIN_PAT in secrets/administration.env.sops |
| CI/CD | None — 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 masterTo force a redeploy without code changes (e.g., after adding Vercel env vars):
npx vercel redeploy --scope devp24coms-projects et-lagerRequired Environment Variables
These must be set in Vercel to replace the hardcoded values in source (see Known Issues):
| Variable | Purpose | Source |
|---|---|---|
MONGODB_URI | MongoDB Atlas connection string | Rotate from committed value in src/app/api/public/mongodb.ts |
MONGODB_DB | Database name | Audit code — currently defaults incorrectly in src/lib/mongodb.ts |
INTERCARS_CLIENT_ID_SECRET | Base64-encoded Intercars OAuth client credentials | Rotate 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
| Data | Location | Backup |
|---|---|---|
| Source code | gitlab.com/pinbox24/et-lager | Git history; can be cloned locally |
| Processed invoice state | MongoDB Atlas nextjs-db-dev | Nightly mongodump → Wasabi s3://p24-infra/et-lager/ (14-day retention). See below. |
| Vercel build artifacts | Vercel stores last N deployments | Rollback via vercel rollback or Vercel dashboard |
| Env vars | Currently none in Vercel | After 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.
| Attribute | Value |
|---|---|
| Script | scripts/et-lager-mongodb-backup.sh |
| Runs on | vps-i1 (hosts the other application-data backup crons) |
| Schedule | 30 2 * * * (02:30 UTC nightly), root cron |
| Target | s3://p24-infra/et-lager/et-lager-mongodump-YYYY-MM-DD.tar.gz |
| Retention | 14 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) |
| Auth | ET_LAGER_MONGODB_BACKUP_URI — a read-only Atlas DB user (not the app’s read-write user), stored in secrets/monitoring.env.sops |
| On failure | Discord 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-backupMitigation 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 ProductionScenario 2: et-lager unreachable (Vercel outage or build error):
- Check Vercel status:
npx vercel inspect https://et-lager.vercel.app --scope devp24coms-projects - Check latest deployment logs: Vercel dashboard → et-lager → Deployments → latest
- If build failed: fix code in GitLab and push to
master
Scenario 3: MongoDB Atlas cluster lost or data corrupted:
-
Pinbox24 API data is unaffected — warehouse/invoice records survive
-
Lost: processed invoice status flags, protocol position overrides, missing product tracking
-
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-restoreList available dumps:
aws ... s3 ls s3://p24-infra/et-lager/(14 daily retained). -
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.
-
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 = downHealthcheck / Monitoring
Prometheus blackbox probes (added 2026-07-03):
https://et-lager.vercel.app/auth/sign-in— HTTP 2xx probe, 30s interval (blackboxjob)https://et-lager.vercel.app— SSL cert expiry probe, 5m interval (blackbox_ssljob)
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: 200Vercel deployment state:
npx vercel inspect https://et-lager.vercel.app --scope devp24coms-projects
# Check: status = ● ReadyPassword / 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):
- MongoDB Atlas dashboard → Database Access → edit user
radieu→ change password - Rebuild the
mongodb+srv://URI with the new password - Update
MONGODB_URIinsecrets/et-lager.env.sops(secret-manager role — never edit inline) - Merge —
secrets-sync.ymlsync-et-lagerpushes the new value to the Vercelet-lagerproject env - 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:
- Intercars B2B partner portal — rotate client credentials
- Update
src/constants/intercars.constants.tsto read fromprocess.env.INTERCARS_CLIENT_ID_SECRET - Add
INTERCARS_CLIENT_ID_SECRETto Vercel env vars - Push to
master
Known Issues
P0 — Critical Security
| Issue | File | Status |
|---|---|---|
| MongoDB credentials hardcoded in committed code | src/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 hardcoded | src/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
| Issue | Detail |
|---|---|
| No token refresh | Pinbox24 JWT expires ~8h; users lose in-progress work on expiry. Acknowledged in TOKEN_EXPIRATION_IMPLEMENTATION.md. |
| Cookie/header mismatch in middleware | Sign-in sets cookie named session; middleware reads cookie authToken. Cookie-based auth in middleware never works — only header-based. |
| Build script broken on Linux | package.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.json | Next.js 16 does not exist; resolves to 15.x. Lockfile deletion would upgrade unexpectedly. |
P2 — Moderate Risk
| Issue | Detail |
|---|---|
| No CI/CD pipeline | No .gitlab-ci.yml; no automated testing, linting, or type-check before deploy |
No .env.example | New 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 files | src/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 production | Several 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 packages | child_process: "^1.0.2" and net: "^1.0.2" are no-op stub packages listed in dependencies |
Compliance Status
| Item | Status | Notes |
|---|---|---|
| Ops doc | ✅ yes | This file |
| Monitoring | ✅ yes | Blackbox probe in Prometheus (added 2026-07-03) |
| Backup | ✅ yes | Vercel 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 | ✅ yes | See Restore section above |
| Healthcheck | ✅ yes | /auth/sign-in HTTP + SSL cert probes |
| Credential rotation | ⚠️ partial | MONGODB_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 | ✅ registered | Updated 2026-07-03 |
| AI-powered | No | No LLM calls in this application |
GH Issues
| Issue | Severity | Status | Title |
|---|---|---|---|
| #2703 | P0 | ✅ Closed (COMPLETED) | [Infra] et-lager — hardcoded MongoDB credentials committed to GitLab master |
| #2704 | P0 | ✅ Closed (COMPLETED) | [Infra] et-lager — unauthenticated SSRF proxy at /api/public/intercars-proxy |
| #2852 | P0 | ⏳ Open | sec(et-lager): INTERCARS_CLIENT_SECRET hardcoded in source — rotate + move to env var |