* fix(download): force browser-downloadable files to save
* fix(storage): require signed upload CORS headers
* fix(storage): keep upload metadata out of signatures
* fix(storage): sign bare upload commands
* revert: keep upload metadata in signed commands
Move site configuration under settings, model analytics and licensing as resources, and isolate scheduler runs under the internal API.
BREAKING CHANGE: site email, branding, analytics, licensing, WebDAV verification, and scheduler endpoint paths have changed.
Refs #451
* fix!: unify pagination and realtime change delivery
Replace offset paging on affected unbounded collections with signed keyset tokens and infinite loading. Persist scoped resource changes so one global SSE connection can resume and invalidate query caches safely.
BREAKING CHANGE: migrated list APIs now accept pageToken and return nextPageToken instead of page and total fields.
Refs #450
* fix: keep page tokens at the HTTP boundary
Move signed page-token handling out of the pure domain layer so dependency-cruiser architecture checks pass without changing behavior.
* fix: route background job stats through usecase
Keep the HTTP boundary from reaching directly into repository ports and cover the new usecase wrapper.
* fix: align clients and checks with cursor pagination
* refactor: unify pagination boundaries and infinite loading
Replace opt-in profile listings with an opt-out private flag and a unified privacy endpoint.
BREAKING CHANGE: showOnProfile, listedAt, and the profile-listing endpoints are replaced by private and PUT /api/shares/:token/privacy.
Centralize request audit recording, preserve immutable download-task history, and derive hourly statistics and backfills from the same authoritative sources. Add durable user registration facts so admin deletion no longer destroys signup history.
* feat(admin): separate billing configuration
Add dedicated storage egress and downloader credit billing contracts, usecases, RPC wrappers, drawers, generated client updates, and coverage.
Agent-Profile: https://agent-kanban.dev/agents/2673e70e0085f4e0
* fix(billing): preserve not found ordering
Check storage and downloader existence before quota_store gating in dedicated billing usecases, and cover enabled missing-resource requests at usecase and route levels.
Agent-Profile: https://agent-kanban.dev/agents/2673e70e0085f4e0
---------
Co-authored-by: Jordan Park <jordan-park@mails.agent-kanban.dev>
* feat: make forcePathStyle configurable per storage
Previously hardcoded to true, which breaks S3-compatible backends that require
virtual-hosted-style addressing (e.g. Alibaba Cloud OSS). Now configurable via
admin storage settings with a toggle switch, defaulting to true for backwards
compatibility.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test: cover storage force path style
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: saltbo <saltbo@foxmail.com>
* feat(avatars): host avatars + team logos on Cloud via SDK 2.4.0; remove public-bucket mode
Host user avatars and org logos on the ZPan Cloud avatar service
(zpan-cloud-sdk ^2.4.0) instead of a public S3/R2 bucket, then remove the
now-dead storages.mode / public-bucket concept entirely (#456 parts 2-3).
- image-upload gateway: upload/delete via SDK uploadAvatar/deleteAvatar against
a bound Cloud client; validate mime (AVATAR_CONTENT_TYPES) + size
(MAX_AVATAR_BYTES) before the call; map cloud error codes to 400/403/413/500;
unbound instance returns 503 cloud_required (delete is a best-effort no-op).
- licensing-cloud: createAvatarUploadClient builds the client with a plain-object
bearer header so both the image content-type and Authorization survive hono's
per-request header merge (a Headers instance would be dropped).
- drop storages.mode (migration via drizzle-kit), StorageRepo.select() no longer
takes a mode, remove StorageMode / Storage.mode / mode schema+audit+UI+i18n and
the PUBLIC_IMAGES bucket + PUBLIC_IMAGES_URL wiring.
Agent-Profile: https://agent-kanban.dev/agents/f759c704c282d88a
* ci(deploy): drop dead PUBLIC_IMAGES R2 provisioning from CF deploy
The Cloud avatar migration removed the PUBLIC_IMAGES binding from
wrangler.toml, so the deploy workflow's R2 public-images steps are dead and
must go too — otherwise every CF deploy keeps re-provisioning a public-read
zpan-public-images bucket (the footgun #456 eliminates) and sets an unused
PUBLIC_IMAGES_URL secret. Removes the bucket-create, managed-public-URL, and
secret steps (steps.r2 was only consumed by the secret step). Also drops a
stale storage-modes line from the v2.0 roadmap.
Agent-Profile: https://agent-kanban.dev/agents/f759c704c282d88a
---------
Co-authored-by: Alex Chen <alex-chen@mails.agent-kanban.dev>