mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-01 15:49:00 +08:00
2ae603bbab
* refactor(api)!: DELETE endpoints return 204 No Content (#443) Resolves item #4 of #443 — DELETE return-shape inconsistency (8 different conventions). Standardize every DELETE on 204 No Content with an empty body, dropping the ack/result bodies: `{id,deleted}`, `{providerId,deleted}`, `{key,deleted}`, `{id,revoked}`, `{ok:true}`, the download-task tombstone, license `{deleted,cloud_unbind_error}`, and the entitlement-revoke / abort-upload-session objects. Kept (the issue's flagged special case): object-delete and empty-trash still carry a purge count — the only delete responses with information a caller can't otherwise derive. Object delete is trimmed from `{id,deleted,purged}` to just `{ purged: number | false }`; empty-trash keeps `{ purged: number }`. Backend: 15 DELETE routes → `204: { description }` + `c.body(null, 204)`; removed the now-dead `deleteDownloaderResponseSchema`. Frontend: added a `discard()` helper (the 204 counterpart to `unwrap()`); the unwrap-based delete wrappers now resolve `void`. cancelUpload/deleteObject now return `{ purged }`. The already-void wrappers (deleteShare, deleteAvatar, …) were untouched — they never read the body. OpenAPI document + Go client regenerated (go build clean). BREAKING CHANGE: all DELETE endpoints now respond 204 with no body. License unbind no longer returns `cloud_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. `unbindLicense` now returns a Result — `{ ok: true }` only when the cloud unbind also succeeds, and a 502 AppError (reason `CLOUD_UNBIND_FAILED`, the cloud error in `details.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 orphaned `spec/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> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
72 lines
2.3 KiB
Gherkin
72 lines
2.3 KiB
Gherkin
Feature: User administration
|
|
Admin user management runs through better-auth's admin plugin
|
|
(/api/auth/admin/*). ZPan owns the per-user storage quota and the entitlement
|
|
grants against each user's personal org, and enforces bans in its auth
|
|
middleware.
|
|
|
|
@users/admin-only @api
|
|
Scenario: Non-admins cannot administer users
|
|
Given an authenticated non-admin user
|
|
When they call the better-auth admin user endpoints
|
|
Then the API responds 403
|
|
|
|
@users/list @api
|
|
Scenario: Admins list users
|
|
Given several users
|
|
When an admin lists users
|
|
Then the users are returned
|
|
|
|
@users/quota-personal-org @api
|
|
Scenario: A user's quota reflects their personal org
|
|
Given a user with a personal org
|
|
When an admin reads the user's quota
|
|
Then the quota reflects their personal organization
|
|
|
|
@users/disable @api
|
|
Scenario: Admins disable (ban) a user
|
|
Given an active user
|
|
When an admin bans them
|
|
Then the user is disabled
|
|
|
|
@users/patch-missing @api
|
|
Scenario: Acting on a missing user returns 404
|
|
Given a user id that does not exist
|
|
When an admin acts on it
|
|
Then the API responds 404
|
|
|
|
@users/disabled-session-rejected @api
|
|
Scenario: A disabled user's existing session is rejected
|
|
Given a user disabled mid-session
|
|
When they make an authenticated request
|
|
Then the auth middleware rejects it
|
|
|
|
@users/delete @api
|
|
Scenario: Admins delete a user
|
|
Given an existing user
|
|
When an admin removes them
|
|
Then the user is removed
|
|
|
|
@users/grant-entitlement @api
|
|
Scenario: Admins grant a storage entitlement
|
|
Given a user with a personal org
|
|
When an admin grants a storage entitlement
|
|
Then the entitlement is recorded against the personal org
|
|
|
|
@users/update-entitlement @api
|
|
Scenario: Admins update an admin-granted entitlement
|
|
Given an existing admin grant
|
|
When an admin updates it
|
|
Then the changes are persisted
|
|
|
|
@users/revoke-entitlement @api
|
|
Scenario: Admins revoke an admin-granted entitlement
|
|
Given an existing admin grant
|
|
When an admin revokes it
|
|
Then the entitlement is removed
|
|
|
|
@users/entitlement-source-guard @api
|
|
Scenario: Only admin-granted entitlements can be edited
|
|
Given an entitlement that was not admin-granted
|
|
When an admin tries to update or revoke it
|
|
Then the API rejects the operation
|