Skip to content

Releases: Dim145/FigureCollector

0.46.3 — single-result search fix

Choose a tag to compare

@Dim145 Dim145 released this 30 Jul 08:26

Bug fix — single-result orzgk searches

When an orzgk search matches exactly one figure, the store redirects straight to that product page, so the search parser (which looks for result cards) found nothing and the lone result vanished. The importer now detects that redirect (via the product page's /product/ canonical) and rebuilds the single result from the product page. A genuine zero-result search still returns empty — no regression.

Also: internal test + design-mockup sample data cleanup (no functional/runtime change).


🐳 ghcr.io/dim145/figurecollector-{server,client,docs}:0.46.3
📝 Full changelog: 0.46.2...0.46.3

0.46.2 — Cloudflare clearance reuse

Choose a tag to compare

@Dim145 Dim145 released this 29 Jul 16:40

Cloudflare clearance reuse (faster orzgk scraping)

The orzgk solver client now reuses the Cloudflare cf_clearance cookie across requests instead of re-solving the challenge on every call.

  • The first request to a host solves via the configured solver (FlareSolverr / Byparr) and harvests cf_clearance + the solver's exact User-Agent.
  • Subsequent requests to the same host replay them on a plain direct GET (no browser) — falling back to the solver only if Cloudflare challenges again (cookie expired / rotated), so it's never worse than before.
  • A burst to one host (wishlist pagination, the price cron) now costs one solve plus cheap replays instead of one ~30s solve each. A per-host lock collapses concurrent cache-misses into a single solve.

Works with every drop-in — notably Byparr, which (unlike FlareSolverr) has no server-side sessions, so the reuse is done client-side. No config or API changes.

Verified live (Byparr + orzgk): first request solved in ~24s, the next replayed in ~10s with the solver seeing exactly one challenge solve.


🐳 ghcr.io/dim145/figurecollector-{server,client,docs}:0.46.2
📝 Full changelog: 0.46.1...0.46.2

0.46.1 — dependency security bumps

Choose a tag to compare

@Dim145 Dim145 released this 28 Jul 22:36

Dependency security bumps

Ships the Dependabot security fixes from 28338b7 into the published images (no functional changes).

  • server: quinn-proto 0.11.14 → 0.11.15, serde_with 3.20.0 → 3.21.0
  • client (build-time): react-router-dom → 7.18.2 + pnpm overrides for sharp, postcss, adm-zip, js-yaml, fast-uri, brace-expansion, protobufjs, dompurify
  • embed-worker: Pillow 12.2.0 → 12.3.0

One high react-router advisory (GHSA-qwww-vcr4-c8h2 — RSC-mode CSRF) is intentionally not fixed: this is a client-side-only SPA that never uses React Server Components mode, so the vulnerable path is unreachable (dismissed as not-applicable). All open Dependabot alerts are now cleared.


🐳 ghcr.io/dim145/figurecollector-{server,client,docs}:0.46.1
📝 Full changelog: 0.46.0...0.46.1

0.46.0 — past the Cloudflare wall

Choose a tag to compare

@Dim145 Dim145 released this 28 Jul 22:04

past the Cloudflare wall

orzgk lookups get past Cloudflare. When the orzgk store hides behind a Cloudflare "checking your browser" challenge (which 403s plain scraping), FigureCollector can now route its search / detail / wishlist fetches through a self-hosted FlareSolverr-compatible solver — Byparr, Solvearr and trawl all speak the same /v1 API.

Opt-in — nothing changes unless you enable it:

  • Set FLARESOLVERR_URL (and optionally FLARESOLVERR_MAX_TIMEOUT_MS, default 60000), or
  • bring up the bundled sidecar overlay:
    docker compose -f docker-compose.yml -f docker-compose.flaresolverr.yml up -d

Left unset, the scraper keeps its previous behaviour (direct fetch), and manual figure entry is always available regardless. For tougher challenges (Turnstile), swap the sidecar image: for Byparr (Camoufox-backed) — the API is identical.

Verified live against orzgk: the solver returned "Challenge solved!" and the parser extracted real figures from the response.

Also in this release

  • security: bump cmov 0.5.3 → 0.5.4 (GHSA-3rjw-m598-pq24)
  • ci: scope GITHUB_TOKEN to contents: read (CodeQL missing-workflow-permissions)

🐳 Images (built by CI on publish): ghcr.io/dim145/figurecollector-{server,client,docs}:0.46.0
📝 Full changelog: 0.45.3...0.46.0

0.45.3 — Bug fixes

Choose a tag to compare

@Dim145 Dim145 released this 04 Jul 12:07

A bug-fix release: the reported catalogue-ambiance click, plus a frontend/backend correctness sweep and one dependency security bump.

Fixed

Catalogue / browse

  • Ambiance preview click — clicking an ambiance tile in the catalogue's "Ambiances visuelles" preview now opens it. Previously it did nothing unless you first went through "Toutes les ambiances".

Frontend

  • The mobile "Générer 3D" checkbox now respects the same admin-only gate as desktop (a non-admin no longer gets a checkbox that reverts).
  • The per-series MangaCollector pill/link now refreshes after linking / syncing / unlinking (was stale up to 5 min).
  • The g keyboard-shortcut hint pill now dismisses after its timeout instead of lingering.
  • The duplicate-figure warning clears once the typed name drops below the search threshold (no more stale matches).
  • Fixed timer/save leaks: achievement toasts no longer fire after navigating away; the shelf-planner's last layout edit is no longer dropped when leaving mid-save; the live-sync WebSocket no longer risks orphaned reconnect timers / duplicate sockets.
  • The entity hero image no longer shows a false "no image" placeholder when moving between two entities that share the same cover URL.

Backend

  • Pre-order stats: the "placed" tile counted every status (including received/cancelled) — it now counts only active pre-orders, and the breakdown total no longer double-counts.
  • Notification channels: editing only a channel's destination no longer silently disables the channel.
  • Follower / following counts now match the follower list (both count publicly-visible profiles), so the badge equals the list.
  • Concurrent first-photo uploads to the same figure no longer 500 (the "primary photo" race is resolved + retried).
  • Manga-server approval/revocation events now appear in the per-event notification-routing settings.
  • The wishlist "already owned" guard is now race-free (evaluated atomically at insert time).
  • Catalogue pagination is now deterministic — a stable tiebreaker prevents a figure being skipped or duplicated across pages when creation timestamps tie.

Security

  • Bumped cmov 0.5.3 → 0.5.4 (GHSA-3rjw-m598-pq24): the previous version could compute wrong results on aarch64 when high register bits are set. It is pulled transitively by the Web-Push signing stack (web-push-nativejwt-simple).

Full changelog: 0.45.2...0.45.3

0.45.2 — Security & NSFW fixes

Choose a tag to compare

@Dim145 Dim145 released this 03 Jul 20:33

A security + bug-fix release. It also rolls up the 0.45.1 NSFW-visibility fixes, which weren't separately released.

🔒 Security

Hardening from an internal security review — self-hosters should upgrade.

  • AI image fetch — SSRF / local-file read. official_image_url is now validated to http(s) when a figure is created or edited, and the embed/gsplat worker's image fetcher rejects non-http(s) schemes and non-public targets (with redirect re-validation). This prevents a signed-in user from steering the worker at file:// or internal addresses once visual search is enabled.
  • PDF parsing crash — RUSTSEC-2026-0187. lopdf bumped to 0.42.0 (via the direct dependency + pdf-extract 0.12) so a crafted invoice / insurance-dossier PDF can no longer abort the server process. A parse-concurrency cap was also added.
  • Notification webhooks — DNS rebinding. Outbound webhook / ntfy / apprise delivery now filters private, link-local and cloud-metadata addresses at connection time, closing a DNS-rebinding window. (OIDC discovery to a self-hosted internal IdP is intentionally unaffected.)
  • Worker internal channel. The embed/gsplat worker refuses to send its token and private photo bytes over plaintext HTTP unless explicitly opted in for a trusted same-host network (WORKER_ALLOW_INSECURE=1).
  • Private-photo caching. The PWA service worker no longer retains private photo bytes past the end of a session (NetworkFirst + cache purge on 401), so they can't linger on a shared device.
  • Container hardening. The optional gsplat GPU worker now drops all Linux capabilities.
  • Dependencies. Pillow pinned ≥ 12.2 (CVE-2026-25990, CVE-2026-40192) in the macOS worker; PyMuPDF pinned ≥ 1.26.7 in the GPU worker.

🖼️ Fixed — NSFW visibility (0.45.1)

  • Hide now truly hides (rather than blurs) NSFW figures in the catalogue discovery rails and the ambiance mosaics — and applies immediately, without a page reload.
  • Achievement seals honour the NSFW preference (generic placeholder when hidden, blurred when set to blur).
  • Reaching an NSFW figure directly (pre-order link / URL) while hiding now shows a consent interstitial with a shortcut to the setting, instead of a generic 404.

Notes

  • Backend and client are updated in place; the embed/gsplat worker images pick up their share of the fixes on their next rebuild. Visual search is admin-flag-gated and off by default.

Full changelog: 0.45.0...0.45.2

0.45.0

Choose a tag to compare

@Dim145 Dim145 released this 27 Jun 21:43

What's new in 0.45.0

A large cumulative release rolling up everything since 0.40.1 — five new features (across 0.41.0–0.45.0), the first automated test suite + CI, and a batch of fixes.

Added

  • Per-shop stock availability (0.42.0) — figure pages and shop pages now show whether each linked shop has the figure in stock / out of stock / preorder, parsed from orzgk (the WooCommerce is_in_stock / backorders_allowed data) and, when the proxy reports it, from the proxy's /product status field. The buy control adapts per shop: in stock keeps the usual Acheter, out of stock demotes to a quiet Voir + a red "Rupture" label, preorder becomes Précommander, and an unknown/unscraped state makes no claim. Each demoted state shows a "Vérifié il y a …" freshness line (flagged stale past 48h); a status not refreshed for 7 days ages back to "unknown" so a stale badge never lingers.
  • Add a figure by photo (0.43.0) — a new Photo tab in the add-figure lookup: snap or upload a photo → on-device DINOv2 embedding → nearest-neighbour match against the catalogue → pick a result and it prefills the form. The photo never leaves the device (only the embedding is sent).
  • Tag your own photos (0.43.0) — owned-item photos can be auto-tagged with WD-Tagger v3 and used to filter your collection by appearance (tag chips on each photo + a tag filter on the collection). Owned-photo tagging is opt-in and stays off until WORKER_INTERNAL_TOKEN is set (see Notes).
  • Collection over time (0.44.0) — a new Évolution section on Insights: a dual-line growth curve of cumulative pieces and cumulative spend, reconstructed month-by-month from purchase dates (spend converted to your display currency).
  • Provenance & archive reason (0.44.0) — record how each piece entered the collection (purchased / gift / trade / found / inherited) and, when archiving, why (sold / traded / lost / gifted). Shown in the owned-item editor and the archived banner.
  • Public vitrine sharing (0.45.0) — share a display cabinet via a public, read-only link (/v/:token), the same way the gift list is shared. Visitors see the cabinet's pieces (photos, condition, for-sale/trade flags) with no account; deliberately stricter than the public profile — prices, value and notes are never exposed.
  • Admin task console (0.41.0) — /admin/tasks rebuilt into a real task-management console: every background task (server crons, 3D scans, OCR jobs) in one filterable table (state / type / source / trigger / search / since — all persisted), a result message for every run, a "hide no-op runs" toggle, a per-service health strip with readable tooltips, and per-row actions (cancel / relaunch / delete) showing who triggered manual runs.

Fixed

  • Paid pre-order balances were still counted in the « Solde à régler » ribbon total — the per-entry view honoured balance_paid_at, but the aggregate didn't.
  • Catalogue covers intermittently failed to load — added a service-worker cache rule + an on-error fallback to the source image so an already-loaded cover never silently breaks.
  • Collection card's top border was clipped on hover.
  • Settings nav rail wasn't sticky and scrolled away.
  • Catalogue ambiance preview could stay stuck loading forever.

Tooling

  • First automated tests + CI. Frontend unit tests (Vitest: money/currency math, admin task formatting, cover-URL resolution, pre-order stats), backend integration tests (#[sqlx::test] against an ephemeral pgvector DB) on top of the existing unit tests, and Playwright E2E smoke flows (auth, navigation, manual figure entry, add-preorder dialog) against an ephemeral Docker stack — all wired into a new ci.yml (client / server / e2e jobs). Advanced CodeQL setup added.

Database

All migrations are idempotent (ADD COLUMN/CREATE … IF NOT EXISTS):

  • server_job_runs.changed + triggered_by_user + backfill, service_heartbeats (0.41.0)
  • figure_shop_stock (0.42.0)
  • owned_photo_tags (photos.visual_tags) + figure_embedding_queue.figure_id made nullable (0.43.0)
  • owned_provenance (acquisition_source / acquired_from / archive_reason on owned_items) (0.44.0)
  • vitrine_share (collection_locations.share_token) (0.45.0)

Notes (opt-in / configuration)

  • The semantic & appearance search, recommendations, and photo recognition are admin-flag-gated and off by default — enable them in Réglages admin (they need the embed worker to have indexed the catalogue).
  • Owned-photo tagging requires a shared secret: set WORKER_INTERNAL_TOKEN on the server equal to EMBED_WORKER_TOKEN on the worker. Unset = the feature stays off (the internal fetch route fail-closes). nginx also 404s /api/internal/* externally.

Full changelog: 0.40.1...0.45.0

0.40.1

Choose a tag to compare

@Dim145 Dim145 released this 24 Jun 23:18

What's new in 0.40.1

Added

  • PayPal invoice parsing — uploaded PayPal « Activités » receipts are now detected and parsed automatically. The parser reads the buyer-side amount from the X € EUR = Y $ USD conversion line (falling back to the de-doubled receipt total), the transaction id (rejoined even when the print page wraps it across two lines), the invoice number, and the French payment date. It tolerates pdf-extract's doubled-bold glyphs (116699,,5500169,50). ORZGK PayPal Standard invoices keep using the generic parser. Adds 4 parser fixture tests.
  • Pre-order “balance paid” tracking — a new Solde réglé le date field on each pre-order records when the remaining balance was actually paid (manufacturers usually bill it weeks before shipment).

Fixed

  • A pre-order no longer shows a phantom Solde restant once it is settled. The figure timeline now reads Payé intégralement when the order ships/arrives, when the deposit reaches the full price, or when the new balance paid date is set — and surfaces that date when present.

Database

  • Migration 20260625000001_preorder_balance_paid adds the nullable preorders.balance_paid_at column (idempotent ADD COLUMN IF NOT EXISTS).

Full changelog: 0.40.0...0.40.1

chore: bump version to 0.40.0

Choose a tag to compare

@Dim145 Dim145 released this 23 Jun 21:02
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

chore: bump version to 0.36.0

Choose a tag to compare

@Dim145 Dim145 released this 19 Jun 19:17
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>