feat: session-authenticated /me usage surface + account Usage panel#108
Conversation
…107) Adds a read-only, session-cookie-gated /me/* surface on apps/api (GET /me/workspaces, GET /me/workspaces/:name/usage) so any signed-in user can see their own workspace memberships and usage, not just admins. Membership lookup via the AUTH service binding is the authorization check — non-member workspaces 404. Wires the account.astro Workspaces panel to consume it instead of listOrganizations(), showing role and per-workspace bytes/object usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (2)
🚫 Excluded labels (none allowed) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe API now exposes session-authenticated workspace membership and usage endpoints. The web account page uses these endpoints to display workspaces and usage details, while workspace record lookup is centralized and covered by route tests. ChangesWorkspace access flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
CodeRabbit (@coderabbitai) review |
✅ Action performedReview finished.
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
uploads-api | a2adfd5 | Commit Preview URL Branch Preview URL |
Jul 13 2026, 12:07 AM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
uploads-web | a2adfd5 | Commit Preview URL Branch Preview URL |
Jul 13 2026, 12:07 AM |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/src/routes/me.ts`:
- Around line 98-99: Update the usage-read flow around getWorkspaceUsage in the
route handler to obtain the storage abstraction through createStorage() from
packages/storage, then invoke the usage query through that returned object
instead of passing c.env.DB directly. Preserve the existing usageWithLimits
response behavior.
- Around line 83-84: Normalize the workspace name from c.req.param("name") at
the start of the "/workspaces/:name/usage" handler before authorization or any
other logic. Use the normalized value for all downstream membership checks and
workspace operations, preserving the existing handler behavior otherwise.
- Around line 38-40: Update the membership-fetching flow in the `/workspaces`
route to throw an appropriate 5xx AppError from `@uploads/errors` when the AUTH
request fails, returns a non-2xx response, or provides a malformed body; do not
convert these cases to an empty memberships array. Preserve the valid
Membership[] path, and let respondError serialize the thrown error so binding
failures and usage requests receive the correct failure status.
In `@apps/api/src/workspace.ts`:
- Around line 122-127: Move the workspace registry lookup out of
loadWorkspaceRecord and expose an equivalent workspace-record read through the
storage API in createStorage(). Update loadWorkspaceRecord to obtain the storage
abstraction and call that method, preserving the existing name validation, key,
JSON type, and cache TTL while removing direct env.REGISTRY access.
In `@apps/web/src/lib/api-client.ts`:
- Around line 27-28: Update the response parsing in the workspace and usage API
client methods to validate each entry’s required workspace and organization
fields, and validate that usage fields are numeric before returning them. Reject
malformed payloads and preserve the documented [] workspace fallback and null
usage fallback instead of passing invalid objects through.
In `@apps/web/src/pages/account.astro`:
- Around line 251-262: Update formatUsage to include the upload-period quota
using uploadsInPeriod, maxUploadsPerPeriod, uploadsRemaining, and periodStart,
alongside the existing storage and object metrics. Preserve the current
formatting for storage and object counts while rendering the API’s upload usage
and limit information.
- Around line 302-312: Update the workspace usage update flow around
getMyWorkspaceUsage so all workspace requests start concurrently after the list
renders, rather than awaiting each request sequentially inside the loop.
Preserve each workspace’s existing usage element lookup and text formatting, and
ensure one request’s delay or failure does not block updates for other
workspaces.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2372171e-fc15-49a7-a8df-857f4dfd16ff
📒 Files selected for processing (6)
apps/api/src/index.tsapps/api/src/routes/me.test.tsapps/api/src/routes/me.tsapps/api/src/workspace.tsapps/web/src/lib/api-client.tsapps/web/src/pages/account.astro
- /me memberships lookup now throws ServiceUnavailableError (auth_lookup_failed) on a non-ok or malformed AUTH response instead of reporting an outage as zero memberships; test added. - api-client validates workspace entries and numeric usage fields before returning them; malformed payloads fall back to []/null. - account page shows the monthly upload quota when a workspace has one. - per-workspace usage fetches now run concurrently via Promise.all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes #107.
Summary
src/routes/me.ts— session-cookie-authenticatedGET /me/workspacesandGET /me/workspaces/:name/usage, gated bysessionAuth, requireSessionUser(notrequireAdminUser). Memberships resolve via theAUTHservice binding's/internal/memberships, mapped to workspace names withworkspacesForOrg. Usage reusesgetWorkspaceUsage+usageWithLimits, same shape asGET /v1/:ws/usage. A workspace the caller isn't a member of 404s (workspace_not_found) — that membership check is the authorization boundary.loadWorkspaceRecordout ofworkspace.ts'sworkspaceAuthWithso/me/workspaces/:name/usagereuses the same KV read instead of duplicating it./meintosrc/index.tsbehind the existingadminUiCorspolicy (same origins,credentials: true, GET+OPTIONS) — CORS isn't the security boundary, the session check is.src/routes/me.test.ts, mirroringadmin-ui.test.ts's stub-AUTH pattern plus theUsageFakeD1harness — 401 with no session, membership-to-workspace mapping, 404 for a non-member workspace, and the usage+limits shape for a member.src/lib/api-client.tswithgetMyWorkspaces/getMyWorkspaceUsage,credentials: "include", defensive[]/nullon failure (same convention asauth-client.ts).account.astronow passesUPLOADS_API_ORIGINthrough likeadmin.astro, adds it to the CSPconnect-src, and the Workspaces panel now sources from/me/workspaces(shows role) instead oflistOrganizations(), with per-workspace usage (bytes used vs. limit, object count) fetched and rendered underneath each row.Non-goals (per the issue)
No file listing, no token UI, no write operations over the session surface.
Test plan
pnpm testinapps/api(189 tests) andapps/web(17 tests) — all passpnpm run typecheckin both apps under Node 24 — no new errors (the 2 pre-existingadmin.astroerrors are untouched and not from this PR)/accountsigned-in (not run in this session — no live session cookie in the sandbox)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes