Commit Graph
138 Commits
Author SHA1 Message Date
openhands d73baf1458 fix(catalog): take furnidata values from the clone source for retro items
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 29s
CI / tests-integration (push) Successful in 1m33s
CI / tests-unit (push) Successful in 1m33s
CI / tests-ui (push) Successful in 2m21s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m53s
The resync route rebuilt every entry from items_base plus the *official
Habbo* furnidata. A classname that only exists on a retro hotel (leet.ws
and friends) is absent from the official set, so lookupOfficialHabboFurni
returned null and the entry was written with revision 0, category
"unknown" and an empty description — even though the clone import had
those exact values available at import time from the source's own
furnidata.

- resync/route.ts: when a classname is genuinely missing from official
  Habbo, fall back to the configured clone sources. Their furnidata is
  indexed by normalized classname and each entry is coerced into the
  OfficialHabboFurniEntry shape, which is the same JSON shape, so it drives
  the existing buildFurniEntry fallbacks for revision, category, name,
  description, defaultdir, partcolors, specialtype, furniline, environment,
  rare and bc. items_base stays authoritative for id, spriteId and dims,
  and public_name still wins over the source name, matching the import.

  The index is memoized per request, not at module scope: a module-level
  cache would pin the source list for the life of the process and a source
  added later would never be picked up. fetchSourceFurnidata already caches
  per URL, so this costs one parse rather than a network round-trip.

  Disabled sources and sources that fail to respond are skipped, so an
  unreachable hotel degrades to the previous items_base-only behaviour
  instead of failing the run. Official Habbo still wins whenever it has the
  classname, so existing behaviour is unchanged for everything but the
  retro-only case.

Applies to every resync mode, so the pre-existing ?missing=1 sweep picks
this up too.
2026-09-29 16:02:58 +02:00
openhands 2f7e557d5e feat(catalog): add a Studio button to fix missing furnidata entries
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 28s
CI / tests-integration (push) Successful in 1m33s
CI / tests-unit (push) Successful in 1m38s
CI / tests-ui (push) Successful in 2m26s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m1s
"Missing furnidata" was only a filter in the Studio status dropdown, so
imported items whose classname was absent from FurnitureData.json could
be found but not fixed from that screen. Only the Catalog Audit page could
repair them, and only globally.

Adds the same shape of quick action that "no nitro" already had:

- studio-client.tsx: a "N no furnidata" shortcut next to the "N no nitro"
  button that sets the missingFurnidata status filter, and a bulk "Add
  missing furnidata (N)" button for the selected rows. Both only appear
  when there is something to act on. Rows that come back repaired flip
  to hasFurnidata: true so the badges and counts update in place; rows
  the server reported in errors keep their state.
- resync/route.ts: accepts an optional { classnames: string[] } body to
  target exactly the selected rows. classnames are resolved through the
  same normalized local index the listing uses to decide hasFurnidata, so
  the rows written are the rows flagged as missing. The upsert is already
  idempotent, and RCON updateCatalog + updateItems run afterwards so the
  emulator picks the new entries up.
  Also clears the Studio furnidata cache after a write, which this route
  never did: without it the listing kept serving a stale hasFurnidata for
  up to the 30s cache TTL, so a repair looked like it had done nothing.
  PERMS is now imported from permission-slugs (identical re-export) so the
  route no longer pulls next-auth into tests.
- studio-filters.test.ts: pins the missingFurnidata branch, in particular
  that an unchecked item (hasFurnidata undefined) is not treated as missing.

The existing ?days / ?missing / ?broken / ?all modes are unchanged; the
body is only consulted when it carries a classnames array.
2026-09-29 15:48:32 +02:00
openhands e4f83a8036 chore: upgrade to pnpm v12, vitest v5 and resolve deprecated subdependencies
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Failing after 21s
CI / tests-unit (push) Skipped
CI / tests-integration (push) Skipped
CI / tests-ui (push) Skipped
CI / preflight (push) Skipped
CI / deploy (push) Skipped
2026-09-27 19:42:34 +02:00
openhands 944527e078 feat(nitro-cleanup): dedupe FurnitureData and clean dangling figure entries
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 36s
CI / tests-integration (push) Successful in 1m58s
CI / tests-unit (push) Successful in 2m16s
CI / tests-ui (push) Successful in 3m8s
CI / preflight (push) Skipped
CI / deploy (push) Failing after 2m30s
Add a gamedata cleanup to the Nitro Cleanup panel: a read-only preview
plus an apply run that dedupes FurnitureData classnames and removes
rows and figure entries that reference nothing.

