feat: add real account workspace controls

This commit is contained in:
2026-07-02 19:52:29 -07:00
parent 1b8fae0116
commit 1cdf89c608
12 changed files with 759 additions and 203 deletions

View File

@@ -4,6 +4,60 @@ This document covers backend authentication integration using better-auth, inclu
## 1. Overview
### Current Project Auth Contract: Session Organization and Profile APIs
#### 1. Scope / Trigger
- Trigger: app shell and account settings need authenticated current-tenant switching and current-account profile updates.
- This repository currently uses the custom `teatea_session` HTTP-only cookie plus Drizzle tables (`sessions`, `accounts`, `organizations`, `memberships`, `roles`), not a better-auth runtime.
#### 2. Signatures
- `GET /api/auth/session`: returns current account, active organization, available organization options, membership, and permissions.
- `POST /api/auth/organization`: switches `sessions.activeOrganizationId` for the current session.
- `PATCH /api/account/profile`: updates the authenticated account's `name` and `avatarUrl`.
#### 3. Contracts
- `GET /api/auth/session` response payload:
- `account: PublicAccount | null`
- `organization: Organization | null`
- `organizations: AccountOrganizationOption[]`
- `membership: Membership | null`
- `permissions: Permission[]`
- `POST /api/auth/organization` request payload: `{ organizationId: string }`.
- `PATCH /api/account/profile` request payload: `{ name: string; avatarUrl: string }`.
- All responses use `{ success, reason, ...payload }` and `Cache-Control: no-store`.
#### 4. Validation & Error Matrix
- Missing/expired session -> `success: false`, `401`, `未登录或会话已过期`.
- Empty `organizationId` -> `success: false`, `400`, `机构不能为空`.
- Organization not in authenticated account's available organization list -> `success: false`, `403`, `无权切换到该机构`.
- Empty profile `name` -> `success: false`, `400`, `用户名称不能为空`.
#### 5. Good/Base/Bad Cases
- Good: platform account with organization-read permission can switch among active organizations.
- Base: organization user can switch only among active memberships.
- Bad: never trust a client-provided organization ID without comparing against server-computed organization options.
#### 6. Tests Required
- Session API asserts `organizations` includes `isActive`, `slug`, and `roleLabel`.
- Organization switch asserts session row changes and forbidden org IDs are rejected.
- Profile update asserts current account only is updated and audit log is recorded.
#### 7. Wrong vs Correct
Wrong:
```typescript
await database.update(sessions).set({ activeOrganizationId: body.organizationId });
```
Correct:
```typescript
const target = context.organizations.find((organization) => organization.id === organizationId);
if (!target) {
throw new Error("无权切换到该机构");
}
await database.update(sessions).set({ activeOrganizationId: organizationId }).where(eq(sessions.id, context.session.id));
```
### What is better-auth
better-auth is a modern authentication library for TypeScript applications that provides: