The Docker build was slower than it needed to be and could drift away from
the lockfile. Three separate causes, fixed together.
The image installed a pinned [email protected].1 while package.json pins 12.11.2
through `packageManager`. pnpm silently self-managed the difference at
build time: it downloaded the right version into an uncached location and
never verified the sha512 hash. Install corepack instead and let it resolve
version and hash from the pin, so the image can no longer diverge and a
tampered tarball is rejected. The resolved pnpm lives in COREPACK_HOME
inside the image, so the migrations image still runs pnpm offline
(`docker run --entrypoint pnpm ... db:migrate`).
The pnpm store cache-mount pointed at /root/.local/share/pnpm/store, but
pnpm keeps its store in $PNPM_HOME/store/v11. The mount was a no-op: every
build re-downloaded all 493 packages ("reused 0, downloaded 493"). Mounting
the store where pnpm actually keeps it turns that into "reused 493,
downloaded 0".
The build context carried backups/ (232MB of rotating database dumps) and
public/vendor/ (regenerated by `pnpm assets:editor` from node_modules).
Both grew the context and invalidated the `COPY . .` layer on every daily
backup rotation, forcing a full Next.js rebuild. Excluding them takes the
context from 276MB to 23MB.
Compose now also exports the build cache to .docker-cache/cms and imports it
again, so the cache survives `scripts/docker-prune.sh`, which trims the
daemon cache to 4GB after every deploy. Verified by pruning the entire
daemon cache: the next build restored every layer from the local export and
downloaded nothing.
Also: byparr's floating `:latest` tag is now pulled on every start, the
migrations build in scripts/docker-update.sh imports the same local cache,
the README no longer claims the build needs host networking or an
iptables-disabled daemon, the pnpm rationale in renovate.json reflects that
Docker follows the pin automatically, and contract tests pin the new
invariants so the store path cannot drift back.
Bundle the remaining imaging hardening and the host/CI fixes from the
"Doe alles" pass into one release.
Furniture:
- Resolve the local bundle extension from FURNI_BUNDLE_EXT (".hab" default,
".nitro" for a stock Nitro client). The configured extension is written by
imports/repairs and preferred on lookup; the other stays a legacy fallback.
Imaging:
- Avatar proxy sends s-maxage=86400 + stale-while-revalidate (300/3600 for the
fallback path) so the edge can hold a render a day.
- Cloudflare cache rule now covers /api/imaging/ (verified MISS->HIT live).
- Document that bare /imaging is shadowed by the edge proxy to the renderer.
Ops/CI:
- docker-update.sh skips a CI-managed host (exit 0) instead of failing cron.
- ci-deploy.sh runs the cross-browser imaging gate (chromium+firefox+webkit)
against the candidate before cutover; DEPLOY_SKIP_CROSS_BROWSER=1 as escape.
- scripts/imager-health-alert.sh alerts (Discord) on imager up/down transitions;
runs from cron every 5 minutes.
Tests:
- Furniture bundle-ext resolver + FURNI_BUNDLE_EXT wiring.
- Imaging performance budget (bytes + warm latency).
- classifyDdos: /api/imaging/* stays in the api bucket.
load-env.ts reads .env.local before .env and gives it precedence, so a
developer override file can hold real secrets. Only .env was ignored, so
that file was one 'git add .' away from being committed.
- Add e2e job (needs deploy, main/master only) that installs the
Playwright browser and smoke-tests the live container on :3002
- Keep deploy-job contract slice from bleeding into the e2e job
- Cover the e2e job in the CI workflow contract test
- Ignore Playwright output dirs (test-results, playwright-report,
blob-report)
- update-Nitrov3.sh: build CMS into .next-staging and swap atomically so a
failed build never takes the live site down; auto-merge new variables
from .env.example; validate env for duplicates/broken lines; restart the
emulator/CMS only when rebuilt or unhealthy; fix step renumbering
- next.config.ts: support NEXT_DIST_DIR for staged production builds
- fix all TS errors (unused imports, missing tryDownloadCandidates helper)
so tsc and the production build pass clean
- add Dockerfile/.dockerignore and switch docker-compose to a CMS container
- include prevailing UI/refactor changes (SurfaceCard, ticketing, tsconfig)
- Parallelize icon and nitro downloads in the audit repair with a
sliding-window worker pool (6 concurrent) to speed up large catalogs
- Add repairMissingNitros: fetch missing .nitro bundles from configured
nitro sources (Wibbo default), validating each bundle before writing
- Add catalog-repair service: generate/apply catalog_items SQL for furni
missing a catalog entry and repair FurnitureData.json (add missing +
dedupe classnames)
- Wire all options through the audit API and client UI with live progress
and result stats (icons, nitros, SQL, furnidata)
- Add Wibbo as default nitro source alongside existing icon sources
- Ignore runtime furni assets downloaded into public/ during repair
Move media storage from public/assets/images/media to storage/media
(outside Git and public/), so uploaded images, favicons and logos are
preserved across rebuilds and git clean. Add a shared media-storage helper,
update the upload/serve actions and API routes, and gitignore storage/.
The clothing importer and imager helpers pointed at a missing route; proxy Habbo with disk/memory cache so avatars work again.
Co-authored-by: Cursor <[email protected]>
- Remove production DB dump (db_backup_*.sql) and update.log from git tracking
- Add DB backups to .gitignore
- Replace all console.log/console.error with structured logger module
- Translate Dutch error messages to English (link-discord.ts)
- Remove dead code blocks (register-form.tsx false && pattern)
- Add ESLint flat config with TypeScript, React, Next.js, jsx-a11y, and security plugins
- Add Prettier config
- Add eslint-plugin-security for security-aware linting
- Fix all 119+ ESLint warnings across the codebase:
- Resolve security/detect-object-injection with safe access patterns
- Resolve security/detect-non-literal-fs-filename with path traversal validation
- Replace <img> with next/image <Image> component
- Remove unused variables and imports
- Replace non-null assertions with proper type guards
- Replace <a> with <Link> for internal navigation
- Use next/script Script component for external scripts
- Fix setState-in-useEffect anti-patterns (navbar-color-picker, logo-generator, theme-switcher)
- Add lint and format scripts to package.json
All checks: typecheck ✓, tests 58/58 ✓, lint 0 errors 0 warnings ✓
Minimal but real App Router app that builds (next build exit 0):
- src/lib/auth.ts: NextAuth v5 Credentials provider calling checkLogin()
(argon2id/bcrypt + md5->argon2id upgrade gated by CONVERT_PASSWORDS), JWT
session, /api/auth/[...nextauth] route handler.
- src/app: root layout, home (force-dynamic, reads hotel_name via siteSettings),
/login client form (signIn).
- next.config.ts: pinned turbopack.root, serverExternalPackages for the Prisma
MariaDB adapter; tsconfig set up for Next.
Routes: / (dynamic), /login, /api/auth. Verified: next build exit 0, 28 tests.
Still needs DB+APP_KEY to run auth end-to-end. i18n/middleware/pages to follow.