refactor(api)!: align client with canonical HTTP contract

Route all feature requests through business API modules, sync generated DTOs, and add communication tests and architecture documentation.

BREAKING CHANGE: migrate the client to plural resource paths, canonical snake_case fields, ISO timestamps, and explicit paginated responses.
This commit is contained in:
2026-09-30 12:04:58 +08:00
parent 00c6eccb63
commit 8ba0212eec
34 changed files with 1137 additions and 1623 deletions
+47 -57
View File
@@ -5,44 +5,44 @@
- `npm run dev` — Vite dev server with HMR
- `npm run lint` / `npm run lint:fix` — ESLint checks / automatic fixes for JS, TS, and Vue; warnings fail the check
- `npm run format:check` / `npm run format` — Prettier check / formatting for source, config, and docs
- `npm run check` — lint, formatting, style contracts, and typecheck
- `npm run check` — lint, formatting, style contracts, typecheck, and communication tests
- `npm run check:styles` — validates shared style contracts and blocks deprecated palettes/classes
- `npm run build` — `vue-tsc -b && vite build` (typecheck must pass or build aborts)
- `npm run typecheck` — standalone Vue / TypeScript typecheck
- `npm run preview` — previews the production build on strict port `5173`
- ESLint uses `eslint.config.js`; Prettier uses `.prettierrc.json`. Keep formatting rules in Prettier. No general test runner exists; `check:styles` remains the project-specific visual-system check.
- ESLint uses `eslint.config.js`; Prettier uses `.prettierrc.json`. Keep formatting rules in Prettier. Communication tests use Node’s built-in runner (`npm test`, Node 22.18+); `check:styles` checks the visual system.
## Architecture
- **Vue 3 + Vite + Tailwind CSS + Vue Router + Pinia + FastAPI + AMap (高德地图) + Naive UI**
- **Vue 3 + Vite + Tailwind CSS + Vue Router + Pinia + Hono + Better Auth + AMap (高德地图) + Naive UI**
- Path alias `@/` → `src/` (configured in both `vite.config.ts` and `tsconfig.app.json`)
- FastAPI client at `src/lib/api.ts`; API base URL comes from `VITE_API_URL`
- HTTP transport at `src/lib/api.ts`; business calls in `features/{clouds,profile,admin}/api.ts`; API base URL comes from `VITE_API_URL`
- AMap loaded lazily via `src/shared/map/amap.ts` with type declarations in `src/shared/map/types/amap.d.ts`
- UI language is Chinese (zh-CN)
- All persistence, storage, authorization, and admin operations go through the FastAPI backend
- All persistence, storage, authorization, and admin operations go through the Hono backend
## Directory Map
| Path | Purpose |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `src/features/` | Business modules; each owns its route views, components, stores, and composables as needed |
| `src/features/auth/` | Auth views, auth store, and Better Auth client |
| `src/features/clouds/` | Shared cloud detail/edit/like components, cloud-type and likes stores, cloud-type constants |
| `src/features/upload/` | UploadView, QuickUploadModal, and useUpload |
| `src/features/profile/` | Profile views, profile store, and ContributionHeatmap |
| `src/features/encyclopedia/` | Encyclopedia views and encyclopedia store |
| `src/features/{map,gallery,community,admin,system}/views/` | Remaining route views grouped by feature |
| `src/shared/map/` | Reusable location picker and mini map, AMap loader, and AMap declarations |
| `src/lib/` | Shared infrastructure: API client and adapters, SEO meta builder, Naive UI theme |
| `src/components/layout/` | AppHeader (top nav bar with auth state) |
| `src/style.css` | Global stylesheet assembly point; imports Tailwind, tokens, base rules, and shared components |
| `src/styles/` | Visual system source: semantic tokens, global square-corner baseline, shared `oc-*` component contracts |
| `src/lib/theme.ts` | Naive UI theme overrides aligned with the semantic tokens |
| `src/router/` | Route registration, lazy view imports, guards, and per-route SEO |
| `src/types/` | Shared domain models (`database.ts`), API DTOs (`api.ts`), and router meta (`router.d.ts`) |
| `docs/project-structure.md` | File ownership, dependency conventions, and examples for placing new code |
| `docs/style-system.md` | Concise implementation and naming guide for the visual system |
| `scripts/check-style-system.mjs` | Automated guard against deprecated palettes and malformed shared classes |
| Path | Purpose |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `src/features/` | Business modules; each owns its route views, components, stores, and composables as needed |
| `src/features/auth/` | Auth views, auth store, and Better Auth client |
| `src/features/clouds/` | Shared cloud detail/edit/like components, cloud-type and likes stores, cloud-type constants |
| `src/features/upload/` | UploadView, QuickUploadModal, and useUpload |
| `src/features/profile/` | Profile views, profile store, and ContributionHeatmap |
| `src/features/encyclopedia/` | Encyclopedia views and encyclopedia store |
| `src/features/{map,gallery,community,admin,system}/views/` | Remaining route views grouped by feature |
| `src/shared/map/` | Reusable location picker and mini map, AMap loader, and AMap declarations |
| `src/lib/` | Shared infrastructure: API client and adapters, SEO meta builder, Naive UI theme |
| `src/components/layout/` | AppHeader (top nav bar with auth state) |
| `src/style.css` | Global stylesheet assembly point; imports Tailwind, tokens, base rules, and shared components |
| `src/styles/` | Visual system source: semantic tokens, global square-corner baseline, shared `oc-*` component contracts |
| `src/lib/theme.ts` | Naive UI theme overrides aligned with the semantic tokens |
| `src/router/` | Route registration, lazy view imports, guards, and per-route SEO |
| `src/types/` | Frontend models (`models.ts`, `view-models.ts`), generated HTTP DTOs (`api-contract.ts`), and router meta (`router.d.ts`) |
| `docs/project-structure.md` | File ownership, dependency conventions, and examples for placing new code |
| `docs/style-system.md` | Concise implementation and naming guide for the visual system |
| `scripts/check-style-system.mjs` | Automated guard against deprecated palettes and malformed shared classes |
- When adding or moving source files, follow `docs/project-structure.md`: colocate business code under its owning feature; keep reusable location primitives in `shared/map` and application layout in `components/layout`.
- Import concrete files directly; route views remain lazy imports in `src/router/index.ts`.
@@ -71,45 +71,35 @@
## Auth Flow
- **`main.ts` initializes auth before mounting**: `authStore.initialize()` exchanges the HttpOnly refresh cookie for an access token. The app only mounts after this initial check.
- Access tokens live in memory only. Refresh cookies are managed by the backend and sent with `credentials: 'include'`.
- `src/lib/api.ts` performs one automatic refresh-and-retry after an authenticated request receives `401`.
- API auth state changes are synchronized back to the Pinia store through `opencloud:auth-updated` and `opencloud:auth-expired` window events.
- Email confirmation and password reset pages consume the opaque `token` query parameter generated by the backend.
- Passwords must contain at least 8 characters in registration, settings, and reset flows.
- Registration, email confirmation, login, password reset, profile creation, and uniqueness checks are backend responsibilities.
- `main.ts` calls `authStore.initialize()` before mounting: Better Auth checks the session cookie and then loads `/profiles/me`.
- The backend manages HttpOnly session cookies; browser requests use `credentials: 'include'`.
- `features/auth/authClient.ts` owns Better Auth SDK configuration and error adaptation. No custom access-token refresh or automatic mutation retry.
- Required business requests receiving 401 dispatch `opencloud:auth-expired`, which clears Pinia auth state. Public requests use `{ auth: false }` to suppress that event.
- Email confirmation and password reset consume the backend’s opaque `token` query parameter. Passwords require at least 8 characters.
## Stores
- **auth** — User session, profile, login/register/logout, username/password update, password reset. `initialize()` must be called before app mounts.
- **clouds** — Simple cache of cloud types returned by `GET /cloud-types`. Fetched once and shared across views.
- **encyclopedia** — Cloud types + user's collection (unlock state). Tracks `unlockPercent` for progress display. Depends on `authStore`.
- **encyclopedia** — Cloud-type catalog cache for encyclopedia pages; numeric rarity comes from the backend.
- **profile** — User profile pages. Fetches profile + cloud list per-user. Supports update/delete/visibility-toggle with optimistic cache patching; backend deletion also removes stored files.
## FastAPI Backend
## Backend and API Conventions
- Backend project lives in the sibling directory `../opencloud-backend`.
- Default development API URL is `http://localhost:8000/api/v1`; set `VITE_API_URL` for deployed environments.
- Frontend route guards are for navigation UX only. The backend enforces ownership, authentication, admin roles, and disabled-account rules.
- Images are uploaded as multipart form data. The backend stores originals, creates thumbnails, blurs coordinates, writes database records, and atomically unlocks badges.
## API Conventions
- All requests must go through `src/lib/api.ts`; do not call `fetch` directly from views, stores, or composables.
- API paths passed to `apiRequest()` are relative to `VITE_API_URL`, for example `/clouds` rather than `/api/v1/clouds`.
- Public requests explicitly use `{ auth: false }`. Authenticated requests use the default and receive a Bearer access token.
- JSON request bodies are plain objects. Image uploads use `FormData`; do not set the multipart `Content-Type` header manually.
- FastAPI errors use `detail`; `ApiError` converts string and validation-array details into a user-facing message.
- Backend DTO fields use `snake_case`. Page/store adapters may expose display-only camelCase fields such as `cloudTypeName`.
- Paginated endpoints return `items`, `page`, `page_size`, `total`, and `total_pages`.
- Cloud batch mutation endpoints accept at most 100 IDs. Profile and admin code split larger selections into chunks of 100.
- Current backend: sibling `../hono-api` (Hono + Better Auth + PostgreSQL + object storage), default `http://localhost:3000`.
- When changing requests, DTOs, pagination, or authentication, read `docs/api-architecture.md`; the backend `API.md` lists endpoints and migration details.
- Views/stores call their feature API module; only feature API modules call `lib/api.ts`. Better Auth SDK calls stay in the auth module.
- Backend `src/schemas/` generates `contracts/api.ts`; run frontend `npm run api:sync` to update `src/types/api-contract.ts`. For coordinated changes, run backend `contract:check` and frontend `api:check`; do not hand-edit generated DTOs.
- Use `query` objects, plain JSON bodies, and typed upload input. The API module handles URL encoding and FormData construction.
- Business JSON uses snake_case and ISO UTC date strings. Pagination returns `items/page/page_size/has_more`; use the explicit flag for navigation.
- Business errors use `{message, issues?}`; Better Auth keeps its SDK protocol. `ApiError` preserves HTTP status and error details.
- Batch mutations accept at most 100 UUID strings; feature API modules split larger selections. Completed batches remain applied after later failures.
- Backend permissions enforce ownership, verified sessions, roles, and disabled accounts; route guards provide navigation UX only.
## Gallery Pagination
- Page-based navigation (50 items/page), not infinite scroll.
- The backend resolves cloud-type/custom-type searches and `@username` searches.
- Each response includes `items`, `total`, `page`, `page_size`, and `total_pages`.
- Search debounced at 250ms.
- Page-based navigation, 50 items/page, with a 250ms search debounce.
- Backend resolves cloud-type names and `@username` searches. Next-page availability comes from `has_more`.
## Map Timeline
@@ -119,14 +109,14 @@
## Upload Flow
- `useUpload` handles file selection, previews, EXIF date extraction, validation, sequential multipart uploads, progress, and badge results. Thumbnail generation, coordinate blurring, storage, database insertion, and badge unlocking run on the backend.
- `useUpload` handles selection, previews, EXIF dates, validation, sequential uploads and progress. The cloud API module encodes multipart data; the backend creates previews, stores variants and inserts records. Coordinates are blurred by the current frontend forms.
- Batch items upload sequentially. If a later item fails, earlier successful uploads remain saved; there is no transactional rollback or resumable upload in the frontend.
- `UploadView` is the full-page batch uploader. `QuickUploadModal` is a single-image shortcut from the map page.
## Build & Deploy
- **Vercel** with `vercel.json`: SPA rewrites, security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy). No CSP or HSTS configured.
- Vite dev and preview servers both use strict port `5173`, matching the backend's default `CORS_ORIGINS` and `FRONTEND_URL`.
- Vite dev and preview servers both use strict port `5173`, matching the backend’s `CORS_ORIGIN` development setting.
- **SEO plugin** in `vite.config.ts`: generates `robots.txt` and `sitemap.xml` at build time. Auth-only routes, including `/encyclopedia`, are excluded from the sitemap and disallowed in `robots.txt`.
## Environment Variables
@@ -137,10 +127,10 @@ Required for map features:
Optional:
- `VITE_API_URL` — FastAPI base URL including `/api/v1` (defaults to `http://localhost:8000/api/v1`)
- `VITE_API_URL` — Hono root URL (defaults to `http://localhost:3000`, without `/api/v1`)
- `VITE_SITE_URL` — canonical frontend URL used by route SEO and generated sitemap; set it explicitly in deployed environments
Backend-only variables such as database credentials, JWT secrets, SMTP settings, cookie policy, CORS origins, and upload paths belong in `../opencloud-backend/.env`, never in the Vite environment.
Backend-only variables such as database credentials, Better Auth secrets, email-provider settings, cookie policy, CORS origins, and object-storage settings belong in `../hono-api/.env`, never in the Vite environment.
## Visual System