Three passes run in a fixed order, because cleanFigureMap has to precede
cleanFigureData: dropping the part that points at a set is what makes
that set unreferenced.

A pass refuses to write when it would delete more than maxRemovals rows
(default 500) and reports the reason, a wrong asset directory otherwise
turns every row into an orphan and one call would empty the file. Passes
that would act on empty input (no libraries, no sets) treat that as a
missing file rather than as a reason to delete everything. Every write
copies the file to a timestamped backup first, so a pass that turns out
to be wrong can be undone by hand.

The plan reads FurnitureData once and hands the parsed copy to both
furniture passes; the file is tens of megabytes in a real deployment.
2026-09-27 17:14:30 +02:00
openhands 17de94d984 feat(nitro): convert imported SWF bundles to WebP Lossless
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 31s
CI / tests-unit (push) Successful in 1m59s
CI / tests-integration (push) Successful in 2m19s
CI / tests-ui (push) Successful in 2m56s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m18s
Newly converted .nitro bundles now store their spritesheet as WebP VP8L
instead of PNG, so imports land much smaller without changing a single
pixel. The texture member and spritesheet.meta.image are both labelled
from the actual bytes, never from a caller's assumption.

- encode through sharp with lossless and exact, so colour hidden under
  alpha 0 survives; this mirrors ImageSharp's TransparentColorMode.Preserve
- detect PNG/WebP by magic bytes and reject anything the client cannot
  render, on create, download and upload paths
- keep the source format when deriving size-32 sheets, scaling composites
  and editing metadata, so existing bundles are never silently rewritten
- report fidelity in the studio: the compression panel re-encodes with the
  same options the importer uses, so it cannot drift and invent false
  warnings, and shows PNG/WebP size estimates

convertSwfToNitro and buildSpritesheet are now async, so the worker, the
main-thread fallback and every import call site await them. PNG stays
supported for existing bundles and icon sidecars are untouched.
2026-09-27 15:42:21 +02:00
openhands 420210ffa0 fix(build): make the production build pass, and stop it eating 20GB
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 30s
CI / tests-integration (push) Successful in 1m39s
CI / tests-unit (push) Successful in 1m43s
CI / tests-ui (push) Successful in 2m31s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m17s
`next build` had never completed on this host, so three real defects were
sitting in the tree untested. All three are now fixed and the build is green.

- The build was not memory-bound the way it looked. Turbopack's builder reached
  20.5GB RSS and died, and raising `--max-old-space-size` could never have
  helped: that flag caps the V8 heap, while the 20GB sat in Turbopack's own Rust
  allocator. The first symptom was misleading because the process doing the
  allocating is a grandchild of `npx`, so watching the direct child shows a
  95MB shim the whole time. Building with `--webpack` puts the build back under
  the JS heap, where the flag actually applies: peak 5.9GB, 150s, exit 0.

- withAdmin's second parameter was typed `{ params?: ... }` and given a `= {}`
  default, which made it optional and `RouteContext | undefined`. Next's
  generated route types assert that argument against `ParamCheck<RouteContext>`
  and reject it, across 113 route files. `tsc --noEmit` cannot see this, because
  Next only adds `.next/types` to the project during a production build — so the
  type check that everyone runs locally was structurally incapable of catching
  the only type error that blocks a deploy. `params` is now required, which is
  also what the code already assumed: it is awaited with no guard. The 35 test
  call sites that invoked a handler with one argument now pass a real context,
  and the await got a guard so a direct internal call cannot turn a missing
  context into a 500.

- `src/app/api/admin/import/furni/route.ts` re-exported `ensureDirectories` and
  `importSingleFurni` for "backward compatibility" that nothing used; the batch
  route imports from `@/lib/services/furni-import` directly. Next rejects any
  value export from a route module that is not an HTTP verb or config, so this
  had been breaking the build for as long as it existed. Removed.

