v2.8.0
💥 BREAKING CHANGES
-
due to
b3ba6c0- unify errors to AIP-193 + Page pagination, enrich access log (#443) (PR #444 by @saltbo):unify errors to AIP-193 + Page pagination, enrich access log (#443) (#444)
-
due to
8abca2f- unify error handling on typed AppError + single jsonError renderer (PR #445 by @saltbo):POST /shares/{token}/objects quota rejection now returns 422
(was an inconsistent 400); every other quota path already returned 422. -
due to
7d819a5- move admin user management to better-auth admin plugin (PR #446 by @saltbo):move admin user management to better-auth admin plugin (#446)
-
due to
2ae603b- DELETE endpoints return 204 No Content (#443) (PR #447 by @saltbo):all DELETE endpoints now respond 204 with no body. License
unbind no longer returnscloud_unbind_error, so a partial cloud-unbind failure
is no longer surfaced in the response body.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com- fix(licensing): surface cloud-unbind failure as 502, don't swallow it as 204
The DELETE→204 sweep turned license unbind into an unconditional 204, which
hid a real partial failure: when the best-effort cloud unbind throws, the local
binding is cleared but the cloud side is left dangling. Reporting 204 (success)
in that case swallows the error.
unbindLicensenow returns a Result —{ ok: true }only when the cloud unbind
also succeeds, and a 502 AppError (reasonCLOUD_UNBIND_FAILED, the cloud error
indetails.metadata) when it fails. The local binding is still cleared either
way; the handler returns 204 on ok and throws the error otherwise.
DELETE success is still an empty 204 — this only restores fail-fast on the one
endpoint whose failure was a soft body field, never a thrown error.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com - test(spec): reconcile users.feature with #446 better-auth migration
pnpm lint:spec(a CI gate) was red on 11 orphanedspec/users.feature
scenarios — leftover from #446, which moved admin user management off our
/api/users/*routes onto better-auth's admin plugin and deleted the old
endpoints/tests but not their spec scenarios. Pre-existing on main; surfaced
here as the only failing CI check.
Reconcile the spec with reality:
- Re-link the behaviors that survived (now via better-auth) to the tests that
already cover them: list / admin-only(403) / disable(ban) / delete(remove) /
patch-missing(act-on-missing→404) → admin-users-ba.integration.test.ts;
quota-personal-org → the per-user quota test in users.integration.test.ts. - Drop scenarios for behavior that no longer exists: batch-toggle (now a
client-side fan-out, no endpoint), invalid-status (ban/unban are explicit),
multi-field filter (better-auth search is single-field, untested), the
unauthenticated 401 guard (better-auth owns it), and the inline
quota-entitlements-in-list (quota is now a per-user sub-resource).
lint:spec: 413 scenarios, all covered.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- fix(licensing): surface cloud-unbind failure as 502, don't swallow it as 204
-
due to
78e2550- unify revoke/cancel on PUT /{resource}/{id}/status (#452) (PR #453 by @saltbo):unify revoke/cancel on PUT /{resource}/{id}/status (#452) (#453)
-
due to
7b8c8c9- unify object upload + rework delete/trash lifecycle (#448) (PR #454 by @saltbo):- removes PUT /objects/{id}/status and POST /objects/{id}/uploads
- PUT .../uploads/{sid}/status -> POST .../uploads/{sid}/completions {parts}
- DELETE /objects/{id} flips hard-purge -> soft-delete; permanent purge
moves to DELETE /trash/objects/{id} - DELETE /trash removed; restore is POST /trash/objects/{id}/restorations
- matters.status enum loses 'trashed' (migration backfills to trashedAt)
The migration swaps the matters_active_name_uniq partial index to exclude
trashed rows (WHERE status='active' AND trashed_at IS NULL). The single-PUT
presign is header-free so the uniform slice uploader's raw PUT matches the
S3 signature. Go downloader client + agent reworked to the unified flow.
-
due to
63d5b45- collapse polymorphic GET responses to one monomorphic schema (#449) (PR #455 by @saltbo):collapse polymorphic GET responses to one monomorphic schema (#449) (#455)
-
due to
0550e41- make landing shares public by default (commit by @saltbo):showOnProfile, listedAt, and the profile-listing endpoints are replaced by private and PUT /api/shares/:token/privacy.
✨ New Features
5836789- spaces, quota ownership, and sharing — design doc §5 implementation (PR #426 by @saltbo)6521d1b- events: unify SSE stream, replace frontend polling (PR #432 by @saltbo)91d10f9- openapi: global OpenAPI document + Scalar UI, drop hand-written stubs (PR #440 by @saltbo)e132cb9- openapi: complete API coverage with truthful schemas + unified error handling (PR #442 by @saltbo)00f48cf- avatars: host avatars + team logos on Cloud via SDK 2.4.0; remove public-bucket mode (PR #467 by @agent-kanban-local[bot])f24c6e2- avatar: self-host avatars on R2 on Workers + fix avatar refresh/fallback (PR #468 by @saltbo)f4b65e4- make forcePathStyle configurable per storage (PR #474 by @wujianguo)470a2d7- add admin form primitives (PR #478 by @agent-kanban-local[bot])82c5452- auth: move OAuth provider editor to drawer (PR #480 by @agent-kanban-local[bot])9acbc01- storage: add provider presets and request preview (commit by @saltbo)3695c80- downloads: add task event timeline (commit by @saltbo)02eda6d- add admin user activity feed (PR #493 by @agent-kanban[bot])7ccaba2- refine admin audit filtering (PR #495 by @agent-kanban[bot])556174f- admin: add team activity tab (PR #498 by @agent-kanban[bot])554c231- admin: redesign dashboard with pro analytics (commit by @saltbo)b7f7fa7- admin: implement operations dashboard stats (commit by @saltbo)6376e2a- admin: refine operations dashboard analytics (commit by @saltbo)75d7b50- webdav: support optional custom domains (commit by @saltbo)0bf1f9e- webdav: derive fixed domain from public URL (commit by @saltbo)e761b6a- webdav: verify derived domains before publishing (commit by @saltbo)d9da509- admin: add live dashboard overview (commit by @saltbo)40c5f10- admin: show storage change trend (commit by @saltbo)df0c5f3- auth: add configurable email verification (commit by @saltbo)23f679d- webdav: add admin service controls (commit by @saltbo)387c731- api-keys: unify user ownership and workspace scopes (commit by @saltbo)31005e1- workspaces: reorganize workspace settings (commit by @saltbo)0cfc0fa- workspaces: add billing settings tab (commit by @saltbo)91720c7- storage: add usage projection and management page (commit by @saltbo)77475d5- storage: add compact file cleanup dialog (commit by @saltbo)7bf5d2f- storage: link files to their locations (commit by @saltbo)720f7c9- storage: redesign backend management (commit by @saltbo)10b6883- profile: add public profile menu link (commit by @saltbo)526d237- connect curated shares to public profiles (PR #519 by @agent-kanban[bot])0550e41- shares: make landing shares public by default (commit by @saltbo)d6d811f- public: redesign profile and share pages (PR #522 by @saltbo)ae9273c- auth: remember last login method (commit by @saltbo)
🐛 Bug Fixes
7bad8d2- audit must-fixes + product gaps (SSRF, move-quota, multipart, password-reset, trash retention, notifications) (PR #428 by @saltbo)df07734- upload: overwrite-on-replace instead of trashing the incumbent (PR #431 by @saltbo)b959e5e- downloader: supervise managed engines and make the CLI go-installable (PR #461 by @saltbo)64b0ea3- downloader: give seeding its own concurrency budget and recycle orphan seeds (PR #462 by @saltbo)5b8495b- downloader: report seeding stopped so the dashboard clears it (PR #463 by @saltbo)399609b- downloader: drive stop-control from the task poll, make reporting pure (PR #464 by @saltbo)8754520- downloads: settle stale downloaders' canceling/pausing tasks (PR #465 by @saltbo)75761b3- downloads: settle control tasks for already-offline downloaders too (commit by @saltbo)e6709f5- downloads: pre-authorize remote-download credits, gate before download, surface suspend reason (commit by @saltbo)1b37bb8- downloads: clear stale seeding runtime on unreachable downloaders (commit by @saltbo)8278b9b- downloads: clear stale seeding by live-downloader set (covers deleted owners) (commit by @saltbo)9afa8f1- downloads: make a blocked remote-download unit retryable so credit top-ups recover (commit by @saltbo)fc6ee38- downloader: request uploadLength/uploadSpeed/connections/numSeeders from aria2 tellStatus (commit by @saltbo)5eac720- downloader: report aria2 per-peer progress; perf(ci): scope server COPY (commit by @saltbo)6e2cb47- downloads: preserve transfer progress when a runtime report omits it (commit by @saltbo)a1ce668- downloader: broaden aria2 BT peer discovery so stale magnets resolve (commit by @saltbo)5051439- downloader: re-attach to session download on restart so progress keeps reporting (commit by @saltbo)a803b2d- downloader: don't adopt an assigned task's auto-seed before it's uploaded (commit by @saltbo)23ded65- downloader: nil-safe isAria2RPCDisconnected to stop a startup panic (commit by @saltbo)29ec826- downloader: resume download path instead of panicking when runtime isn't complete (commit by @saltbo)f701f41- security: resolve CodeQL code-scanning alerts (PR #472 by @saltbo)26c6608- restore preview admin credential repair (PR #476 by @agent-kanban-local[bot])7cfbbf7- clean downloader terminal artifacts (PR #477 by @agent-kanban-local[bot])fb2281f- downloader: clean local data only on task delete (commit by @saltbo)c5f2b87- e2e: seed storage without title column (commit by @saltbo)b4ea0a5- storage: handle empty provider values (commit by @saltbo)5f20ae2- storage: align custom host preview (commit by @saltbo)cf7b1de- downloader: consolidate polling into heartbeat (commit by @saltbo)2c0cdd8- downloader: prevent aria2 session auto-restore (commit by @saltbo)26b7a1d- downloader: preserve failed upload results for retry (commit by @saltbo)74c0d0c- downloader: report aria2 metadata runtime (commit by @saltbo)63ceeab- stabilize webdav traffic integration fixture (commit by @saltbo)a6ee3bc- preserve admin user search focus (commit by @agent-kanban[bot])d7454a6- admin: keep user filter mounted during loading (commit by @saltbo)9c93b99- webdav: improve Finder compatibility (commit by @saltbo)77aa770- workspaces: standardize workspace names and slugs (commit by @saltbo)62b5290- admin: refine dashboard card styling (commit by @saltbo)ab81f13- admin: harden dashboard stats ranges (commit by @saltbo)8eb388e- admin: align dashboard stats data sources (commit by @saltbo)b540956- admin: keep stats queries read-only (commit by @saltbo)337db89- admin: mount stats under site api group (commit by @saltbo)8a8fa3e- repair admin statistics integrity (PR #502 by @saltbo)175b210- surface incomplete historical statistics (PR #503 by @saltbo)659d772- analytics: rely exclusively on hourly rollups (commit by @saltbo)06f0ca1- analytics: include current hourly rollups (commit by @saltbo)15189c4- downloads: protect remote download target folders (PR #506 by @saltbo)882f38e- downloads: harden active target folders (PR #507 by @saltbo)f0292cc- events: allow org API keys to stream download tasks (PR #509 by @agent-kanban[bot])ca654b8- analytics: rebuild complete historical counters (commit by @saltbo)3dc4b17- analytics: snapshot mutable report status (commit by @saltbo)c85e60f- analytics: enforce trustworthy offline statistics (PR #510 by @saltbo)53ef166- stats: validate snapshot rollup markers by scope (PR #511 by @saltbo)816f584- stats: reject ranges without closed buckets (PR #512 by @saltbo)a54b863- stats: populate share inventory base rows (PR #513 by @saltbo)34a801c- stats: validate required rollup dimensions (PR #514 by @saltbo)3abf4f2- storage: preserve purge history and track usage deltas (commit by @saltbo)b6cbc61- admin: enforce exact analytics data sources (commit by @saltbo)52c69de- admin: count recent users from live data (commit by @saltbo)14abbf2- admin: use live overview metrics (commit by @saltbo)ff78a33- audit: enforce HTTP usecase boundary (commit by @saltbo)9e028f8- stats: make D1 backfill exact and bounded (commit by @saltbo)498ce73- stats: split D1 counter queries within limits (commit by @saltbo)e79e03d- stats: preserve missing data in dashboard charts (commit by @saltbo)42e2a9b- stats: show exact partial-day snapshots (commit by @saltbo)e8a8232- stats: include current exact snapshots (commit by @saltbo)1d365ea- analytics: stabilize chart dimensions (commit by @saltbo)2d215a2- stats: show exact data from partial ranges (commit by @saltbo)e4750f8- stats: recover activity from authentication sessions (commit by @saltbo)5acbd7e- stats: backfill registration source rollups (commit by @saltbo)1b82630- stats: show exact boundary-day data (commit by @saltbo)819143f- stats: separate growth chart scales (commit by @saltbo)ea26ff0- stats: honor traffic ledger history (commit by @saltbo)ee5d186- stats: preserve traffic completion marker (commit by @saltbo)58be589- navigation: route admin entry through admin index (commit by @saltbo)3f0531c- webdav: sync generated OpenAPI client (commit by @saltbo)b48779e- downloads: decode task tokens as UTF-8 (commit by @saltbo)7c3738f- downloads: preserve owner uid for api key uploads (commit by @saltbo)eea3daf- upload: sync stored content type after upload (commit by @saltbo)a83c3ac- api-keys: normalize legacy scope metadata (commit by @saltbo)ea8fdf2- files: allow collapsing active folder tree branches (commit by @saltbo)498b091- ui: prevent dialog content overflow (commit by @saltbo)53606f7- architecture: route storage usage through usecase (commit by @saltbo)2c1d797- e2e: update storage with PATCH (commit by @saltbo)83c635c- e2e: seed enabled storage semantics (commit by @saltbo)948b628- e2e: unbind cloud license bindings (commit by @saltbo)2123810- e2e: configure public tunnel origin (commit by @saltbo)9ccea6c- e2e: redeem credits from workspace billing (commit by @saltbo)8924660- downloads: preserve integer event fields in D1 (commit by @saltbo)b8041c0- analytics: make pie chart legend scrollable (commit by @saltbo)4ec1fb5- downloader: restore idle heartbeat interval (commit by @saltbo)
⚡ Performance Improvements
♻️ Refactors
c55e806- admin: dedupe format helpers, merge entitlement dialog, group team routes (PR #427 by @saltbo)b6e8c81- architecture cleanup (storage type, dedup, dead deps) (PR #429 by @saltbo)191ee0a- server: clean architecture migration (hono-cf-clean-arch) (PR #433 by @saltbo)fbec747- usecases: consolidate licensing into one file, merge tiny usecases, drop dead code (PR #434 by @saltbo)705aa67- server: usecase-per-resource — move all handler logic into usecases, lock the http boundary (PR #435 by @saltbo)c2cdf99- server: group admin-console resources under http/console + usecases/console (PR #436 by @saltbo)3402a1e- api: RESTful resource-oriented API — drop /admin, status sub-resources, merge audience-split routers (PR #437 by @saltbo)c1542e6- openapi: drop the committed spec artifact; gen Go client from the full doc (PR #441 by @saltbo)b3ba6c0- api: unify errors to AIP-193 + Page pagination, enrich access log (#443) (PR #444 by @saltbo)8abca2f- errors: unify error handling on typed AppError + single jsonError renderer (PR #445 by @saltbo)7d819a5- users: move admin user management to better-auth admin plugin (PR #446 by @saltbo)2ae603b- api: DELETE endpoints return 204 No Content (#443) (PR #447 by @saltbo)78e2550- api: unify revoke/cancel on PUT /{resource}/{id}/status (#452) (PR #453 by @saltbo)7b8c8c9- api: unify object upload + rework delete/trash lifecycle (#448) (PR #454 by @saltbo)63d5b45- api: collapse polymorphic GET responses to one monomorphic schema (#449) (PR #455 by @saltbo)a020cf7- branding: store logo + favicon as base64 data URIs (drop public bucket) (PR #466 by @saltbo)407e4ac- s3: drop dead getPublicUrl gateway method (commit by @saltbo)5b385fa- admin: unify remaining admin headers and drawers (PR #481 by @agent-kanban-local[bot])e55dae3- admin: standardize management forms (commit by @saltbo)22e8164- admin: merge downloader settings drawers (commit by @saltbo)4cdcd29- admin: compact oauth providers table (commit by @saltbo)3fdc7b4- downloader: reorganize cmd downloader runtime (commit by @saltbo)983a554- admin: focus overview on operations dashboard (commit by @saltbo)8bd1e55- analytics: replace daily stats with hourly rollups (PR #505 by @saltbo)60f8f64- analytics: enforce offline result boundaries (commit by @saltbo)7fe16f3- site: replace options with structured config APIs (commit by @saltbo)e55ee53- stats: unify audit and fact pipelines (commit by @saltbo)
👷 Build System
🔧 Chores
d693837- update default branch references from master to main (commit by @saltbo)7f46a84- deps-dev: bump vite from 7.3.2 to 7.3.5 (PR #439 by @dependabot[bot])ea02367- deps: bump nodemailer from 8.0.5 to 8.0.9 (PR #438 by @dependabot[bot])4089f8d- deps: bump hono from 4.12.21 to 4.12.25 (PR #459 by @dependabot[bot])582d869- deps: bump nodemailer 8→9.0.1 and undici 7.24.8→7.28.0 (security) (PR #469 by @saltbo)e5ab679- deps: pin @esbuild-kit's esbuild to 0.25.12 to clear dev-server CVE (PR #470 by @saltbo)3f6751d- openapi: update generated downloader client (commit by @saltbo)c55f6f4- openapi: update generated storage client (commit by @saltbo)bc15bc3- deps: bump golang.org/x/net (PR #496 by @dependabot[bot])10748cd- lsp: configure language server support (commit by @saltbo)8cd151c- openapi: refresh generated Go client (commit by @saltbo)44198fa- openapi: regenerate Go client (commit by @saltbo)2f7026b- openapi: regenerate Go client (commit by @saltbo)46f802a- deps: bump the npm_and_yarn group across 1 directory with 2 updates (PR #516 by @dependabot[bot])10602e5- branding: remove deprecated SVG assets (commit by @saltbo)5d0215d- release: v2.8.0 (commit by @saltbo)
Docker
docker pull ghcr.io/saltbo/zpan:v2.8.0
docker pull ghcr.io/saltbo/zpan:v2.8.0-cli