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.
The hotel moved from .nitro to .hab bundles, so the imager must treat .hab
like .nitro and the public avatar endpoints must accept the figure strings
the client sends (including extra trailing segments such as `.0`, which the
old FIGURE_RE rejected with a 400).
- imager-upstream: relax FIGURE_RE, extend parseAvatarQuery test coverage
- api/imaging/avatar + /imaging: shared validation, placeholder and ETag/304
- api/imaging/badge: single-flight upstream fetch and conditional responses
- imager-http/imager-png: strong ETag helpers and APNG passthrough guard
- csp + next/image: unoptimized external images, tightened CSP
- deps: override postcss-selector-parser to ^7.1.6
- also fold in the pending drizzle draft snapshot and stray test cleanup
Database:
- Add missing indexes (users.credits, users_currency(type,amount),
users_settings.respects_received, camera_web.timestamp,
messenger_offline.user_id) via migrations 0020/0021
- Use partial .select() everywhere instead of SELECT * (tickets, users,
rooms, audit logs, catalog tree, polls, radio, password reset)
- Add queryPrepared/queryPreparedOne (server-side prepared statements)
and switch the login check to a prepared statement; drop dead
cache options from the pool config
- Raise total_users/total_rooms COUNT(*) cache TTL to 5m
Caching:
- Consolidate the three cache helpers (cached, redisCache, cachedQuery)
into a single memory-first implementation backed by Redis
- invalidateKey now clears the in-process cache as well as Redis
- Cache homepage sections, news list, and leaderboard tabs; share one
news_list cache key between homepage and news archive
- siteSettings: in-process cache with TTL so repeated getters no longer
pay a Redis round-trip per call
- Share a 10s poll cache across all radio SSE connections
- Normalize timestamps after cache reads (Redis JSON round-trip)
Assets:
- Enable AVIF/WebP via images.formats and remove unoptimized from news
covers and the homepage hero (149KB jpg) with proper sizes/priority
- Support ?format=webp|avif|png in the /imaging proxy via sharp
Other:
- Fix pnpm supply-chain minimumReleaseAge failures by excluding the
freshly-published packages (next 16.3.1, hookform resolvers 5.8.0,
resend 6.20.0)
- Remove unused before/after fields from housekeeping AuditEntry
- Fix proxy routes to use resolveImagerBase instead of removed resolveUpstreamBase
- Fix avatarImageUrl type signature to use AvatarOptions
- Run biome formatter
- Update imager to use epicnabbo.nl by default with effect=14 and img_format=apng
- Add figure string converter (old Habbo format -> epicnabbo.nl short format)
- Update avatarImageUrl to auto-convert figure strings
- Update online-users-widget to use new avatarImageUrl
- Add tests for imager and figure conversion
Avatars are proxied from the slow Habbo upstream on every request
(~350ms each) and Cloudflare was serving them as DYNAMIC because the
Cache-Control had no s-maxage. Add s-maxage=86400 + stale-while-revalidate
so edge/CDN caches avatars and repeats are served instantly.
- Change default public imager URL from habbo.com to /imaging
- /imaging and /api/imaging/avatar now proxy from upstream (habbo.com) instead of redirecting
- Add resolveUpstreamBase() to separate public URL from upstream URL
- Update admin settings default and description