- `isomorphic-dompurify` builds its server-side DOM through jsdom. Bundled, that
  pulls jsdom's `browser/default-stylesheet.css` into the server chunk, where the
  path no longer resolves, and page-data collection dies with ENOENT on every
  page that sanitizes HTML. Marked external so Node resolves it from
  node_modules and the standalone tracer includes it.

The remaining build warning is a pre-existing circular dependency between
chunks that share the webpack runtime. It costs hash reuse, not correctness, and
is left alone rather than churned here.

Verified: build exit 0, 276 static pages generated, 3223 tests pass, tsc and
biome clean.
2026-09-25 19:47:10 +02:00
openhands 203399aab7 fix(cache): true LRU, stale-while-revalidate and cross-process invalidation
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 32s
CI / tests-integration (push) Successful in 1m38s
CI / tests-unit (push) Successful in 1m42s
CI / tests-ui (push) Successful in 2m33s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m43s
The in-process cache was a FIFO of 500 entries that was never touched on a
read, so a key polled on every request could be evicted by an unrelated burst
of dynamic keys. That looked exactly like the cache being cleared at random,
and it is what made the site fall back to the database unpredictably.

- Evict least-recently-used instead, and raise the default budget to 2000
  (CACHE_MEMORY_MAX_ENTRIES). Reading a key now marks it as used, so a hot key
  only leaves when a hotter one takes its place.
- Add opt-in stale-while-revalidate (CachedOptions.staleMs). The grace window
  lives on the entry, so one call site opting in protects every reader of that
  key. A failed background refresh keeps serving the last good value instead of
  falling through to the origin, and is reported once rather than per read.
- Invalidate across processes. invalidateKey() now clears memory, deletes the
  Redis key and publishes a signal, so a value written by one process is no
  longer served stale by the others for the rest of its TTL. A failed Redis
  delete no longer skips the broadcast.
- Guard against a refresh that started before an invalidation writing its
  outdated result back into the cache.
- Read the news revision at most once a second per process instead of on every
  call, with a pub/sub signal to drop the local copy when it rotates. A Redis
  outage now degrades to the in-process cache rather than to no cache at all.
- Warm the hot public keys on boot, so the first visitors after a deploy do not
  each pay for a miss.
- Count hits, misses, stale serves, errors and evictions per key, exposed at
  GET /api/admin/devops/cache. Without it a wrong REDIS_URL, a full budget and
  a dead origin all look identical from the outside.
- Enforce the imaging cache budget for real: records are .img/.json pairs, so
  the old cap counted files and never removed anything while entries were
  fresh. Sweeps are throttled per directory and prune to a low-water mark.
- Cap the JWT version map, and stop per-test scratch roots from littering the
  runtime imaging cache.

Public read-only endpoints get grace windows; admin, account and auth data
deliberately stays fresh. Redis TTLs get a little jitter so keys written
together no longer expire together.

3209 tests pass. next build could not be verified on this host: the optimized
build is OOM-killed before prerender, so this has not run in a real Next
runtime yet.
2026-09-25 18:26:45 +02:00
openhands 6c53f4680c feat(studio): run nitro scans in the background with cancel-re-attach and nightly auto-clean 2026-09-19 13:41:14 +02:00
openhands 9d571e0c29 perf(studio): stream nitro repair progress over SSE and allow cancelling
CI / check (push) Successful in 4m23s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m43s
2026-09-19 13:12:45 +02:00
openhands ba81d16f00 perf(studio): cache nitro scan, stream progress, virtualize cleanup list
CI / check (push) Successful in 4m14s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m10s
2026-09-19 13:01:59 +02:00
openhands 8638e81444 refactor(db): typed query helpers, shared test FormData helper
CI / check (push) Successful in 4m10s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m4s
Replace raw db.execute tuple casts with queryRows/rowsFrom/execResult/
affectedRows helpers from lib/db, drop redundant mysql2 casts on typed
query builders, and centralize per-test fakeForm into test/fake-form.
Update db mocks in tests so helpers resolve against mocked execute.
2026-09-17 21:02:57 +02:00
openhands 438277a17a feat(studio): expand nitro cleanup with repair, auto-clean, and orphaned assets
CI / check (push) Successful in 4m15s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m59s
Scan distinguishes fake, broken, and orphaned SWF/icon assets with age
metadata, deletes per asset kind, re-downloads broken nitro bundles from
configured sources, auto-cleans old fake leftovers, and exports a JSON
manifest. Adds rebuild and auto-clean API endpoints with audit coverage
and a housekeeping preview route under the hotel domain.

