Commit Graph
23 Commits
Author SHA1 Message Date
openhands 984b5bf793 feat: auto-repair sprite_id drift after every import
CI / check (push) Successful in 28s
CI / release (push) Skipped
CI / deploy (push) Successful in 54s
The emulator sends items_base.sprite_id to the client and Nitro resolves
the furniture by looking up that id in FurnitureData.json. Legacy rows
where sprite_id drifted from the id made the client render a completely
different item (e.g. a wired or custom asset instead of the purchased
furniture).

Add verifyAndFixSpriteIds() which repairs any row whose sprite_id no
longer matches its id while the local furnidata carries the item under
that id, and run it after every import path (single, batch, batch-regen,
clone) next to the offer_id rebuild.
2026-08-21 14:02:42 +02:00
openhands 4b7cb519df fix: furniture interaction detection and automatic catalog offer_id rebuild
Interaction detection:
- Trust SWF-derived sit/lay/stand flags only when the logic XML actually
  contains action data (new hasActions metadata); otherwise fall back to
  keyword detection so custom furni without <action> nodes are still
  classified correctly
- Add French/Dutch/German/Spanish/Italian keywords (chaise, banquette,
  stoel, silla, sedia, stuhl, tafel, mesa, ...) to sit/lay/stand detection
  with token-boundary matching to avoid false positives like bedside_table
- Unify interaction_modes_count priority: mechanic fixed modes, then raw
  animation state count, then sit/lay fallback, then keyword default
- Detect mechanic type even when real flags are not used

Catalog integrity:
- Skip duplicate catalog_items inserts in clone and upload imports
- Re-check live catalog rows before applying generated repair SQL
- Add rebuildCatalogOfferIds() which rebuilds every catalog_items.offer_id
  from the local FurnitureData.json (matched by entry id, then classname)
  and run it after every import path (single, batch, batch-regen, clone)
2026-08-21 13:39:21 +02:00
openhands 652d331402 Add live furnidata reload without hotel reload and notImported status filter
CI / check (push) Successful in 26s
CI / release (push) Skipped
CI / deploy (push) Successful in 57s
- Add /api/admin/import/furni?action=live-reconcile POST endpoint that reconciles FurnitureData.json with items_base without triggering RCON hotel reload
- Add 'notImported' status filter in studio to show all non-imported furniture items
- Useful for verifying imports and seeing what's missing after furniture import
2026-08-20 16:58:26 +02:00
openhands 6021a91ef7 fix: harden furni import pipeline for production
CI / check (push) Successful in 26s
CI / release (push) Skipped
CI / deploy (push) Successful in 57s
- importSingleFurni: actually update an existing item instead of failing on a primary key INSERT collision; refuse to reassign a spriteId owned by a different classname
- reconcileFurniDataWithItemsBase: also sync each entry's offerid to the DB sprite id, not just the id
- Move the full-table interaction_modes_count verification out of the per-item import hot path and run it once per batch/single import
- clone-import: apply the auto-detected interaction_type (was hardcoded 'default'), set FurnitureData offerid and catalog_items.offer_id to the new local sprite id (was the source hotel's id / -1)
- clone batch route: run the same post-import reconcile/verify/ownership/RCON consolidation as the furni batch route
- upload-import: set offer_id to the allocated id in both the direct insert and the generated SQL migration (was -1)
- generateCatalogSql: use the classname as catalog_name and the item id as offer_id so rows match the app-managed import convention
- stats + missing-nitro endpoints: match catalog membership via item_ids instead of the catalog_name join, so translated display names no longer break counts
- deleteImportedItem/rollback: delete catalog rows by the canonical item_ids link only
- classifyFurni: wall items always classify as 'walls' before prefix rules (rare_/val_/xmas_) can misfile them
2026-08-20 11:48:51 +02:00
openhands b4968a9967 Reconcile FurnitureData.json with items_base after import
CI / check (push) Successful in 45s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m16s
After every single import, batch import and batch-regen, cross-check all
items_base rows against the local FurnitureData.json: entries whose
spriteId drifted are corrected to the DB id, and missing entries plus
real id conflicts are reported in the API/SSE response. Entries that are
missing entirely are counted (rebuilding them needs SWF/nitro metadata).
2026-08-18 20:43:41 +02:00
openhands b4021f6b42 Backfill icons too in the Gamedata bundle sync
CI / check (push) Successful in 38s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m28s
Extend the post-import backfill to also copy missing _icon.png files
into /var/www/Gamedata/icons (in addition to nitros into
bundled/furniture) so all imported furniture shows an icon in-game.
Report copied nitros and icons separately in the API/SSE responses.
2026-08-18 20:35:48 +02:00
openhands bcd24bfca4 Backfill missing nitros into the Gamedata bundle on import
CI / check (push) Successful in 43s
CI / release (push) Skipped
CI / deploy (push) Failing after 1m19s
After each single import, batch import and batch-regen, copy every
.nitro file that is missing from /var/www/Gamedata/bundled/furniture so
the emulator can render all imported furniture even when the mirror write
was skipped. Reports the copied files in the API/SSE response.
2026-08-18 20:29:30 +02:00
openhands 18825115a4 Auto-fix furni asset ownership after import
CI / check (push) Successful in 44s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m17s
After each single or batch import, chown the swf/icon/nitro asset
directories and the FurnitureData.json folders to www-data:www-data so
nginx and the emulator can read newly written files. Best-effort and
non-blocking; the API response reports which folders were fixed.
2026-08-18 19:52:32 +02:00
openhands c95ad4a37f Reconcile offer_id after every import
CI / check (push) Successful in 40s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m12s
After each single or batch import, walk all catalog items under the
Imported Furniture tree and set offer_id to the furnidata sprite id,
repairing any stale values (e.g. legacy -1 rows). Reports how many
rows were fixed in the API response.
2026-08-18 19:40:17 +02:00
openhands 54f2bc5e0c Add clone source selection to Studio furni browser
CI / check (push) Successful in 32s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m16s
Allow the admin Studio to browse and import furniture from custom retro
hotel sources, not just official Habbo furnidata.

- GET /api/admin/import/furni?source=<id> lists a clone source's
  furnidata (via getCloneList) and returns a per-item iconUrl from the
  source's iconBaseUrl so previews render correctly
- Studio client gets a source selector dropdown (official Habbo plus all
  configured clone_sources) that resets the selection and reloads the
  list when switched
- Single and batch imports now send sourceId so SWF/nitro/icon assets
  are fetched from the selected hotel's CDN
- Preview images prefer the source iconUrl with the existing fallback
  chain; header shows the active source name
2026-08-15 15:43:51 +02:00
openhands 7ef5038a9e Add updateExisting option to furniture import
CI / check (push) Successful in 1m14s
CI / release (push) Skipped
CI / deploy (push) Successful in 2m15s
- Added updateExisting parameter to importSingleFurni and API routes
- When updateExisting=true, existing items are updated (catalog price/page) instead of failing
- Added catalogUpdated flag to ImportSingleResult
- Updates existing catalog entries (price, page) instead of skipping duplicates
2026-08-14 18:35:00 +02:00
openhands aee099339c perf: optimize import batch performance with caching + validation
CI / check (push) Successful in 32s
CI / release (push) Skipped
CI / deploy (push) Successful in 59s
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
2026-08-06 20:27:06 +02:00
openhands a1775fbf1e perf: enable source-specific asset downloads and make nitro/icon URLs optional
CI / check (push) Successful in 26s
CI / release (push) Skipped
CI / deploy (push) Successful in 48s
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
2026-08-06 20:19:54 +02:00
Simo c78f8812ad fix: use configured furni source and catalog assets 2026-08-02 14:22:05 +02:00
openhands 22d455da7a fix(auth): drop nonexistent account_blocked column from login lookup
CI / check (push) Successful in 34s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m42s
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.
2026-08-01 17:38:43 +02:00
SimoandCursor 9aa4f331bf refactor(db): migrate app pages and APIs from Prisma facade to Drizzle (4)
Co-authored-by: Cursor <[email protected]>
2026-08-01 14:15:36 +02:00
openhands 5e8a13a84f fix: resolve all biomaly lint errors and warnings across CMS
Deploy / release (push) Successful in 4s
Deploy / deploy (push) Skipped
- Fix CSS parser config (tailwindDirectives enabled)
- Fix noDangerouslySetInnerHtml via SanitizedHtml component
- Fix useExhaustiveDependencies in catalog-manager-dialog
- Fix noArrayIndexKey across 26 files (stable keys)
- Fix SVG a11y (titles, roles, aria-labels)
- Fix label/input associations (htmlFor/id pairs)
- Fix static element interactions (role + keyboard support)
- Fix noImgElement, noDescendingSpecificity (disabled - external Habbo URLs)
- Fix noNonNullAssertion, useTemplate, unused vars/imports
- Add SanitizedHtml shared component
- Migrate biome.json to 2.5.4 schema
2026-07-20 17:41:53 +02:00
openhands 045dc06a1f fix: resolve type errors and migrate pnpm settings to pnpm-workspace.yaml
Local Build and Deploy / deploy (push) Failing after 56s
- 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
2026-07-13 22:10:50 +02:00
openhands df38dccbf1 style: format code biome
Local Build and Deploy / deploy (push) Failing after 46s
2026-07-13 21:57:41 +02:00
openhands 2e4ed76121 style: format code with prettier
Local Build and Deploy / deploy (push) Successful in 49s
2026-07-12 21:07:34 +02:00
remco e85e4d74ea revert fb8e77bb68
Local Build and Deploy / deploy (push) Successful in 1m11s
revert style: clean up code with prettier and eslint
2026-07-12 21:02:03 +02:00
openhands fb8e77bb68 style: clean up code with prettier and eslint 2026-07-12 20:31:05 +02:00
Simo eb759f54bf feat: connect guarded asset import api 2026-07-12 19:12:25 +02:00