first commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
---
|
||||
name: wallora-searcher
|
||||
description: Project-specific guide for working on the Wallora searcher Next.js app. Use when Codex is asked to modify, debug, design, review, build, lint, or explain this project, especially public APIs with JWT auth, admin session auth, protected admin pages, SEO public pages, shadcn UI, Supabase data with wg_ table prefixes, gallery photo sync jobs, Aliyun OSS images, NextAuth authentication, Swagger/OpenAPI docs, files under app/, public/, Next.js configuration, Tailwind CSS styling, package scripts, or project setup.
|
||||
---
|
||||
|
||||
# Wallora Searcher
|
||||
|
||||
## Purpose
|
||||
|
||||
Use this skill to work consistently inside the Wallora searcher project. Treat the local codebase as the source of truth and keep changes aligned with the current Next.js App Router, React, TypeScript, and Tailwind CSS setup.
|
||||
|
||||
## First Steps
|
||||
|
||||
1. Confirm the working directory is the project root: `/Users/luoyangwei/Documents/workspace/wallora.top/searcher`.
|
||||
2. Read `references/project.md` when the task involves commands, current baseline state, or project conventions.
|
||||
3. Read `references/architecture.md` when the task involves API design, authentication, authorization, admin features, public website pages, data storage, image upload/storage, or API documentation.
|
||||
4. Read `references/supabase-gallery-sync.md` when the task involves gallery photos, Unsplash sync, cron jobs, Supabase migrations, or `wg_gallery_*` tables.
|
||||
5. Inspect the relevant files before editing. Prefer `rg` and targeted `sed -n` reads.
|
||||
|
||||
## Project Workflow
|
||||
|
||||
- Use `pnpm` commands because the project has `pnpm-lock.yaml` and `pnpm-workspace.yaml`.
|
||||
- Use the App Router under `app/`; avoid creating a `pages/` tree unless the project intentionally changes architecture.
|
||||
- Keep UI work in React Server Components by default. Add `"use client"` only when interactivity, state, effects, or browser APIs require it.
|
||||
- Use shadcn/ui as the base UI system once it is initialized. Check `components.json` and installed UI components before importing or adding components.
|
||||
- Prefer Tailwind utility classes and the existing `app/globals.css` theme variables before adding custom CSS.
|
||||
- Keep metadata in `app/layout.tsx` current when building user-facing pages.
|
||||
- Do not assume a Git repository is present in this directory; verify before using Git-based workflows.
|
||||
- Do not add Vercel-specific deployment config unless the user explicitly changes the deployment target. This project is not planned for Vercel.
|
||||
|
||||
## Product Architecture
|
||||
|
||||
- Separate three surfaces clearly: public API clients, authenticated admin users, and public SEO website visitors.
|
||||
- Treat API JWT authorization and admin session authorization as different trust boundaries. Do not reuse admin session checks as public API authorization.
|
||||
- Store application data in Supabase and design RLS/policies deliberately for every exposed table.
|
||||
- Prefix application-owned Supabase table names with `wg_`.
|
||||
- Store image files in Aliyun OSS; keep metadata, ownership, and public/private visibility decisions in Supabase.
|
||||
- Treat raw gallery rows as admin curation data. Public gallery APIs should expose picked rows only.
|
||||
- Picked photos use `wg_gallery_photos.is_picked` plus `picked_*` and `oss_*` metadata; admin pick/unpick should mirror selected images to Aliyun OSS when `OSS_*` env vars are configured.
|
||||
- App photo APIs live under `/api/v1/gallery/photos`; list/detail endpoints expose picked photos only and include `isFavorited` when a Bearer app-user JWT is provided.
|
||||
- Collections are built from picked photos with `wg_photo_collections` and `wg_photo_collection_items`; admin CRUD lives at `/admin/collections` and public APIs live under `/api/v1/gallery/collections`.
|
||||
- App user features use `wg_app_users`, `wg_user_favorites`, and `wg_user_settings`; public app user auth is email/password plus Bearer JWT, kept separate from admin NextAuth sessions. Admins can view and enable/disable app users under `/admin/users`.
|
||||
- Use NextAuth for authentication flows where appropriate, especially account/password admin login and session persistence.
|
||||
- Generate and update `swagger.yaml` when API routes or request/response contracts change.
|
||||
- Protect every page under `app/(admin)` except `/admin/login`. Use route middleware/proxy and server-side layout checks for protected admin route groups.
|
||||
|
||||
## Frontend Expectations
|
||||
|
||||
- Build the actual usable experience as the first screen; avoid placeholder landing copy unless explicitly requested.
|
||||
- Keep layouts responsive from mobile through desktop using stable dimensions, clear spacing, and readable type.
|
||||
- Use visual assets for website/app experiences when they materially improve the feature. Prefer existing public assets only when they are relevant.
|
||||
- Avoid adding decorative gradients, oversized marketing sections, or generic template aesthetics unless the user asks for that direction.
|
||||
- When adding interactive UI, include loading, empty, error, and disabled states where the workflow naturally needs them.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the smallest useful checks after edits:
|
||||
|
||||
```bash
|
||||
pnpm format:check
|
||||
pnpm lint
|
||||
pnpm build
|
||||
```
|
||||
|
||||
For visual or interaction changes, start the dev server with `pnpm dev` and verify in the browser at `http://localhost:3000`. Use screenshots or browser inspection when layout quality matters.
|
||||
|
||||
If a command fails because dependencies are missing, install with `pnpm install` only after confirming that is appropriate for the workspace.
|
||||
|
||||
## References
|
||||
|
||||
- `references/project.md`: project snapshot, file map, scripts, conventions, and current baseline notes.
|
||||
- `references/architecture.md`: role model, auth boundaries, storage choices, API documentation expectations, and implementation guardrails.
|
||||
- `references/supabase-gallery-sync.md`: `wg_` table naming, gallery sync schema, Unsplash cron workflow, and non-Vercel scheduler expectations.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Wallora Searcher"
|
||||
short_description: "Guide work on the Wallora searcher app"
|
||||
default_prompt: "Use $wallora-searcher to implement a focused change in the Wallora searcher project."
|
||||
@@ -0,0 +1,96 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,109 @@
|
||||
# Wallora Searcher Project Reference
|
||||
|
||||
## Snapshot
|
||||
|
||||
- Root: `/Users/luoyangwei/Documents/workspace/wallora.top/searcher`
|
||||
- Package: `searcher`
|
||||
- Framework: Next.js `16.2.6` with App Router
|
||||
- React: `19.2.4`
|
||||
- Styling: Tailwind CSS v4 via `@import "tailwindcss";`
|
||||
- TypeScript: enabled
|
||||
- Package manager: `pnpm`
|
||||
- Git: this directory is not currently a Git repository
|
||||
- Intended UI system: shadcn/ui
|
||||
- Intended data store: Supabase with `wg_` table prefixes for project-owned tables
|
||||
- Intended image store: Aliyun OSS
|
||||
- Intended auth foundation: NextAuth plus JWT authorization for public APIs
|
||||
- Intended API documentation: `swagger.yaml`
|
||||
|
||||
## Important Files
|
||||
|
||||
- `app/layout.tsx`: root HTML shell, metadata, body layout classes, CSS-variable font tokens
|
||||
- `app/(site)/page.tsx`: public website home page
|
||||
- `app/(admin)/admin/login/page.tsx`: public admin login page
|
||||
- `app/(admin)/admin/(protected)/...`: admin pages that require a NextAuth session
|
||||
- `app/(admin)/admin/(protected)/gallery/page.tsx`: admin gallery picker with picked/color filters
|
||||
- `app/(admin)/admin/(protected)/collections/page.tsx`: admin collections CRUD surface
|
||||
- `components/admin/collections/CollectionsManager.tsx`: create/edit/delete published collections from picked photos
|
||||
- `app/api/admin/gallery-photos/route.ts`: protected admin gallery list and pick/unpick endpoint
|
||||
- `app/api/admin/photo-collections/route.ts`: protected admin collection list/create API
|
||||
- `app/api/admin/photo-collections/[id]/route.ts`: protected admin collection read/update/delete API
|
||||
- `app/api/v1/gallery/photos/route.ts`: public picked-photo API for waterfall pagination
|
||||
- `app/api/v1/gallery/photos/[id]/route.ts`: public picked-photo detail API plus favorite/unfavorite shortcut
|
||||
- `app/api/v1/gallery/collections/route.ts`: public published collection list API
|
||||
- `app/api/v1/gallery/collections/[slug]/route.ts`: public published collection detail API with paginated photos
|
||||
- `app/api/v1/auth/register/route.ts`: app user email/password registration
|
||||
- `app/api/v1/auth/login/route.ts`: app user email/password login and Bearer JWT issuance
|
||||
- `app/api/v1/users/me/route.ts`: current app user profile from Bearer JWT
|
||||
- `app/api/v1/users/me/favorites/...`: app user favorites list/add/remove APIs
|
||||
- `app/api/v1/users/me/settings/route.ts`: app user preferences and download preferences APIs
|
||||
- `app/(admin)/admin/(protected)/users/page.tsx`: protected admin app user list
|
||||
- `components/admin/users/UsersManager.tsx`: admin app user list with enable/disable actions
|
||||
- `app/api/admin/app-users/[id]/route.ts`: protected admin app user status update API
|
||||
- `app/api/cron/gallery-photos/route.ts`: protected gallery sync trigger endpoint
|
||||
- `app/globals.css`: Tailwind import, CSS variables for background/foreground, font theme tokens
|
||||
- `package.json`: scripts and dependencies
|
||||
- `next.config.ts`: Next.js config
|
||||
- `public/`: starter SVG assets from create-next-app
|
||||
- `swagger.yaml`: create or update when public API contracts are introduced or changed
|
||||
- `supabase/migrations/`: SQL migration scripts, including `wg_` gallery tables
|
||||
- `scripts/sync-gallery-photos.mjs`: one-shot gallery sync script for server cron/process managers
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
pnpm lint
|
||||
pnpm build
|
||||
pnpm start
|
||||
pnpm sync:gallery
|
||||
```
|
||||
|
||||
Use `pnpm format:check`, `pnpm lint`, and `pnpm build` for verification. Use `pnpm sync:gallery` only when intentionally touching the real Supabase database.
|
||||
|
||||
## Current Baseline Notes
|
||||
|
||||
- The public home page still contains create-next-app starter content unless updated later.
|
||||
- The admin dashboard uses shadcn `dashboard-01` under `/admin/dashboard`.
|
||||
- The admin login page uses shadcn `login-03` and NextAuth credentials under `/admin/login`.
|
||||
- The admin gallery supports picked/unpicked filters, color-family filters, pick/unpick actions, and OSS mirror status.
|
||||
- The admin collections page supports create, edit, delete, publish/draft status, cover selection, and selecting photos from picked photos.
|
||||
- Public photo consumption should use `/api/v1/gallery/photos`, which returns picked photos only.
|
||||
- App photo detail should use `/api/v1/gallery/photos/{id}`. Public callers can omit auth; authenticated callers receive `isFavorited` and can `POST`/`DELETE` the same route to favorite/unfavorite.
|
||||
- Public collection consumption should use `/api/v1/gallery/collections` and `/api/v1/gallery/collections/{slug}`; collection photos are picked photos only.
|
||||
- App users register and login with email/password via `/api/v1/auth/register` and `/api/v1/auth/login`; the returned Bearer JWT is for public app APIs only.
|
||||
- App user favorites and settings live under `/api/v1/users/me/...`.
|
||||
- Admins can review app users and enable/disable accounts under `/admin/users`.
|
||||
- Bare `/dashboard` and `/login` routes should stay absent; admin templates belong under `/admin`.
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
- Keep components typed with TypeScript and follow existing double-quote import style.
|
||||
- Prefer server components unless client behavior is necessary.
|
||||
- Put global tokens and truly global styles in `app/globals.css`; keep page-specific styling near components with Tailwind classes.
|
||||
- Keep font tokens in CSS variables; avoid `next/font/google` unless the deployment environment can reliably fetch Google Fonts during build.
|
||||
- Avoid broad dependency additions for small UI tasks; use the platform and existing stack first.
|
||||
- Use `pnpm dlx shadcn@latest ...` for shadcn CLI tasks.
|
||||
- Verify current Supabase, NextAuth, and Aliyun OSS APIs before implementing auth-sensitive or storage-sensitive behavior.
|
||||
- For database objects created by this app, use the `wg_` prefix, e.g. `wg_gallery_photos`.
|
||||
- Do not read or print secrets from `.env.local`; report only whether required variables are present.
|
||||
- This project is not targeting Vercel. Prefer server cron, process manager jobs, or external scheduler calls over `vercel.json` cron config.
|
||||
|
||||
## Design Conventions
|
||||
|
||||
- Replace starter content with domain-specific product UI when asked to build the app experience.
|
||||
- Keep cards at `8px` radius or less unless a future design system says otherwise.
|
||||
- Do not nest cards inside cards.
|
||||
- Avoid one-note palettes and generic purple/blue gradient-heavy layouts.
|
||||
- Make text fit inside controls and panels at mobile and desktop widths.
|
||||
|
||||
## Architecture Summary
|
||||
|
||||
- Public API: available to external users; use JWT login/authorization and document routes in `swagger.yaml`.
|
||||
- Admin: account/password login; use NextAuth sessions to persist login state and gate admin features.
|
||||
- Public website: open internet access; optimize for SEO, metadata, crawlability, and performance.
|
||||
- Supabase stores application data with `wg_` table prefixes; Aliyun OSS stores image files while Supabase stores image metadata.
|
||||
- Gallery photo ingestion writes to `wg_gallery_photos` and tracks progress in `wg_gallery_sync_state`.
|
||||
- Picked photo metadata lives on `wg_gallery_photos` via `is_picked`, `picked_at`, `picked_by`, and `oss_*` fields.
|
||||
- Collections live in `wg_photo_collections` and `wg_photo_collection_items`.
|
||||
- App users/favorites/settings live in `wg_app_users`, `wg_user_favorites`, and `wg_user_settings`; identity fields include email now and schema placeholders for phone/WeChat later.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Supabase Gallery Sync Reference
|
||||
|
||||
## Naming
|
||||
|
||||
Use `wg_` for project-owned Supabase objects.
|
||||
|
||||
- Gallery table: `wg_gallery_photos`
|
||||
- Sync state table: `wg_gallery_sync_state`
|
||||
- Collections tables: `wg_photo_collections`, `wg_photo_collection_items`
|
||||
- User tables: `wg_app_users`, `wg_user_favorites`, `wg_user_settings`
|
||||
- Example trigger/function names: `set_wg_gallery_photos_updated_at`, `set_wg_updated_at`
|
||||
|
||||
Do not introduce unprefixed replacements such as `gallery_photos`.
|
||||
|
||||
## Files
|
||||
|
||||
- `supabase/migrations/20260530000000_create_wg_gallery_tables.sql`: table/RLS/index/trigger migration.
|
||||
- `lib/supabase/service-role.ts`: server-only Supabase service-role client.
|
||||
- `supabase/migrations/20260601000000_extend_gallery_pick_collections_users.sql`: picked-photo, OSS metadata, collections, and user/favorite/settings schema extension.
|
||||
- `server/api/unsplash.ts`: official Unsplash API fetch and response validation.
|
||||
- `server/admin/gallery-sync.ts`: reusable sync implementation for Next route handlers.
|
||||
- `server/admin/gallery-pick.ts`: admin pick/unpick implementation and OSS mirror call.
|
||||
- `server/storage/aliyun-oss.ts`: server-only Aliyun OSS PUT Object helper.
|
||||
- `app/api/admin/gallery-photos/route.ts`: protected admin list + pick/unpick API.
|
||||
- `app/api/admin/photo-collections/route.ts`: protected admin collection list/create API.
|
||||
- `app/api/admin/photo-collections/[id]/route.ts`: protected admin collection read/update/delete API.
|
||||
- `app/api/v1/gallery/photos/route.ts`: public picked photos API.
|
||||
- `app/api/v1/gallery/collections/route.ts`: public published collections API.
|
||||
- `app/api/v1/gallery/collections/[slug]/route.ts`: public published collection detail API.
|
||||
- `app/api/cron/gallery-photos/route.ts`: protected HTTP trigger, guarded by `CRON_SECRET`.
|
||||
- `scripts/sync-gallery-photos.mjs`: one-shot Node script for server cron/process managers.
|
||||
|
||||
## Environment
|
||||
|
||||
Required for sync:
|
||||
|
||||
- `NEXT_PUBLIC_SUPABASE_URL`
|
||||
- `SUPABASE_SERVICE_ROLE_KEY`
|
||||
- `UNSPLASH_ACCESS_KEY`
|
||||
|
||||
Required for OSS mirroring when an admin marks a photo as picked:
|
||||
|
||||
- `OSS_REGION`
|
||||
- `OSS_ACCESS_KEY_ID`
|
||||
- `OSS_ACCESS_KEY_SECRET`
|
||||
- `OSS_BUCKET`
|
||||
- `OSS_PUBLIC_URL` or `OSS_PUBLIC_BASE_URL`
|
||||
|
||||
Optional for OSS:
|
||||
|
||||
- `OSS_ENDPOINT` to override the region-derived endpoint
|
||||
|
||||
Required for HTTP trigger:
|
||||
|
||||
- `CRON_SECRET`
|
||||
|
||||
Never print secret values. It is fine to report whether each variable is set.
|
||||
|
||||
## Scheduler
|
||||
|
||||
This project is not intended for Vercel deployment. Do not add `vercel.json` cron config unless the deployment target changes.
|
||||
|
||||
Preferred server cron:
|
||||
|
||||
```bash
|
||||
*/10 * * * * cd /Users/luoyangwei/Documents/workspace/wallora.top/searcher && pnpm sync:gallery
|
||||
```
|
||||
|
||||
The script runs once and exits. That makes it suitable for crontab, PM2 cron, Docker scheduled jobs, or an external scheduler.
|
||||
|
||||
## Sync Behavior
|
||||
|
||||
Source endpoint:
|
||||
|
||||
```txt
|
||||
https://api.unsplash.com/topics/wallpapers/photos?page=<page>&per_page=20
|
||||
```
|
||||
|
||||
Use the official Unsplash API with `Authorization: Client-ID <UNSPLASH_ACCESS_KEY>`. Do not rely on `unsplash.com/napi/...`; it is a website-internal endpoint and can return `401`, anti-bot HTML, or other non-contract responses.
|
||||
|
||||
Workflow:
|
||||
|
||||
1. Read `wg_gallery_sync_state` for source `unsplash-wallpapers`.
|
||||
2. Fetch the current page from the official Unsplash API with `per_page = 20`.
|
||||
3. Require JSON and validate that the response is an array of photo-like objects.
|
||||
4. Map every item into `wg_gallery_photos`.
|
||||
5. Populate `color_family` from the Unsplash hex color so admin color filters work.
|
||||
6. Upsert with `onConflict: "unsplash_id"` and `ignoreDuplicates: true`.
|
||||
7. Update `wg_gallery_sync_state` with next page, seen count, inserted count, timestamp, and last error.
|
||||
|
||||
If Unsplash returns an error, anti-bot HTML, or any non-JSON response, record the error in `wg_gallery_sync_state.last_error` and do not write photo rows.
|
||||
|
||||
## Picked Photos
|
||||
|
||||
- Raw gallery rows are for admin curation.
|
||||
- Public consumers should query picked rows only through `/api/v1/gallery/photos`.
|
||||
- RLS should expose `wg_gallery_photos` to `anon`/`authenticated` only when `is_picked = true`.
|
||||
- Admin pick/unpick operations run through server-only code using the Supabase service-role key.
|
||||
- Picking a photo attempts to mirror the selected Unsplash image into Aliyun OSS and stores `oss_bucket`, `oss_object_key`, `oss_url`, `oss_synced_at`, and `oss_sync_error`.
|
||||
- If OSS is missing or upload fails, the photo can still be marked picked, but `oss_sync_error` should be visible to admin users.
|
||||
|
||||
## Collections
|
||||
|
||||
- Collections are admin-managed under `/admin/collections`.
|
||||
- Admin APIs are session-protected and run with the Supabase service-role client.
|
||||
- Collection items must reference picked photos only.
|
||||
- Published collections are exposed publicly; draft collections are not returned by public APIs.
|
||||
- Public collection detail uses `page` and `pageSize` for waterfall-friendly pagination of collection photos.
|
||||
- Public gallery photo detail lives at `/api/v1/gallery/photos/[id]`; it is picked-only and supports authenticated favorite/unfavorite shortcuts.
|
||||
|
||||
## App Users
|
||||
|
||||
- App users are separate from admin users and live in `wg_app_users`.
|
||||
- Email/password auth uses server-side password hashing and Bearer JWT issuance under `/api/v1/auth/*`.
|
||||
- Protected app-user routes live under `/api/v1/users/me/*` and must validate Bearer JWTs server-side.
|
||||
- Admin app-user review and status control live under `/admin/users` and `/api/admin/app-users/[id]`.
|
||||
- Favorites live in `wg_user_favorites` and should only reference picked photos.
|
||||
- Preferences and download preferences live in `wg_user_settings` as JSON objects.
|
||||
- `wg_app_users` has additive identity columns for future phone and WeChat login support.
|
||||
|
||||
## Verification
|
||||
|
||||
Use these checks after touching sync code:
|
||||
|
||||
```bash
|
||||
node --check scripts/sync-gallery-photos.mjs
|
||||
pnpm format:check
|
||||
pnpm lint
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Only run `pnpm sync:gallery` when the user explicitly wants to write to the configured Supabase database.
|
||||
Reference in New Issue
Block a user