Verified: full vitest suite (2213 tests), typecheck, and biome all pass.
2026-09-15 21:59:21 +02:00
openhands 694f87d98c feat(studio): add nitro cleanup tool for fake and broken bundles
CI / check (push) Failing after 1m22s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
2026-09-15 20:54:10 +02:00
openhands 644d53ca16 Rebuild organize imports: move furniture between categories, 500-item cap
CI / check (push) Failing after 5m12s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
2026-09-14 18:24:48 +02:00
openhands 1bbd809b43 Replace standalone catalog editor with the unified Visual Catalog studio
CI / check (push) Failing after 26s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
Make /admin/catalog a full-screen catalog studio that replaces the old
listing plus separate [id]/builder-club detail pages:

- Embed CatalogManagerWorkspace on /admin/catalog with a Normal/Builder
  Club toggle, Catalog Sync status, packages (normal), Organize imports
  and a diagnostics link to /admin/studio/maintenance.
- Manage BC items directly in the studio Items tab (new BcItemsEditor,
  CRUD via existing bc actions; /api/admin/catalog/items now serves BC).
- Inline editor: add pageTextTeaser field for both catalogs and remove
  the legacy full-editor links.
- Remove the 'Open full editor' context action from the tree.
- Move catalog-items-table (dir + barrel) and catalog-translate-tab out
  of the app route into src/components/admin/catalog and update all
  importers.
- Keep /admin/catalog/[id], builder-club/[id] and /admin/catalog/maintenance
  as redirects into the new studio; consolidate maintenance panels into
  /admin/studio/maintenance and point the nav item there.
- Delete the old listing/table/tabs/forms and the standalone bc-manager.
2026-09-14 17:20:52 +02:00
Simo c977fe95ba fix(studio): recover uncertain imports with persistent request identities
CI / check (push) Successful in 3m12s
CI / deploy (push) Successful in 19s
CI / publish-container (push) Successful in 1m12s
2026-09-13 18:33:06 +02:00
openhands fb68df3ba9 feat(import): apply translation to successful bulk items
CI / check (push) Successful in 2m27s
CI / deploy (push) Successful in 1m15s
CI / publish-container (push) Successful in 56s
The interactive batch ignored the client's Translate option, so translated names never landed in the language files during a bulk run. Patch each successful item through patchLocalizedFurniDataEntries (mutex-guarded, best-effort) when translation is requested, surfacing failures as item warnings instead of failing the import.
2026-09-13 12:54:00 +02:00
openhands ffcd4232d6 feat(import): durable batch checkpoint and transient auto-retry
CI / check (push) Successful in 2m24s
CI / deploy (push) Successful in 1m32s
CI / publish-container (push) Successful in 47s
Mirror interactive batch runs into the import-job store so interrupted imports (restart, time-out, disconnect) can be resumed from Import History. Items are checkpointed as they settle (coalesced, serialized saves) and the mirror starts 'running' so the boot-time worker marks it 'interrupted' instead of double-importing; done items are never re-imported. Add bounded backoff retry for transient download/connection failures before marking an item failed, and point the client's time-out/network toasts at Import History.
2026-09-13 12:50:17 +02:00
openhands 2a708b01b1 perf(import): adjustable concurrency and skip redundant SWF downloads
CI / check (push) Successful in 2m22s
CI / deploy (push) Successful in 1m12s
CI / publish-container (push) Successful in 44s
Raise batch concurrency (furni 3->12, clone 10->12) with a Speed control next to Translate. Skip the SWF download when a .nitro bundle already exists on disk (color variants share the base nitro), and stop flagging that as a failed download.
2026-09-13 12:14:52 +02:00
openhands b92f897ba2 perf(studio): virtualize the furniture table and throttle batch progress
CI / check (push) Successful in 2m33s
CI / deploy (push) Successful in 1m28s
CI / publish-container (push) Successful in 57s
- Render the table view through a virtualizer too, using a shared grid
  template so the sticky header and rows keep perfect column alignment
- Keep semantic table/row/cell elements while virtualizing
- Cap the batch item-details list to the latest 60 rows (newest first)
- Coalesce per-item progress events server-side (120ms throttle) in both
  the exact-import and clone SSE batch runners
