Introduce getCachedAdminCount to cache un-filtered table count(*) queries in Redis for admin lists (starting with UsersPage), avoiding heavy full table scans on every request while keeping exact counts for search/filtered queries.
The 5-minute disk probe now reclaims storage automatically: from 85% it runs the gentle age-windowed Docker prune, from 90% it drops the age windows (docker-prune.sh --force: all unused build cache and unreferenced images, all stopped containers) so a mount can never silently max out. Alerts still fire at 85/90/95% and their hint now points at non-Docker growth when reclaiming is not enough. Force mode is reserved for the worker; deploys keep the gentle mode. Volumes are off-limits in every path.
Add a pure df parser (disk-usage.ts) with 85/90/95% threshold classification, a diskPressure() alert (Discord/email/alert_logs, severity escalates with fill), and a 5-minute host-side probe in jobs-worker.ts that raises one alert per crossing mount, cooldown-gated per mount+level. Real mounts only: overlay/tmpfs pseudo filesystems are ignored.
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.
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.
- 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
- 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
The Arcturus errors "page hierarchy contains a cycle page 354 and 357" and
"sibling order 1 is used more than once (111 problems)" come from
catalog_pages, not catalog_items: pages 354/357 point at themselves, and
many parents have child pages sharing the same order_num. Extend the
emulator catalog scan + fix to detect both: pages whose parent chain loops
back get detached (parent_id = 0 on the highest cycle member) and every
affected parent's children are renumbered sequentially, preserving their
current relative order.
Add scripts/diag-emulator.ts to inspect the live catalog state.
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.
Runs the full catalog audit (runCatalogAudit) against the live hotel DB with
no repair/sql options — a pure read pass — and cross-checks the emitted
summary against independent DB + asset-dir measurements: row totals, duplicate
classname groups, orphaned catalog references and missing catalog / nitro /
icon counts must match exactly, with zero error events and a final
'batch_complete'.
Guarded by RUN_CATALOG_AUDIT_LIVE=1 so CI never runs it; loads the real .env
because vitest fakes DATABASE_URL.
The site-settings loader kept an in-process map forever after a Redis miss
and promoted DEFAULTS (no logo/theme) to Redis on any DB error, so a build
that started before the DB was reachable stuck the site on the preset logo
and default theme until a manual reload or full restart.
- Redis miss now reloads from the database instead of the stale in-process map
- a DB failure returns defaults only as an in-process last resort and never
writes them to Redis, so the shared cache can't be poisoned by a transient
error at startup
- regression tests: DB re-read on Redis miss after cache expiry, defaults never
promoted to Redis, recovery from transient DB failure