Badge item_names already start with 'badge_' (e.g. badge_citycpa3),
so checking for badge_<item_name>_icon.png produces a double prefix
(badge_badge_citycpa3_icon.png). Now uses item_name.startsWith('badge_')
instead, which correctly identifies badge items without depending on
icon files existing in the directory.
Previously identified badge items by interaction_type='badge', but
clone-imported badges have interaction_type='default'. Now checks for
badge_<code>_icon.png file existence instead.
Badge icons are stored as badge_<code>_icon.png (with badge_ prefix)
instead of <code>_icon.png like regular furni. Update the audit and
icon repair to check for both patterns so badge items are not falsely
flagged as missing icons.
Badge items use .gif files, not .nitro bundles, so they should not
be flagged as missing .nitro or missing catalog entries. Also add
badge logicType handling in furni-import.ts so badges get the correct
interaction_type when imported.
Anonymous HTML was sent with 'public, max-age=60, s-maxage=300,
stale-while-revalidate=300'. After a rebuild the old chunk URLs (keyed by
deploy id) are deleted, so any browser/CDN holding the stale HTML got 404s
for up to five minutes. Since the deploy id is the git commit, the HTML must
be re-fetched after every deploy; only content-hashed static assets should
be cached. Return no-store for all HTML documents.
theme-init.js adds the 'dark' class to <html> before React hydrates,
causing a hydration mismatch (React #418) for users with a saved dark
theme. Mark the root element with suppressHydrationWarning.
- hashPassword now emits bcrypt (cost 12) instead of argon2id
- checkLogin migrates legacy md5/argon2id hashes to bcrypt on sign-in
- keep argon2id verification only as a one-time migration path
- replace ARGON2_* env vars with BCRYPT_COST
Previously allocateCatalogItemId was called per entry, but since generation
does not INSERT, MAX(id) never advanced and every entry got the same id.
Now MAX(id) is read once and a local counter hands out sequential ids.
- 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
Repair Icons now resolves all furni asset write targets (webroot plus
the live gamedata like /var/www/Gamedata) and writes downloaded or
extracted icons to every missing location. Icons already present in one
target are copied across instead of re-downloaded, and local .nitro
bundles are searched in every target.
Fall back to well-known public furni icon hosts (HabboAssets, Hubbly,
Leet) when no clone sources are configured, so Repair Icons can download
missing icons out of the box. Also handle variant classnames (base name
and '*' replaced with '_') and extract icons from remote .nitro bundles.
Send a browser User-Agent on downloads to avoid being blocked by hotels.
- Remove output: 'standalone' from next.config.ts to allow normal 'next start'
- Allow empty SENTRY_DSN/NEXT_PUBLIC_SENTRY_DSN in env validation (zod)
- Add 'unsafe-inline' to style-src CSP only in development for Turbopack HMR
- Clear placeholder Sentry DSN values from .env
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.
getLoginUser selected users.account_blocked, which does not exist in the
DB (nor the Drizzle schema). Every credentials authorize() call threw a
SQL error -> NextAuth CallbackRouteError -> 'error=Configuration', so no
login could ever succeed. Remove the phantom column from the query and
LoginUser interface.
Also fix all remaining biome noNonNullAssertion / noExplicitAny lint
warnings so CI's check job (biome:lint) passes and the push deploy runs.
- hashPassword now emits argon2id (same params as the legacy AtomCMS
Laravel setup: memory 64MB, iterations 4, parallelism 1)
- legacy md5 and bcrypt hashes are verified and auto-upgraded to
argon2id on successful login (CONVERT_PASSWORDS=true)
- replace BCRYPT_ROUNDS env with ARGON2_MEMORY_KB / ARGON2_ITERATIONS /
ARGON2_PARALLELISM
- update README and add tests for argon2id and bcrypt upgrade paths
Align grids on data.items, only treat SSE done as success, hard-fail
clothing sets when libs fail, and add Cancel via AbortController.
Co-authored-by: Cursor <[email protected]>
Split OnlineUsersWidget from StatusCard, decouple ad delete button, sync badge import to ExternalTexts+WebsiteBadges, add Import section hub with cancelable SSE jobs and upload SQL option.
Co-authored-by: Cursor <[email protected]>
Merged read-model inbox at /admin/tickets and /mod/tickets with type badges and deep links; CMS-only lists moved to /desk. No DB schema merge.
Co-authored-by: Cursor <[email protected]>