2026-09-13 11:53:07 +02:00
openhands d02aaa0d87 perf(studio): cache furni imports and virtualize the furniture grid
CI / check (push) Failing after 22s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
- Add TTL-based caching for local index lookup, furnidata classnames,
  catalog id set, nitro file presence and import stats
- Invalidate caches after furnace single/batch/clone imports
- Rewrite batch progress with elapsed time, rate and verification chips
- Virtualize the grid with @tanstack/react-virtual and replace the
  Load more button with infinite scroll via an IntersectionObserver
2026-09-13 11:40:55 +02:00
openhands 388d8992f8 feat: add CSV/JSON export, advanced bulk filters & selection to Catalog Manager
CI / check (push) Failing after 1m1s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
- Admin Audit Log: CSV/JSON export via ?format=query param
- Catalog Manager: club_only / have_offer filters (multi-filter support)
- Offer discovery: refactored discoverOffers() to accept filters object
- Translations added for new filter labels
- Test updates for all modified paths
2026-09-12 20:40:33 +02:00
openhands 4d720ee03c fix(furni): generate scale 32 from bundles with .png frame keys
CI / check (push) Successful in 2m30s
CI / deploy (push) Successful in 1m25s
CI / publish-container (push) Successful in 48s
resolveNitroFrame now matches spritesheet frame keys that carry a .png
suffix or namespaced naming, and isScale/scaleName preserve that suffix.
Broken source sprites (missing frames or references to icon artwork) are
skipped and reported instead of aborting the whole generation, and the
studio UI surfaces the skipped count.
2026-09-12 12:46:17 +02:00
openhands 683fee3bd9 fix: resolve layout type errors in organize-imports-dialog
CI / check (push) Successful in 2m29s
CI / deploy (push) Successful in 18s
CI / publish-container (push) Successful in 1m32s
2026-09-11 23:25:53 +02:00
openhands 61c7519bea fix(studio): show all offers in the imported-furniture tree
CI / check (push) Successful in 2m23s
CI / deploy (push) Successful in 1m11s
CI / publish-container (push) Successful in 44s
The organize-imports route defaulted to a 500-item limit, silently
hiding offers beyond the first batch of imported pages.  Remove the
effective cap (limit now means 'all', guarded only by a 50k lint cap)
so every offer already sitting in the import tree is returned and
grouped.
2026-09-11 20:33:27 +02:00
openhands 19dc0347df fix(studio): harden organize-imports against long-running queries and hangs
CI / check (push) Successful in 2m33s
CI / deploy (push) Successful in 1m26s
CI / publish-container (push) Successful in 50s
The original GET route used a correlated NOT EXISTS / FIND_IN_SET
subquery over the entire catalog_items table for every recent import
audit entry, causing server timeouts when the audit log or catalog
grew large.  The per-item host-page validation inside the create
action also issued one SELECT + one UPDATE per moved offer.

Changes:
- GET /api/admin/import/organize: replace the correlated subquery
  with a bounded candidate list and a JS-side placed-set check, then
  resolve all needed base items in a single indexed SELECT.  This
  bounds the query cost regardless of catalog or audit log size.
- organizeImportFurni action: validate mover ids in one SELECT, then
  batch every move per group into a single UPDATE with a CASE
  expression instead of one UPDATE per item.
- OrganizeImportsDialog: add a 45-second abort timeout on the fetch
  and a distinct load-error state so the UI never silently hangs.
- Add 'loadError' translation key (en + nl).
2026-09-11 20:24:38 +02:00
openhands 01a85ebcd0 feat(studio): add organize-imports tool with suggestion review and approval
CI / check (push) Successful in 2m22s
CI / deploy (push) Successful in 1m34s
CI / publish-container (push) Successful in 46s
Adds a Studio 'Organize imports' dialog that groups recently imported
furniture and furniture already sitting in the auto-created import
pages into suggested catalog categories.  Each group is presented with
its suggested name, icon, and layout which can be overridden before
approval; approved groups are turned into real catalog pages in a
single atomic export run.  Offers already inside the import subtree are
moved to the new pages; brand-new furniture gets a fresh offer.

- groupSuggestedCategories: generic pure helper reusing the same
  per-item label heuristic that drives suggestCategoryName; items with
  no label land in a 'Other Furni' remainder bucket
- GET /api/admin/import/organize: returns items from the imported
  furniture tree (catalog_items JOIN items_base via page id set from
  the imported-furniture root) union audit-logged but not-yet-placed
  recent imports; marks alreadyPlaced vs new
- organizeImportFurni server action: validates import-page membership
  before moving any offer, creates pages + inserts/moves items in one
  withCatalogExport snapshot, logs activity
- OrganizeImportsDialog: full-featured Studio dialog with price panel
  (applies to new offers only), parent select, per-group approval,
  editable name/icon/layout with suggestion reset chips, source tags
- import-pages.ts server helper: locates the imported-furniture tree
- Studio nav: 'Organize imports' button with FolderTree icon,
  gated on CATALOG_EDIT, wired next to the catalog manager
- en + nl translations for organizeImports.* keys with ICU plurals
- groupSuggestedCategories unit tests (deterministic grouping,
  remainder handling, size+alpha ordering, label consistency)
2026-09-11 19:38:18 +02:00
openhands f832479e52 feat(catalog): add auto-category wizard with live preview and smart suggestions
CI / check (push) Successful in 3m1s
CI / deploy (push) Successful in 2m7s
CI / publish-container (push) Successful in 45s
- Add AutoCategoryDialog: pick furni (or empty page), suggest caption/icon/layout, live preview
- Add createAutoCategory server action (page + offers in one export) with CatalogKind
- Extend furni search API with interactionType
- Share ShopTile and refactor inline-editor/items-shop-preview to use it
- Add suggestion heuristics (suggestCategoryName/dIcon/layout) with tests
- Add autoCategory translations (en/nl)
2026-09-11 18:47:31 +02:00
Simo 88790d1a0d feat(cms): recover drafts and failed imports with verified UI workflows
CI / check (push) Successful in 2m3s
CI / deploy (push) Successful in 1m31s
CI / publish-container (push) Successful in 1m41s
2026-09-11 08:58:10 +02:00
Simo 76f0420d64 feat(import): run catalog synchronizations as durable jobs
CI / check (push) Successful in 57s
CI / deploy (push) Successful in 18s
CI / publish-container (push) Successful in 1m18s
2026-09-11 00:36:22 +02:00
openhands 3e69b72513 feat(catalog): prevent and repair Arcturus emulator data errors
CI / check (push) Failing after 26s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
Block invalid catalog_items writes at the API level (points currency
allowlist, non-negative prices, positive amount, limited stack >= sold
count, unique sibling order numbers) and auto-assign unique order numbers
on bulk create. Add a catalog-maintenance scan + transactional repair that
fixes pre-existing rows: resets unsupported points_type, clamps negative
costs, sets amount to 1, raises limited_stack, renumbers duplicate orders
and deletes offers with missing page/item references. Surface the issue
count and a fix button in the admin maintenance panel.

Also: add enabled/retired flag to clone sources, classify poster and
currency furniture in item-kind, and remove the obsolete update-Nitrov3.sh.
2026-09-10 11:29:28 +02:00
Simo 1ef405be0a feat(cms): add recovery history and operational reliability tools
CI / check (push) Failing after 22s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
2026-09-09 21:57:56 +02:00
Simo 89526af344 feat(hk): expand search and add operational user overview
CI / check (push) Successful in 54s
CI / deploy (push) Successful in 1m16s
CI / publish-container (push) Successful in 1m57s
2026-09-09 21:14:10 +02:00
Simo c389c3893d feat(cms): improve catalog, editorial recovery and operations
CI / check (push) Successful in 52s
CI / deploy (push) Successful in 2m10s
CI / publish-container (push) Failing after 1m18s
2026-09-09 19:36:15 +02:00
Simo 193b6686a9 refactor(catalog): unify commands and embed guarded catalog workspace
CI / check (push) Successful in 1m16s
CI / deploy (push) Successful in 28s
2026-09-06 19:40:52 +02:00
Simo a070004e7b feat(admin): reorganize housekeeping and strengthen shared workflows
CI / check (push) Successful in 1m5s
CI / deploy (push) Successful in 1m4s
2026-09-06 17:17:30 +02:00
openhands 8c12fc6c60 refactor(cms): deduplicate shared admin components and fix studio batch completion
CI / check (push) Failing after 1m35s
CI / deploy (push) Skipped
CI / e2e (push) Skipped
- fix(studio): stop markBatchDone infinite recursion so batches complete
- refactor(api): merge api-response into api and drop the duplicate module
- refactor(media): extract shared media loader and URL validator
- refactor(ui): extract shared LoadingSpinner for site and admin groups
- refactor(dates): consolidate raw date formatting into formatDate util
- refactor(logs): share a single generic log-list loader across tables
- refactor(theme): merge both ColorField components and reuse contrast helpers
- refactor(import): extract shared ImportErrorBanner and SearchInput
- chore(): remove dead theme-editor-tabs after inlining tab components
2026-09-05 20:58:42 +02:00
Simo 570f3dde44 feat(catalog): repair missing furniture components with verified status
CI / check (push) Successful in 1m33s
CI / deploy (push) Successful in 1m22s
CI / e2e (push) Successful in 24s
2026-09-05 18:12:34 +02:00
Simo cd75361b9f feat(catalog): generate derived scale 32 with preview and backup restore
CI / check (push) Successful in 1m32s
CI / deploy (push) Successful in 1m12s
CI / e2e (push) Successful in 21s
2026-09-05 17:36:22 +02:00
Simo 08b0b5021e feat(catalog): inspect Nitro scales and preview original sprites
CI / check (push) Successful in 1m47s
CI / deploy (push) Successful in 1m30s
CI / e2e (push) Successful in 22s
2026-09-05 17:22:27 +02:00
Simo 2619bec165 feat(catalog): queue furniture imports with history and matching file attachments
CI / check (push) Successful in 1m30s
CI / deploy (push) Successful in 1m26s
CI / e2e (push) Successful in 25s
2026-09-05 17:02:26 +02:00
Simo 33b6d1520e fix(catalog): stream single imports and preserve response errors
CI / check (push) Successful in 1m31s
CI / deploy (push) Successful in 1m13s
CI / e2e (push) Successful in 21s
2026-09-05 15:16:42 +02:00
Simo f0dcbee162 fix(ci): normalize source asset test formatting
CI / check (push) Successful in 1m30s
CI / deploy (push) Successful in 1m9s
CI / e2e (push) Successful in 21s
2026-09-05 15:07:26 +02:00
Simo 159b1f7d1b fix(catalog): check source assets and offer reachable import alternatives
CI / check (push) Failing after 19s
CI / deploy (push) Skipped
CI / e2e (push) Skipped
2026-09-05 14:56:04 +02:00
Simo 8a919f85b2 Preserve furniture revisions when downloading source assets
CI / check (push) Successful in 1m31s
CI / deploy (push) Successful in 1m21s
CI / e2e (push) Successful in 21s
2026-09-05 14:29:48 +02:00
Simo a78d8256e0 Recognize imported furniture by normalized classname and local IDs
CI / check (push) Successful in 1m30s
CI / deploy (push) Successful in 1m21s
CI / e2e (push) Successful in 22s
2026-09-05 14:04:52 +02:00
Simo 9f791ba284 Support Gitea configuration and safe Catalog Studio furniture completion
CI / check (push) Successful in 1m31s
CI / deploy (push) Successful in 1m18s
CI / e2e (push) Successful in 21s
2026-09-05 13:33:40 +02:00
Simo 26f071117b Add Catalog Studio inspection and guided import review
CI / check (push) Successful in 1m39s
CI / deploy (push) Successful in 1m18s
CI / e2e (push) Successful in 25s
2026-09-05 13:10:43 +02:00
Simo 9ec9ab31ad feat: export Catalog Studio assets and SQL to catalog repository
CI / check (pull_request) Failing after 1m21s
CI / deploy (pull_request) Skipped
CI / e2e (pull_request) Skipped
2026-09-05 11:49:00 +02:00
openhands 458972fdfd fix: improve batch import error handling and resilience
CI / check (push) Successful in 1m25s
CI / deploy (push) Successful in 1m57s
CI / e2e (push) Successful in 26s
- Add retries for furnidata fetch (3 attempts, 1s delay)
- Better error messages for user (distinguish 502/400/other)
- Client-side: show detailed error from SSE stream when available
- Remove unused admin-helpers.test.ts
2026-09-04 18:28:14 +02:00