Retain purged matter and image metadata while hiding tombstones from live reads. Record exact storage deltas and build hourly trends only after the opening balance.
Build versioned UTC hourly rollups, validate analytics facts, and keep dashboard requests result-only with explicit coverage semantics.
Move derived metrics to the server and simplify the admin dashboard around one reconcilable UTC range without export.
* docs: add spaces/quota/sharing design decisions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(store): require team owner role for billing and purchase endpoints
Team orgs now gate checkout, billing portal, credits (balance/ledger/
redemptions), and order management behind the owner role. Personal orgs
are unaffected. Implements docs/design/spaces-quota-sharing.md §2.1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(store): clarify purchase target and gate billing UI to owners
The store page now states which space purchases fund, hides purchase
and billing surfaces from non-owner team members with guidance to
contact the owner, and labels team orders with the team name on the
cloud side. Implements docs/design/spaces-quota-sharing.md §2.1.2-3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(objects): cross-space copy/move with file manager entry
Adds POST /api/objects/:id/transfers (copy or move a file/folder into
another space) reusing the save-to-drive copy engine; move = copy +
trash source, and the source survives any partial copy. The file
manager gains a 'Copy/Move to space' action with a space/folder picker.
Also fixes a privilege hole: save-to-drive (and the new transfer
endpoint) previously accepted any personal org as a write target,
allowing writes into other users' personal spaces; targets are now
restricted to orgs with editor access or the caller's own personal
org. The transfer folder picker also fixes the save-to-drive dialog
listing the active org's folders instead of the selected target's.
Implements docs/design/spaces-quota-sharing.md §3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(admin): per-team quota management and default team quota
Admins can now grant, edit, and revoke storage entitlements for any
space (team or personal) via /api/admin/quotas/:orgId/entitlements and
a new admin Quotas page. New teams take their initial quota from the
default_team_quota system option (falling back to default_org_quota),
configurable in admin settings. Completes the v2.2 roadmap item
'Per-team storage quota set by admin'; implements design doc §2.3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(quotas): allocate purchased storage packs between owned spaces
Space owners can move whole one-time purchased packs (cloud_order
grants) between spaces they own via
POST /api/quotas/me/entitlements/:id/transfers. Plans and admin grants
are not transferable, and a transfer is blocked when the source space's
usage would exceed its remaining quota. The storage page lists the
current space's packs with a move dialog. Implements design doc §2.2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(shares): received shares inbox on the shares page
GET /api/shares?box=received lists active shares addressed to the
current user (matched by user id or the email the share targeted),
with the sharer's name. The shares page gains a sent/received toggle;
received items open the share landing page. This is an inbox of share
links, not a mounted filesystem (design doc §4.4).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(hooks): cover default team quota in site options hook
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(store,admin): drop purchase-target banner; scope admin quotas page to teams
The storage page no longer shows the 'purchases fund X space' line —
owners see the store normally and non-owner members keep the guidance
notice. The admin Quotas page now lists team spaces only; personal
quotas stay on the user detail page, removing the overlapping entry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* revert(quotas): remove storage pack allocation
Allocation (§5.5) operated on an empty set: the store only sells
per-workspace subscriptions, and the original design restricted moves
to one-time cloud packs, which don't exist in the catalog. Subscriptions
can't be safely allocated either — the webhook cancellation/downgrade
path matches on the original targetOrgId, so a moved entitlement's
claw-back silently fails and leaves ghost capacity. The feature is also
redundant: family owners subscribe the team space directly (§2.1) and
self-hosted admins grant capacity straight to it (§2.3).
Removes the quota-allocation service, /api/quotas/me/entitlements
endpoints, the storage-page packs panel, isOrgOwner helper, and related
tests/i18n. Design doc §2.2 updated to record why it was deferred and
the prerequisite (a one-time pack SKU) for revisiting.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(admin): replace quotas page with Teams management (list + detail)
Promotes the admin quota surface to a proper Teams section, sibling to
Users: a team list page where each row opens a team detail page, and the
detail page manages quota entitlements (grant/edit/revoke) — mirroring
the user detail page. All backed by org data.
- New /api/admin/teams (list + detail) with member count, owner, and
effective storage usage; teams identified by non-personal slug so
legacy teams with null metadata are included.
- New /admin/teams list + /admin/teams/$orgId detail routes; the old
/admin/quotas page is removed and the nav item becomes 'Teams'.
- Entitlement CRUD continues to reuse the org-generic
/api/admin/quotas/:orgId/entitlements endpoints (invisible to users).
- Personal-space quotas remain on the user detail page.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(admin): use a distinct icon for the Teams nav item
Users and Teams both used people icons (Users / UsersRound) and were
hard to tell apart in the sidebar. Teams now uses Building2 (org).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(admin): chunk team member/owner IN-lists under D1's param cap
listTeams bound all team orgIds into single member-count and owner-name
queries; on D1 (100 bound-param cap) that breaks past ~100 teams.
getEffectiveQuotasByOrg already chunks at 90 — match it for the two new
queries. Members of a given org all land in one chunk, so per-org owner
ordering is preserved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>