Commit Graph
82 Commits
Author SHA1 Message Date
saltbo 31005e1446 feat(workspaces): reorganize workspace settings 2026-07-23 09:58:48 -04:00
saltbo df0c5f3fbd feat(auth): add configurable email verification 2026-07-22 10:15:29 -04:00
saltbo 1d365eab5a fix(analytics): stabilize chart dimensions 2026-07-22 02:21:07 -04:00
saltbo e79e03d8fa fix(stats): preserve missing data in dashboard charts 2026-07-22 00:45:17 -04:00
saltbo d9da5094bb feat(admin): add live dashboard overview 2026-07-20 22:35:12 -04:00
saltbo 7fe16f3ed7 refactor(site): replace options with structured config APIs 2026-07-20 15:17:48 -04:00
saltbo c5f2b87935 fix(e2e): seed storage without title column 2026-06-24 15:21:28 -04:00
agent-kanban-local[bot]andJordan Park 7cfbbf77b7 fix: clean downloader terminal artifacts (#477)
* fix: clean downloader terminal artifacts

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* test: tolerate stale cloud license cleanup

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* test: retry transient pairing poll failures

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* fix: make suspended downloader cleanup idempotent

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

---------

Co-authored-by: Jordan Park <jordan-park@mails.agent-kanban.dev>
2026-06-24 03:14:38 -04:00
agent-kanban-local[bot]andJordan Park 470a2d7e12 feat: add admin form primitives (#478)
* feat: add admin form primitives

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* fix: preserve admin form label associations

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* chore: retry e2e checks

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* test: tolerate cloud license cleanup races

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

* chore: retry cf e2e

Agent-Profile: https://agent-kanban.dev/agents/57ed5bcf43079e29

---------

Co-authored-by: Jordan Park <jordan-park@mails.agent-kanban.dev>
2026-06-24 03:14:15 -04:00
agent-kanban-local[bot]andAlex Chen 00f48cf355 feat(avatars): host avatars + team logos on Cloud via SDK 2.4.0; remove public-bucket mode (#467)
* 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>
2026-06-20 00:16:07 -04:00
saltboandClaude Opus 4.8 0138e7779e test(e2e): retry admin nav so post-sign-in redirect can't interrupt it
signInAsAdmin waited for the /files redirect then immediately goto'd
/admin/storages, but the client redirect to /files could still be in
flight and interrupt the navigation ("interrupted by another navigation
to /files"), flaking responsive-admin. Wrap the goto + URL assertion in
expect(...).toPass() so it re-navigates until /admin/storages commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:32:51 -04:00
Jasper VanandClaude Opus 4.8 7b8c8c915e refactor(api)!: unify object upload + rework delete/trash lifecycle (#448) (#454)
Resolve #448 — one upload entry point and an AIP-164 soft delete.

Upload: POST /objects now returns size-decided upload instructions
{ sessionId, partSize, urls }; the server picks single PutObject (<=5 GiB)
vs 5 GiB-part multipart (>5 GiB) and rejects >5 TiB. The client PUTs each
slice, reads its ETag, then POSTs them to
POST /objects/{id}/uploads/{sid}/completions (returns the live object).
DELETE /objects/{id}/uploads/{sid} aborts and discards the draft.

Trash: matters.status drops 'trashed' (enum is {draft,active}); trash is
tracked by the existing trashedAt timestamp. DELETE /objects/{id} now
soft-deletes; the recycle bin lives under /trash/objects (list roots, get,
restorations, purge). Empty-trash is a frontend loop over roots.

BREAKING CHANGE:
- 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.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 21:21:43 -04:00
Jasper VanandClaude Opus 4.8 b3ba6c00ff refactor(api)!: unify errors to AIP-193 + Page<T> pagination, enrich access log (#443) (#444)
* refactor(api)!: unify errors to AIP-193 + Page<T> pagination, enrich access log (#443)

Settle the API consistency issues from #443 before SDKs ship. Breaking changes
across the error envelope, list envelopes, and the generated Go client.

Errors → AIP-193 google.rpc.Status (https://google.aip.dev/193):
- every error body is now { error: { code, message, status, details:[ErrorInfo] } }
- machine-readable, switchable key is details[0].reason (UPPER_SNAKE); status is the
  canonical google.rpc.Code; dynamic context lives in metadata (string→string)
- built once in server/lib/http-errors.ts (buildErrorBody/ApiError/mapDomainError);
  inline handlers use apiError(c,status,msg,opts?); thrown errors flow through
  app.onError → renderError. Resolves #8 (one casing; no-storage 503 everywhere) and
  #9 (resource/maxBytes/conflictingName/licensing fields folded into metadata;
  featureGateErrorSchema removed)

Pagination → Page<T> = { items, total, page, pageSize } via pageSchema + integer
pageQuerySchema, applied to every list endpoint. image-hosting/images stays cursor
(the one intentional exception). unreadCount moved out of the notifications list into
/notifications/stats; entitlements drop the redundant orgId; team invitations use items.

Access log: every 4xx/5xx carries reason + full message (set by apiError and
renderError); a thrown domain error logs its mapped status (409, not 500); unhandled
500s log the full cause chain while the client gets a generic message.

Frontend ApiError exposes reason/metadata/canonicalStatus; consumers updated. Go
client regenerated from the new OpenAPI document.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(api): fix e2e name-conflict assertion + cover AIP-193 error branches

- e2e/name-conflict.spec.ts: assert body.error.details[0].reason (AIP-193) instead
  of the removed top-level body.code
- unit-test buildErrorBody, ApiError, and every mapDomainError branch
  (server/lib/http-errors.test.ts) and renderError + isHandledError
  (server/middleware/error-handler.test.ts)
- integration-test the apiError error-branch guards the refactor touched:
  shares, redirect, site/invitations, objects, store/storefront, and the
  requirePermission middleware (authz) — restoring patch coverage above target

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(api): drop ad-hoc [spec:] breadcrumbs from new coverage tests

lint:spec governs spec↔test traceability: a [spec: id] breadcrumb must map to a
documented @id scenario in spec/**/*.feature. The added error-branch coverage
tests are not Gherkin scenarios, so reference no spec id — use plain titles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(objects): allow the file-manager pageSize (500) on the objects list

The shared pageQuerySchema caps pageSize at 100, but the file manager loads a
whole folder client-side (FILES_PAGE_SIZE=500, transfer dialog 200) — the old
z.string() query param was unbounded. With the cap, GET /api/objects?pageSize=500
returned 400, the file-manager list query errored and retried, and the toolbar /
table never rendered (e2e: responsive @desktop + name-conflict table state). Raise
just this list's ceiling to 1000 (default stays 20); other lists keep the 100 cap.

Regression-tested: GET /api/objects?pageSize=500 → 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 22:58:35 -04:00
Jasper VanandClaude Opus 4.8 3402a1e099 refactor(api): RESTful resource-oriented API — drop /admin, status sub-resources, merge audience-split routers (#437)
* refactor(api): RESTful resource-oriented API — drop /admin, status sub-resources, merge audience-split routers

Reorganize the entire HTTP surface around resource abstraction instead of
business/audience abstraction.

- Auth: authMiddleware is now soft + global for /api/*; gating is per-route
  (requireAuth/requireAdmin/requireTeamRole), so one resource path serves
  public, user, and admin callers (no security change — guards moved, not dropped).
- Drop /admin from URLs; merge audience-split routers into one resource each
  (announcements, auth-providers, users, teams, quotas, invite-codes,
  site-invitations, downloaders, branding, audit).
- State transitions -> PUT /:id/status: objects (confirm/trash/restore),
  download-tasks (pause/resume/cancel), background-jobs, image-hosting confirm.
- Verbs -> noun sub-resources: objects/:id/copies, download-tasks/:id/attempts,
  background-jobs/:id/retries, site-invitations/:id/deliveries,
  licensing/pairings + /pairings/:code + refresh-runs, teams/:id/invite-links.
- Config -> /api/site/* (branding, email, options, instance, changelog);
  ihost -> image-hosting; me + profiles + admin/users -> one /api/users
  (the :username slot also resolves the internal id, so the admin UI is unchanged).
- External downloader OpenAPI contract cut over in lockstep.

Frontend (rpc.ts + api.ts) and all integration/CF/unit tests updated to match.
Typecheck (server + src), lint:http, biome, and all 4394 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(downloader): regenerate Go client + sync spec for the new RESTful contract

The Go downloader agent (cmd/) and the BDD spec live in this repo, so they must
move with the API:

- Regenerate docs/openapi/downloader.json and cmd/internal/openapi/client.gen.go
  from the updated server OpenAPI.
- Update the hand-written Go client: heartbeat -> /downloaders/me/heartbeats,
  register -> /downloaders, object confirm -> PUT /objects/:id/status, upload
  complete -> PUT .../status, abort -> DELETE .../uploads/:sid. Drop the now-dead
  union helpers (jsonBody/decodeJSON) and the bytes import.
- spec: drop the obsolete teams invite-token-missing scenario (the route is now
  a path param) and add the auth-providers anon-public-list scenario (the merged
  GET serves the public list to anonymous callers).

gofmt clean, go test (121) pass, lint:spec passes (418 scenarios covered).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(api): cover users admin detail/entitlements + getUser wrapper

Close the patch-coverage gaps from the users-resource merge: add integration
tests for GET /api/users/:id (admin detail, success + 404) and
GET /api/users/:id/entitlements (success + 404), and a unit test for the
getUser() api.ts wrapper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(e2e): update Playwright specs + global setup to the new RESTful paths

The e2e specs make direct API calls / response matchers that bypass the SPA, so
they need the new paths too: global-setup storage+options seeding
(/api/storages, /api/site/options), image-host (/api/image-hosting, confirm via
PUT /images/:id/status), object confirm in archive (PUT /objects/:id/status),
announcements and site-invitations (/api/announcements, /api/site-invitations,
/api/site/email). The cloud pairing action:'approve' is the external cloud API,
left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(e2e): fix cloud-store instance pairing path to /api/licensing/pairings

The cloud-store spec calls the INSTANCE pairing endpoint directly:
POST /api/licensing/pair -> /api/licensing/pairings and the poll
GET /api/licensing/pair/:code/poll -> GET /api/licensing/pairings/:code.
/api/licensing/status and /binding are unchanged; /api/pairings is the
external cloud API, left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(api): rename /api/site-invitations to /api/invitations

Avoids visual proximity with the /api/site/* config namespace. Top-level
/api/invitations is unambiguous — team invitations are nested under
/api/teams/:id/invitations and invite codes under /api/invite-codes. URL-only
change; the internal site-invitations naming stays (still the accurate concept).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(api): group resources by functional domain (URLs)

Move non-core resources under functional-domain prefixes (not permission):
- /api/site/* absorbs storages, auth-providers, audit-events, licensing,
  invitations, invite-codes (joining branding, email, options, instance, changelog)
- /api/downloads/* = tasks + downloaders (regenerated OpenAPI + Go client)
Core resources stay top-level. Updates app.ts, rpc.ts, OpenAPI doc + Go agent
client, and all integration/CF/e2e tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(server): mirror functional-domain grouping in http/ and usecases/ dirs

Reorganize source files to match the functional URL domains established for
the routes, so the directory tree reflects the same grouping as the API:

- http/{site,downloads,image-hosting}/ and usecases/{site,downloads,image-hosting}/
- dissolve the permission-based console/ dir — admin resources are grouped by
  domain (site), not by audience
- console/user -> top-level (users is a core resource, not an admin-only one)

Co-located tests move with their sources; relative imports and vi.mock paths
updated for the new depths. Pure file/directory restructure, no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(server): finish structural cleanup — merge split admin routers, drop rename leftovers

Three follow-ups from the directory-structure review, completing the
one-file-per-resource and domain-named-file conventions:

- Merge the last two audience-split router files into their resource file as a
  second export (matching branding/quotas/invite-codes/site-invitations):
  teams-admin.ts -> teams.ts (adminTeams), licensing-admin.ts -> licensing.ts
  (licensing + licensingAdmin).
- Drop pre-rename filename leftovers now that the dirs carry the domain:
  http/image-hosting/{ihost,ihost-config} -> {images,config};
  http/site/site-invitations -> invitations;
  usecases/site/{site-invitation,site-public-origin} -> {invitation,public-origin};
  usecases/image-hosting/{image-hosting,image-hosting-config} -> {images,config}.
- Group the loose store helpers under the store domain:
  http/{cloud-store-helpers,traffic-metering-utils} -> http/cloud-store/{helpers,traffic-metering}.

Routes and exports unchanged; pure file/structure move. tests + co-located
specs move with their sources. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(api): move announcements under /api/site, co-locate stray tests

Announcements is instance-level, admin-authored content (like branding) — a
site resource, not a top-level one. Move it under the site domain:
- /api/announcements -> /api/site/announcements (mount, RPC base path, api.test, e2e spec)
- http/announcements -> http/site/announcements; usecases/announcement -> usecases/site/announcement

Co-locate the tests that drifted from their sources during the dir reorg
(the 1:1-paired cf-test/unit tests belong next to what they exercise):
- http/storages.cf-test.ts -> http/site/ (next to storages.ts)
- usecases/{license-certificate,license-policy,license-refresh,licensing-admin}.test
  -> usecases/site/ (next to the licensing usecase; imports simplified to ./licensing)

No behavior change beyond the announcements path. Routes/exports otherwise stable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(usecases): de-fragment the users and objects domains at the usecase layer

The HTTP layer already serves these as single resources; consolidate their
usecases to match, removing leftover files that mirrored the old split:

- Fold me.ts (avatar) + profile.ts (public lookup) into user.ts — one user
  usecase with self/public/admin sections; drop the stale /api/me/avatar and
  /api/profiles/:username doc comments. Their unit tests move into user.test.ts.
- Fold matter.ts (confirmUpload, draft→active) into object.ts — the objects
  domain is now under one "object" name (the Matter *type* stays in ports/).

Importers updated; no behavior change. server tsc + lint:http + lint:spec clean;
Node 4337 / CF 57 / libsql 6 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(usecases): fold sub-concern usecases into their resource (one file per resource)

Consolidate the usecase layer so each resource is a single source file:

- object.ts absorbs object-upload-session, purge, and save-to-drive (its
  upload-session / recursive-purge / save-to-drive sub-concerns)
- share.ts absorbs share-notification and share-ref

External importers re-pointed (trash, redirect, entry-node, workers/scheduled,
http/share-utils, and the surviving integration/cf tests). share.ts now pulls
copyMatterToOrg/saveShareToDrive from object. share.test.ts asserts the real
notification+email fan-out now that dispatchShareCreated is intra-module.

Shared domain services (storage-usage, cloud-traffic-metering, captcha) stay
separate — they're used by many resources. 5 files removed; no behavior change.
Node 4337 / CF 57 / libsql 6 green; tsc + lint:http + lint:spec clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(http): collapse concern-split integration tests into one per resource

Each resource now has a single Node integration test file; the scenario-split
files fold into their resource's main:

- objects-quota + object-multipart-live -> objects.integration.test.ts
- me + profile -> users.integration.test.ts
- quotas-listing -> quotas.integration.test.ts
- teams-admin -> teams.integration.test.ts
- share-public -> shares.integration.test.ts (share-public.cf-test stays — CF runtime)

Helpers de-duplicated or scoped per describe; all [spec:] breadcrumbs preserved
(lint:spec still 418). 7 files removed, all 4337 tests retained. The multipart-live
block now restoreAllMocks so it exercises the real S3 gateway (latent bug fixed).
Node 4337 / CF 57 / libsql 6 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: finish test-file reorg + convert cloud licensing to a real Playwright e2e

Directory grouping (finishing the reorg): auth tests -> http/auth/, cloud-store
test -> cloud-store/, captcha + signup-mode -> usecases/site/ (with import-depth
fixes the moves needed).

One file per resource at the test layer:
- save-to-drive.integration + purge.integration -> object.integration.test.ts
- save-to-drive.cf-test -> object.cf-test.ts
- share-notification.integration -> share.integration.test.ts
- webdav.e2e (a vitest integration test, not Playwright) -> merged into
  webdav.integration.test.ts

Cloud licensing e2e: e2e-cloud-integration.test.ts was a vitest file mostly
duplicating existing integration coverage (licensing-admin.integration +
licensing-cloud.test) and the pairing e2e already in cloud-store.spec.ts.
Replaced with a real Playwright e2e (e2e/licensing.spec.ts): pair+approve ->
assert a Pro gate opens -> unbind -> assert it closes. Shared pairing helpers
extracted to e2e/helpers.ts (cloud-store.spec now imports them). run-cloud-e2e
runs both cloud specs in one tunnel; CI grep-invert excludes the new title from
the no-cloud run.

tsc + lint:http + lint:spec clean; Node 4337 / CF 57 / libsql 6 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(server): move the cloud-store domain under store/ (matches /api/store)

Following the dir move: http/cloud-store/* -> http/store/*, the cloud-store +
cloud-traffic-metering usecases -> usecases/store/, and the top-level
cloud-traffic-metering http integration test -> http/store/. The http/cloud-store.ts
barrel now re-exports from ./store/*. All importers + moved-file imports rewired.

tsc + lint:http + lint:spec clean; Node 4337 / CF 57 / libsql 6 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(server): drop the cloud- prefix under store/ now that the dir carries it

- usecases/store/cloud-store -> store.ts; cloud-traffic-metering -> traffic-metering.ts
- http/store/cloud-store.integration -> store.integration; cloud-traffic-metering
  .integration -> traffic-metering.integration
- the http barrel http/cloud-store.ts -> http/store/index.ts (re-exports from
  ./storefront + ./webhooks); app.ts imports './http/store'

store/ is now uniformly named (storefront/webhooks/helpers/shared/traffic-metering
+ store + index). tsc + lint:http + lint:spec clean; Node 4337 / CF 57 / libsql 6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(e2e): licensing spec asserts the bind/unbind lifecycle, not a pro-only gate

The cloud E2E account is business-tier; its pairing certificate does not grant
open_registration (that's why the old vitest test seeded a local pro cert for
that assertion). Assert the edition-agnostic licensing lifecycle instead:
pairAndApprove (binds + waits active) -> unbind -> /status reports bound:false.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:51:01 -04:00
Jasper VanandClaude Fable 5 6521d1b722 feat(events): unify SSE stream, replace frontend polling (#432)
* feat(events): unify SSE stream, replace frontend polling

Add a single /api/events SSE endpoint that multiplexes domains via named
events. Jobs and notifications are always-on; download tasks are an opt-in
per-connection subscription carried in the EventSource URL, so the server only
polls what an open page needs and a browser tab holds one connection.

- Replace refetchInterval polling: sidebar active-job badge, tasks list,
  notification unread count
- Fold the download-tasks SSE into the unified endpoint; remove the now-dead
  /api/download-tasks/events route, downloadTaskEventsUrl, downloadTasksUrlApi
- Client subscription registry: useServerEvents (single connection, reconnects
  when the merged subscription query changes) + useServerEventSubscription
- Decouple the keep-alive heartbeat (25s idle) from the 2s data poll

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(downloader): regenerate OpenAPI spec/client after dropping /events

Removing /api/download-tasks/events from the unified-SSE refactor drifted the
generated downloader OpenAPI doc and Go client (caught by openapi:downloader:check
in CI). Regenerate both. With the events operation gone, the single-value
assignedTo enum constant collapses from GetApiDownloadTasksParamsAssignedToMe to
Me, so update the hand-written client reference to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(events): cover unified SSE store and endpoint

Codecov flagged the new SSE code as uncovered.

- Unit-test the pure server-events-store (merge/sort query key, listener
  notifications, subscription lifecycle)
- Integration-test GET /api/events: 401 unauthenticated, and that an authed
  user with a queued job receives jobs + notifications events
- Exclude useServerEvents.ts (EventSource/React-effect glue, not unit-testable
  without a DOM) from coverage, matching the existing src/routes & src/components
  exclusions; its logic lives in the now-covered store

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(e2e): stop waiting on networkidle now that SSE is always connected

The unified /api/events stream is mounted on every authenticated page, so the
network never goes idle and waitForLoadState('networkidle') hangs until the job
times out. Drop the four networkidle waits (sign-in helper + image-host reloads);
subsequent navigations and element auto-waits already gate readiness.

Also broaden the /api/events test to cover the abort and error branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 00:14:24 -04:00
Jasper Van ac00164fb4 ci(e2e): run core checks before cloud store flow
ci(e2e): run core checks before cloud store flow
2026-06-08 10:55:11 -04:00
Jasper Van 9d70acfdea feat(licensing): support independent business authorization
Support independent Pro and Business licensing, migrate Cloud store integration through the SDK, gate Business-only credit billing features, and validate the Cloud store E2E flow.
2026-06-08 01:42:23 -04:00
saltbo 745abb4ce6 ci(e2e): skip live cloud tunnel outages 2026-06-04 20:46:22 -04:00
saltbo bff1c94c52 ci(e2e): skip live cloud test on Stripe rate limit 2026-06-04 20:28:19 -04:00
saltbo ced28102fa test(e2e): allow cloud storefront propagation 2026-06-04 18:56:26 -04:00
saltbo c26a9b4fd4 test(e2e): verify cloud products by id 2026-06-04 18:43:07 -04:00
saltbo 45b08702fa test: remove cloud store e2e preflight polling 2026-06-03 15:11:08 -04:00
saltbo 5b4d110723 test: tighten cloud store e2e selectors 2026-06-03 14:36:16 -04:00
saltbo 086319da69 test: harden cloud e2e tunnel handling 2026-06-03 14:19:17 -04:00
saltbo f6d54efb9b test: retry stripe rate limits in cloud store e2e 2026-06-03 14:02:23 -04:00
saltbo 87b40bb8ef test: stabilize cloud e2e auth flow 2026-06-03 03:10:44 -04:00
saltbo cf12806902 fix(e2e): clean cloud license slots 2026-06-02 22:52:14 -04:00
saltbo 4a54f2dfa3 fix(e2e): stabilize cloud pairing slots 2026-06-02 22:41:00 -04:00
saltbo 38adeb44f9 fix(store): align storage credits storefront 2026-06-02 14:08:13 -04:00
Jasper Van 908786837d fix: align store pricing with USD contract
* fix: align store pricing with usd contract

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* fix: align gift card cloud payload

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* fix: normalize cloud gift card credits

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* test: cover gift card payload without expiry

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* fix: keep gift card payload mapping executable

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* fix: map credits to cloud wallets

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* test: align credits e2e selector

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98

* fix: keep store credits on credits contract

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98
2026-06-02 01:44:00 -04:00
Jasper Van f45e8732fc feat: integrate cloud credits store flow
Replace ZPAN Store Wallet semantics with Credits-only APIs and UI.

Proxy Cloud credits through credit-account routes, send unitless gift-card credits, and block metered downloads on insufficient Cloud credits before presign while rolling back local counters.

Verification waiver: E2E fails only because the hosted Cloud target rejects the merged Credits gift-card payload; AK follow-up kxel47x6x6l3 tracks that target gap.
2026-06-01 22:31:46 -04:00
saltbo f5eab8f611 test: relax cloud e2e readiness timeouts 2026-06-01 12:27:04 -04:00
saltbo 76d615833d ci: stabilize cf cloud store e2e 2026-06-01 11:57:29 -04:00
saltbo 21b5d1bd88 test: clean stale cloud licenses in e2e 2026-05-15 18:46:00 -04:00
saltbo 2438275ea8 feat(archive): queue streaming archive jobs 2026-05-15 09:59:08 -04:00
saltbo 9efdcbb424 refactor(cloud-store): use cloud sdk contracts 2026-05-11 15:03:34 -04:00
saltbo 9aeccccd41 test(e2e): match cloud gift card list privacy 2026-05-11 10:22:24 -04:00
saltbo 0c070d8a83 fix(cloud-store): align gift cards with cloud model 2026-05-11 10:10:04 -04:00
saltbo 736fd00cc4 fix(cloud-store): use paged gift card create response 2026-05-11 09:57:14 -04:00
saltbo 63fccef791 test(e2e): avoid shared cloud license cleanup 2026-05-10 14:48:31 -04:00
saltbo ab9014133f test(e2e): isolate cloud runtime ports 2026-05-10 12:19:09 -04:00
saltbo a8aa2e8784 ci(e2e): isolate cloud account binding 2026-05-10 02:54:34 -04:00
saltbo ddeb49ded6 test(cloud): verify staging license unbind 2026-05-10 01:18:03 -04:00
saltbo 14a0723120 fix(cloud): keep staging e2e licenses isolated 2026-05-09 22:33:50 -04:00
saltbo 800cda9f9c test(cloud): add tunnel-backed store e2e 2026-05-09 22:17:52 -04:00
Jasper Van 0b65e2dc15 [v2.6] Integrate zpan with new cloud order flow and complete migration cleanup
fix(store): guard cloud order actions by org
2026-05-08 15:42:05 -04:00
saltbo ff5ef9b10c fix(announcements): highlight pinned items 2026-05-02 23:23:34 -04:00
saltbo 65b0c508ec fix(announcements): simplify publishing controls 2026-05-02 23:13:08 -04:00
saltbo fecff6d949 fix(announcements): use full markdown editor 2026-05-02 23:02:24 -04:00
saltbo 6314717795 feat(announcements): add site announcement management 2026-05-02 22:17:46 -04:00