10 KiB
10 KiB
AGENTS.md
Commands
npm run dev— Vite dev server with HMRnpm run build—vue-tsc -b && vite build(typecheck must pass or build aborts)npx vue-tsc -b— standalone typecheck (no script in package.json)- No linter, formatter, or test runner exists
Architecture
- Vue 3 + Vite + Tailwind CSS + Vue Router + Pinia + FastAPI + AMap (高德地图) + Naive UI
- Path alias
@/→src/(configured in bothvite.config.tsandtsconfig.app.json) - FastAPI client at
src/lib/api.ts; API base URL comes fromVITE_API_URL - AMap loaded lazily via
src/lib/amap.tswith type declarations insrc/types/amap.d.ts - UI language is Chinese (zh-CN)
- All persistence, storage, authorization, and admin operations go through the FastAPI backend
Directory Map
| Path | Purpose |
|---|---|
src/lib/ |
Singletons and utilities: FastAPI client, amap, canvas patch, cloudTypes constants, cloudBadges canvas renderer, SEO meta builder |
src/stores/ |
Pinia stores: auth, clouds (cloud_types cache), encyclopedia (collection + unlock tracking), profile (user pages + cloud CRUD) |
src/composables/ |
Vue composables: useUpload (batch multipart upload, EXIF extraction, badge result handling) |
src/components/cloud/ |
Cloud-related modals and widgets: ImageDetailModal, CloudEditModal, MapPickerModal, QuickUploadModal, MiniLocationMap |
src/components/layout/ |
AppHeader (top nav bar with auth state) |
src/components/profile/ |
ContributionHeatmap |
src/style.css |
Global visual system hooks: shared button/card utility classes, base typography, body background |
src/views/ |
Route-level page components (see Routes below) |
src/types/ |
TypeScript types: domain models (database.ts), API DTOs (api.ts), AMap declarations (amap.d.ts), router meta (router.d.ts) |
Routes
| Path | View | Auth Required |
|---|---|---|
/ |
MapView | No |
/login, /register, /forgot-password, /auth/confirm, /auth/reset-password |
Auth views | No |
/upload |
UploadView | Yes |
/encyclopedia |
EncyclopediaView | Yes |
/encyclopedia/:id |
CloudTypeView | Yes |
/gallery |
GalleryView | No |
/community |
CommunityView | No |
/profile |
ProfileView (own) | Yes |
/profile/settings |
ProfileSettingsView | Yes |
/profile/:id |
ProfileView (public) | No |
/admin |
AdminView | Yes (admin) |
/403 |
ForbiddenView | No |
/401 |
AuthRequiredView | No |
/:pathMatch(.*)* |
NotFoundView | No |
- Route guards in
router/index.ts:requiresAuthredirects to/401with the intended URL in theredirectquery;requiresAdminredirects to/403 - SEO meta tags applied per-route via
lib/seo.tsinrouter.afterEach
Auth Flow
main.tsinitializes 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.tsperforms one automatic refresh-and-retry after an authenticated request receives401.- API auth state changes are synchronized back to the Pinia store through
opencloud:auth-updatedandopencloud:auth-expiredwindow events. - Email confirmation and password reset pages consume the opaque
tokenquery 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.
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
unlockPercentfor progress display. Depends onauthStore. - 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 project lives in the sibling directory
../opencloud-backend. - Default development API URL is
http://localhost:8000/api/v1; setVITE_API_URLfor 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 callfetchdirectly from views, stores, or composables. - API paths passed to
apiRequest()are relative toVITE_API_URL, for example/cloudsrather 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 multipartContent-Typeheader manually. - FastAPI errors use
detail;ApiErrorconverts 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 ascloudTypeName. - Paginated endpoints return
items,page,page_size,total, andtotal_pages. - Cloud batch mutation endpoints accept at most 100 IDs. Profile and admin code split larger selections into chunks of 100.
Gallery Pagination
- Page-based navigation (50 items/page), not infinite scroll.
- The backend resolves cloud-type/custom-type searches and
@usernamesearches. - Each response includes
items,total,page,page_size, andtotal_pages. - Search debounced at 250ms.
Map Timeline
- Realtime mode: slider selects a minute of today, shows clouds captured within 2 hours before that time. Marker opacity decays with age.
- Archive mode: browse clouds by day or month. Toggling timeline controls closed auto-returns to realtime.
- Slider resets to current time each time the controls panel is opened.
Upload Flow
useUploadhandles 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.- 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.
UploadViewis the full-page batch uploader.QuickUploadModalis 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 defaultCORS_ORIGINSandFRONTEND_URL. - SEO plugin in
vite.config.ts: generatesrobots.txtandsitemap.xmlat build time. Auth-only routes, including/encyclopedia, are excluded from the sitemap and disallowed inrobots.txt. lib/canvas.ts: patchesHTMLCanvasElement.getContext('2d')to always passwillReadFrequently: true— needed for the badge card renderer inlib/cloudBadges.ts.
Environment Variables
Required for map features:
VITE_AMAP_KEY
Optional:
VITE_API_URL— FastAPI base URL including/api/v1(defaults tohttp://localhost:8000/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.
Naive UI
- Used for modals, buttons, inputs, tags, progress bars, alerts, dropdowns, empty states, skeletons, and message toasts.
- No SSR — pure client-side rendering.
- Custom CSS overrides via scoped
<style>blocks for slider styling, transitions, and component tweaks. - Do not rely on default Naive UI primary/secondary colors for page CTAs or panel actions. The project now uses shared global classes in
src/style.cssto keep buttons visually consistent with the sky-atlas theme. - Auth buttons use
oc-primary-buttonwith semantic variants:oc-primary-button--teal— login / account-access actionsoc-primary-button--sky— register / create / forward actions
- Panel and utility buttons use
oc-panel-buttonwith semantic variants:oc-panel-button--neutral— white card-style utility actionsoc-panel-button--sky— primary panel actionsoc-panel-button--teal— active toggles / confirm actionsoc-panel-button--danger— destructive actionsoc-panel-button--amber— admin/state-toggle actions where warning emphasis fits better than danger
- Card-like containers should prefer shared shadow classes instead of ad-hoc shadows:
oc-panel-cardoc-panel-card-softoc-empty-card
- When styling
NButton, prefertype="default"plus the shared class when a custom panel button variant is intended. Otherwise Naive UI theme variables may override white backgrounds or semantic colors.
Visual Notes
- The site icon and header brand mark are a matching pixel-style cloud motif; avoid reintroducing emoji or unrelated icon styles for primary brand surfaces.
- Header action buttons use slight hover lift (
translate(-1px, -1px)) with hard-offset shadow growth; new header-like actions should follow that interaction pattern. - Heatmap view toggles in
ContributionHeatmapintentionally use separate buttons with gap spacing instead ofNButtonGroup, because grouped buttons visually collide once hard shadows and hover transforms are applied.
MVP Constraints
- No realtime updates — refresh-based loading
- No OAuth — email/password only, email confirmation required
- No AI cloud identification — manual type selection
- AMap only (China-focused), no Mapbox fallback yet