97 lines
5.7 KiB
Markdown
97 lines
5.7 KiB
Markdown
# Wallora Searcher Architecture Reference
|
|
|
|
## Surfaces And Roles
|
|
|
|
The project has three user-facing surfaces:
|
|
|
|
- Public API: available to external users. Use JWT login/authorization for protected API access. Keep contracts documented in `swagger.yaml`.
|
|
- Admin: available only after account/password login. Use NextAuth-backed sessions to persist admin login state and gate admin features.
|
|
- Public website: available to anyone on the public internet. Optimize pages for SEO, metadata, crawlability, and public performance.
|
|
|
|
Keep these surfaces separate in routing, middleware, authorization helpers, and mental model.
|
|
|
|
## Route Boundaries
|
|
|
|
Prefer clear route groups as the app grows:
|
|
|
|
- Public website: `app/(site)/...`
|
|
- Admin UI: `app/(admin)/admin/...`
|
|
- Protected admin UI: `app/(admin)/admin/(protected)/...`
|
|
- Public API: `app/api/...`
|
|
- Auth endpoints: `app/api/auth/...` for NextAuth
|
|
|
|
If the existing tree does not yet use route groups, introduce them only when it makes the current change clearer.
|
|
|
|
## Authentication And Authorization
|
|
|
|
- Public API authorization: validate JWTs explicitly for protected API routes. Define who issues the token before implementing token verification.
|
|
- Current app-user JWTs are issued by `/api/v1/auth/login` and `/api/v1/auth/register`, signed with `APP_JWT_SECRET` or `AUTH_SECRET`, and validated by server-only helpers before accessing `/api/v1/users/me/...`.
|
|
- Disabled app users should not be able to log in or use existing Bearer tokens because token validation reloads current `wg_app_users.status`.
|
|
- Admin authorization: use NextAuth account/password login and session checks. Do not rely on public API JWT logic as the admin session source.
|
|
- Only `/admin/login` is public under the admin surface. All other `/admin` pages need middleware/proxy protection and server-side `auth()` checks, preferably via a protected route-group layout.
|
|
- Server code must keep secrets server-only. Never expose service-role Supabase keys, Aliyun OSS secrets, NextAuth secrets, or JWT signing secrets in `NEXT_PUBLIC_*`.
|
|
- Keep authorization decisions close to the server boundary: route handlers, server actions, middleware, or server-only helpers.
|
|
|
|
## Data Storage
|
|
|
|
Use Supabase for application data.
|
|
|
|
- Prefix project-owned tables, indexes, triggers, and helper functions with `wg_` when practical.
|
|
- Enable and design RLS for tables exposed through Supabase APIs.
|
|
- Do not use user-editable metadata claims for authorization decisions.
|
|
- Model admin roles and API consumer permissions in trusted tables or trusted auth metadata.
|
|
- Verify Supabase behavior against current documentation before implementing schema, RLS, or auth-sensitive behavior.
|
|
- Use service role credentials only in server-only code, one-shot scripts, cron workers, or protected internal route handlers.
|
|
- Treat raw gallery rows as admin data. Public photo APIs should return picked rows only.
|
|
- App-facing gallery list/detail endpoints may accept an optional Bearer app-user JWT to add user-specific fields such as `isFavorited`; anonymous responses must still work.
|
|
- Keep picked state and OSS mirror metadata on `wg_gallery_photos`; use collections tables to group picked photos into public sets.
|
|
- Admin collection writes should validate that every collection item references a picked photo.
|
|
- Public collection APIs should filter on `is_published = true` and keep collection photo pagination compatible with infinite scroll.
|
|
- App user favorites should accept picked photos only, and preferences/download preferences should be stored as JSON in `wg_user_settings`.
|
|
- Keep phone and WeChat identity support as additive fields on `wg_app_users`; do not split them into separate user tables unless provider-specific complexity requires it later.
|
|
|
|
## Image Storage
|
|
|
|
Use Aliyun OSS for image files.
|
|
|
|
- Store files in OSS buckets; store metadata such as object key, URL, owner, MIME type, size, visibility, and related entity in Supabase.
|
|
- Prefer server-side signed upload or server-mediated upload for private/admin flows.
|
|
- Avoid placing OSS access keys in browser-exposed code.
|
|
- Existing OSS env support accepts `OSS_REGION`, `OSS_ACCESS_KEY_ID`, `OSS_ACCESS_KEY_SECRET`, `OSS_BUCKET`, and either `OSS_PUBLIC_URL` or `OSS_PUBLIC_BASE_URL`.
|
|
- Decide whether each image class is public, signed, or private before generating URLs.
|
|
|
|
## UI System
|
|
|
|
Use shadcn/ui as the base UI layer.
|
|
|
|
- Initialize and inspect shadcn project config before adding components.
|
|
- Use the project package runner: `pnpm dlx shadcn@latest ...`.
|
|
- Prefer existing shadcn components before writing custom controls.
|
|
- Use semantic tokens and component variants instead of raw color overrides.
|
|
- Compose forms, tables, dialogs, sheets, empty states, loading states, and feedback with shadcn primitives where available.
|
|
|
|
## SEO Website Pages
|
|
|
|
For public website pages:
|
|
|
|
- Use server-rendered pages by default.
|
|
- Keep page-level metadata accurate with Next.js metadata APIs.
|
|
- Use semantic HTML, crawlable content, canonical URLs where needed, and fast-loading assets.
|
|
- Avoid hiding important public content behind client-only rendering.
|
|
|
|
## API Documentation
|
|
|
|
Maintain `swagger.yaml` for public API contracts.
|
|
|
|
- Update it whenever API endpoints, auth requirements, request schemas, response schemas, status codes, or error formats change.
|
|
- Keep operation IDs stable and descriptive.
|
|
- Document JWT requirements per route instead of assuming all routes share the same auth behavior.
|
|
- Treat generated examples as contract examples, not placeholders.
|
|
|
|
## Scheduled Jobs
|
|
|
|
- Do not assume Vercel Cron. This project is not planned for Vercel.
|
|
- Prefer a one-shot script that can be called by crontab, PM2, Docker, or another scheduler.
|
|
- Keep scheduled routes protected with a secret when an HTTP trigger is useful.
|
|
- Never rely on `setInterval` inside a Next.js request/serverless process for durable jobs.
|