Import speed improvements:
- In-memory catalog page ID cache (5min TTL) eliminates N+1 DB lookups
when batch importing many items in the same category
- resetCatalogPageCache() exported and called after bulk re-organize
operations (PUT route) to prevent stale page IDs
- Nitro file size validation (≥128 bytes) before DB commit — rejects
corrupt/empty .nitro files that would break the client
- batchLookupByClassnames now uses Promise.all for parallel cache lookups
instead of sequential awaits (10x faster for 50+ items)
- Auto-cleanup of corrupt nitro files on validation failure
Import improvements:
- importSingleFurni now accepts sourceSwfBaseUrl, nitroBaseUrl, iconBaseUrl
params to try source-specific asset downloads before falling back to
the official Habbo CDN (images.habbo.com)
- Source-specific URL cascade:
1. Try sourceSwfBaseUrl/hof_furni/{rev}/{name}.swf
2. Try sourceNitroBaseUrl/{name}.nitro (pre-made nitro bundles)
3. Fallback to images.habbo.com/dcr/hof_furni/{rev}/{name}.swf
- Same fallback chain for icon downloads
- Clone sources can now have empty nitroBaseUrl/iconBaseUrl (retro
hotels without nitro support)
- Added VirtualCity source (verified SWF downloads via virtualc.nl/dcr)
- Added sourceSwfBaseUrl field to CloneSource interface
- Both batch and single import routes pass source params through
- Clone POST route accepts sourceSwfBaseUrl, makes nitro/icon optional
- Nitro fallback download tries .nitro files before SWF conversion
- Fix missing await in pets API route causing empty responses
- Fix updateSetting to use upsert pattern instead of update-only
- Create missing /api/admin/sounds/upload route (upload was broken)
- Wire bulk delete actions in catalog table
- Replace native confirm() with useConfirmDialog() across rooms and clone pages
- Add error logging to silent catch blocks in radio actions and audit route
- Add graceful degradation to devops health endpoint
- Add cache eviction to clone icon route to prevent memory leak
- Internationalize hardcoded Italian strings to English
- Remove placeholder created_at fields from prefix API responses
- Remove dead code and fix type errors in translations and import pages
- Standardize PERMS import path in analytics export route
- CloneSource interface now has a hotel field (maps to habbo_gamedata_hotel)
- DEFAULT_SOURCES presets include their associated hotel (it/com)
- When cloning from a source with a hotel configured, habbo_gamedata_hotel
is set automatically so official furnidata enrichment uses the correct locale
- Clone source form UI now has a hotel select dropdown
- Source list shows the hotel label when configured
- Clone API route passes hotel through to upsertSource
- import-badge.ts, badges route, and badges edit route now resolve
ExternalTexts.json via getGamedataRoot() instead of the hardcoded
public/nitro-assets/gamedata/ path
- writeBadgeToExternalTexts creates the file (and parent dirs) when
missing, so fresh deployments no longer fail with ENOENT
- upload-import SQL maker now classifies furni via classifyFurni and
generates correct category sub-pages (imp_<catKey>) instead of
hardcoded imp_other
Replace the serialized SELECT MAX + INSERT id-allocation chains for
items_base and catalog_items with a lazy-seeded in-process counter so
concurrent clone workers no longer queue on a global lock per item.
Re-seeds after 60s idle to avoid colliding with externally added rows.
Raise the clone import concurrency default from 6 to the batch cap of 10.
- Clone import: defer FurnitureData.json writes and append all entries in a
single batched write instead of one read-modify-write per item, removing
the main serialization bottleneck for large batches.
- Clone import: raise SSE batch concurrency cap from 5 to 10 and bump the
clone client/route default from 2 to 6.
- Add a flush hook to runSseBatch so callers can batch deferred work before
batch_complete is emitted, and surface flush errors as an error event.
- Enable Next.js Cache Components (instant: false opt-out) and silence the
related build warnings in next.config.ts.
- Switch isomorphic-dompurify to dompurify and refresh dependencies.
- Detect HTML responses from FlareSolverr and throw descriptive error
instead of letting JSON.parse fail with cryptic 'Unexpected token' error
- Add try/catch to clone/route.ts GET handler to return 502 with
clear message instead of Internal Server Error 500
- Add FLARESOLVERR_URL to .env
- 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
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.
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]>
- Fix noPrecisionLoss on BIGINT UNSIGNED max value (2^64-1) with biome-ignore comments
- Fix noThenProperty on custom thenable with biome-ignore comment
- Auto-format remaining files (biome check --write)
- Re-stage auto-fixed files from previous commit
- Replace Prisma client runtime with Drizzle ORM (zero Prisma engine/query engine in production)
- Add Prisma-compatible facade (@/lib/prisma-facade.ts) backed by Drizzle for backwards compatibility
- Runtime queries route through Drizzle ORM; @prisma/client is now devDependency (types only)
- Remove @prisma/adapter-mariadb dependency; delete prisma-pool.ts and types/prisma.ts
- New Drizzle schema layer: src/db/schema.ts (176 tables) and src/lib/db.ts (connection)
- Update README documenting the dual-layer ORM architecture
- Restore src/generated/ gitignore (build artifact for local type generation)
- 0 TypeScript errors, 583 tests passing
The facade intentionally uses `any` types to match the Prisma Client API surface,
allowing existing code to run unmodified while routing queries through Drizzle at runtime.
- Changed from blocking JSON endpoint to SSE streaming (like sync-all/repair-icons)
- Progress updates during each audit phase with item counts
- Proper error handling with typed AuditEvent for every failure path
- AbortController support for the client
- Shows real-time progress for each check section
Checks for:
- items_base entries without catalog_items (not purchasable)
- catalog_items referencing non-existent items_base
- Items without .nitro or icon files on disk
- Duplicate classnames
- Items missing from all configured clone sources
- Wrap all DB/file operations in try-catch, send error events via SSE
- Report skipped/progress events for items that already have icons
- Add 'started' event with total count so the UI shows real-time progress
- Catch route-level errors and stream them instead of returning JSON
- Use log line content as React key instead of array index
- Upload .nitro bundles directly with full DB, catalog, and furnidata integration
- Generate SQL migration files on upload (optional)
- Auto-sync missing furniture from all configured clone sources (SSE batch)
- Repair missing icons by extracting from local .nitro or downloading from sources
- All behind ASSETS_IMPORT permission
Furni name suggestions, import enrichment, and badge texts now follow habbo_gamedata_hotel instead of hardcoded habbo.it.
Co-authored-by: Cursor <[email protected]>
Use raw SQL for counts/loads/creates/moves so Habbo DBs with VARCHAR page_id and no AUTO_INCREMENT still show and persist furni.
Co-authored-by: Cursor <[email protected]>
Use an opaque admin-canvas portal and lazy parentId fetches instead of mode=full, which fails on large catalogs. Search no longer loads the entire tree.
Co-authored-by: Cursor <[email protected]>
Seed root tabs from SSR, load the full tree without CLEAR_TREE races, coerce parent ids, and tolerate catalog_items page_id type mismatches so category pages actually appear.
Co-authored-by: Cursor <[email protected]>
Gate permissions on ACL manage + resolve super-admin from live user ranks, restore prefixes API routes, and anglicize hardcoded admin copy with nav i18n.
Co-authored-by: Cursor <[email protected]>
Gate translations, RCON, and user mutations on SETTINGS_EDIT, RCON_EXECUTE, and USERS_EDIT instead of dashboard/rank checks; redirect the legacy user-edit URL to the guarded canonical page.
Co-authored-by: Cursor <[email protected]>
- Move pnpm.onlyBuiltDependencies/overrides from package.json to pnpm-workspace.yaml (clears pnpm WARN)
- Allow useServerAction run() to accept actions returning void
- Make adminAction/authAction input optional so no-schema actions can be called without args
- Return ActionResult from updateBcPage
- Fix categoryPageMap value type (number | undefined)
- Declare DbService.queryCount field
- Use definite assignment for release in withFurniDataLock
- Narrow pair type in theme-contrast test