docs: index admin gallery moderation

This commit is contained in:
FengLee
2026-05-20 10:49:42 +08:00
parent 632c94be78
commit 95a6f5fbb3
4 changed files with 12 additions and 1 deletions

View File

@@ -112,6 +112,8 @@ Important generation helpers:
| GET | `/api/gallery` | Public | `src/app/api/gallery/route.ts` | Query `type=image|video`, `category=text2img|img2img|text2video|img2video`, `limit`, `offset`, `sort=newest|popular`, `q`/`search` | Public completed works with `thumbnailUrl`, `total`, `nextOffset`, and `hasMore`; missing public image thumbnails are lazily generated into local `thumbnails/gallery`. Responses allow short private browser caching while the gallery page also keeps a bounded localStorage cache for instant first paint. |
| DELETE | `/api/gallery` | Admin | `src/app/api/gallery/route.ts` | Query `id` or body `{ ids: [...] }` | Unpublishes up to 100 works by setting `is_public=false`. |
| POST | `/api/gallery/publish` | User | `src/app/api/gallery/publish/route.ts` | Work metadata, `resultUrl`, optional thumbnail/reference/model fields | Copies image originals to object-backed gallery storage, ensures local gallery thumbnails, and inserts public completed work. |
| GET | `/api/admin/gallery/works` | Admin | `src/app/api/admin/gallery/works/route.ts` | Query `q`, `type=all|image|video|text2img|img2img|text2video|img2video`, `limit`, `offset`, `sort` | Admin gallery-management list of public completed works with author email/nickname, prompt, media URL, thumbnail, total, `nextOffset`, and `hasMore`. |
| PUT | `/api/admin/gallery/prompt` | Admin | `src/app/api/admin/gallery/prompt/route.ts`, `src/lib/admin-gallery-prompt-service.ts` | `{ workId, prompt, emailSubject, emailBody, reasonKey }` | Sends the author notification email first, then updates `works.prompt` only after email success, and writes a platform log without storing full prompt text. Missing/invalid author email, unchanged prompt, non-public work, or email failure blocks the update. |
## Admin Routes
@@ -133,6 +135,8 @@ All routes in this section require admin unless noted.
| POST | `/api/admin/system-apis/yuanjie-capabilities` | `src/app/api/admin/system-apis/yuanjie-capabilities/route.ts` | Admin-only 元界 AI built-in installer retained for system-default-model template management, not for the generic smart import UI. `{ syncModels: true }` resets only `provider = '元界 AI' AND type = 'image'` rows and installs 17 inactive image rows. `{ syncVideoModels: true }` resets only `provider = '元界 AI' AND type = 'video'` rows and installs inactive video rows with `videoUsageModes`. Rows have no API Key by default; admins must edit each model to set Key, pricing, visibility/member scope, polling, usage mode, and enable it before users can generate. |
| GET/POST/PUT/DELETE | `/api/admin/model-recommendations` | `src/app/api/admin/model-recommendations/route.ts` | Managed model recommendations. |
| GET/DELETE | `/api/admin/generation-jobs` | `src/app/api/admin/generation-jobs/route.ts` | Admin task listing and deletion. |
| GET | `/api/admin/gallery/works` | `src/app/api/admin/gallery/works/route.ts` | Admin public gallery work listing for prompt moderation. |
| PUT | `/api/admin/gallery/prompt` | `src/app/api/admin/gallery/prompt/route.ts` | Admin prompt moderation endpoint. Requires email notification success before updating `works.prompt`. |
| GET | `/api/admin/data-export` | `src/app/api/admin/data-export/route.ts` | Export business data plus `_media` entries for storage assets referenced by works and site config. `_meta` reports media count/bytes/missing/skipped. |
| POST | `/api/admin/data-import` | `src/app/api/admin/data-import/route.ts` | Import business data. Accepts optional `_media`; restores media to sha-based keys, remaps users/custom API keys/works, imports in a transaction with per-row savepoints, preserves password hashes/encrypted secrets, and dedupes works by URL/source URL/media SHA only inside the same `user_id`. |
| GET/PUT/POST | `/api/admin/email-settings` | `src/app/api/admin/email-settings/route.ts` | Read/update/test email settings. |

View File

@@ -186,6 +186,8 @@ Gallery detail metadata must not load original images just to compute size. `Ima
The public gallery page should use server gallery rows only. It must not merge `miaojing_published_gallery` or `miaojing_creation_history` from browser localStorage into the gallery feed, and it must not auto-sync historical local published records into Supabase on page load. `/api/gallery` is the authority for all gallery views, including all/category filters and search, and should only return stable platform media URLs under `/api/local-storage/...`; legacy external import URLs are not public gallery candidates. To keep reopen latency low, `src/app/gallery/page.tsx` caches bounded page data in browser localStorage for instant first paint, prunes entries after 7 days or when the cache cap is exceeded, and immediately revalidates the first page in the background so published/deleted works replace cached rows. It should request small pages and append via IntersectionObserver as the user scrolls, not load the entire public gallery into the DOM.
Admin gallery moderation is separate from the public gallery page. `src/components/admin/gallery-management-tab.tsx` lists public completed works through `/api/admin/gallery/works`; prompt edits go through `/api/admin/gallery/prompt` and `src/lib/admin-gallery-prompt-service.ts`. The service enforces the moderation rule that the author notification email must send successfully before `works.prompt` is updated. Platform logs record the admin, work, author, reason key, prompt length changes, and notification result, but must not store the full original or edited prompt text.
Fullscreen image overlays should accept a thumbnail fallback and display it immediately while the original object-storage image loads. If object storage is slow or the original fails, the user still sees the high-quality local preview and the fullscreen controls stay usable; copy/download/share actions still receive the original URL.
`/api/health` caches storage health briefly and bounds object bucket probing, so health checks do not block page monitoring on a slow object-storage HEAD request. Optional runtime schema checks cache success or non-owner skips; production migrations should still apply schema changes explicitly, but request paths should not repeatedly run DDL.
@@ -233,6 +235,8 @@ Admin console UI is split across:
Admin auth flows through the same login endpoint with admin role checks. API routes should use `requireAdmin`.
Gallery prompt moderation uses `requireAdminUser` when the route needs the admin user ID for platform logs, and `requireAdmin` for read-only admin list APIs. The moderation endpoint should fail closed when the work is no longer public, the author email is missing/invalid, the prompt is unchanged, or SMTP sending fails.
## Upgrade And Deployment Architecture
Scripts:

View File

@@ -108,6 +108,7 @@ Use this guide when the user reports behavior. Start from the symptom row, inspe
| Imported gallery images do not render after production data import | `src/app/api/admin/data-export/route.ts`, `src/app/api/admin/data-import/route.ts`, `src/lib/local-storage.ts`, `src/app/api/local-storage/[...path]/route.ts`, DB `works.result_url` | New exports should include `_media`; import should persist media through the active storage adapter. If using an older export without `_media`, DB rows alone cannot recreate missing `/api/local-storage/*` files. For object migration, run `pnpm run storage:sync-object -- --verify-only` before switching to `STORAGE_MODE=object`. |
| Rainyun ROS bucket created but object storage still fails | `scripts/rainyun-ros-prepare.mjs`, `.env.local`, `src/lib/local-storage.ts`, `scripts/storage-sync-to-object.mjs`, `/api/health` | The Rainyun API link is control-plane bucket creation, not the media upload path. Verify `.env.local` has reviewed `OBJECT_STORAGE_BUCKET`, `OBJECT_STORAGE_ENDPOINT`, `OBJECT_STORAGE_ACCESS_KEY_ID`, `OBJECT_STORAGE_SECRET_ACCESS_KEY`, `OBJECT_STORAGE_FORCE_PATH_STYLE=true`, and `STORAGE_MODE=dual`; then run `/api/health` and `pnpm run storage:sync-object -- --dry-run`. |
| Gallery delete does not remove public item | `src/app/api/gallery/route.ts`, admin UI route using it | DELETE unpublishes by setting `is_public = false`, not hard delete. |
| Admin gallery prompt edit fails, sends no email, or prompt changes without audit trail | `src/components/admin/gallery-management-tab.tsx`, `src/app/api/admin/gallery/works/route.ts`, `src/app/api/admin/gallery/prompt/route.ts`, `src/lib/admin-gallery-prompt-service.ts`, `src/lib/email-service.ts`, `src/lib/platform-logs.ts` | Prompt moderation is console-only and requires a valid author email. `/api/admin/gallery/prompt` must send the email before updating `works.prompt`; SMTP failure, unchanged prompt, non-public work, or invalid author email should block the update. Platform logs should include reason key and prompt length metadata, not full prompt text. |
| Search/filter/sort wrong | `src/app/api/gallery/route.ts`, `src/app/gallery/page.tsx` | Query params `type`, `category`, `limit`, `offset`, `sort`, `q/search`; SQL where/order, browser cache signature, and pagination append state. |
| Gallery search box looks inconsistent with the rest of the UI | `src/app/gallery/page.tsx` | The search field is a custom glass panel with an inner focused input surface; avoid reverting it to a plain transparent input row. |
| Gallery hover makes images muddy, covers the image with prompt text, shows only a single-color/static glow, has transparent gaps, does not match image colors, misses the card corners, moves too fast, looks too hard-edged, or action buttons disappear on dark/light images | `src/app/gallery/page.tsx`, `src/app/globals.css` | Gallery cards should not use a full-image dark hover overlay, center prompt text, transparent border gaps, generated unrelated colors, broad square glow under the card, or a separate outer halo layer. Keep hover feedback on the card container with scale plus a real `gallery-card-border-frame` wrapper using 3-5 sampled image colors in a single blurred 3px continuous clockwise border around the full work-card container, including all four corners and the prompt/footer area, and keep like/download buttons legible through sampled image brightness inversion. |

View File

@@ -1,6 +1,6 @@
# Feature Code Location Index
Last source audit: 2026-05-12, based on git commit `8ee86a9`.
Last source audit: 2026-05-20, based on git commit `632c94b`.
Use this document to jump directly to code before broad searching.
@@ -110,6 +110,7 @@ Use this document to jump directly to code before broad searching.
| Public gallery page | `src/app/gallery/page.tsx`, `src/app/globals.css` | Lists public works, search/sort/filter, preview/download, and one-click reuse. It requests `/api/gallery` in small pages instead of fetching the full gallery, uses a bounded `miaojing:gallery:v3` browser localStorage cache for instant reopen, revalidates page 0 in the background, debounces search, and uses an IntersectionObserver sentinel to append the next page only when the user scrolls near it. Cached entries expire quickly for freshness and are pruned after 7 days or when the entry cap is exceeded. Image cards and detail display use `thumbnailUrl || url`, while fullscreen, download, copy/share, and reuse actions use original `url`. The search box is custom styled in-page to match the glass UI; gallery cards sample 3-5 distinct colors from the image and use a real `gallery-card-border-frame` wrapper with a single 3px blurred, continuous clockwise multicolor border around the full work-card container, including all four corners and the prompt/footer area. Avoid image-covering dark overlays, broad square glow blocks, or a separate outer halo layer. Hover like/download/reuse buttons invert against sampled image brightness. Gallery detail image previews use `ImageMetadataBadge` for actual ratio/resolution, and the detail footer writes a reuse draft before navigating to the matching `/create?type=...` mode. Mobile gallery must keep at least two masonry columns; `masonryColumnCount` bottoms out at 2 and `.gallery-masonry-grid`/card CSS trims spacing and metadata density on phones. |
| Public gallery API | `src/app/api/gallery/route.ts` | GET public works with `thumbnailUrl`, `total`, `nextOffset`, and `hasMore`, queues missing or old-profile image thumbnails for background backfill without delaying the response, admin DELETE unpublishes. Gallery author names use `profiles.display_nickname` first and never expose login username unless no display nickname exists. |
| Publish API | `src/app/api/gallery/publish/route.ts` | Copies image originals into object-backed gallery folders, stores local thumbnails, and inserts public work. |
| Admin gallery prompt moderation | `src/components/admin/gallery-management-tab.tsx`, `src/app/api/admin/gallery/works/route.ts`, `src/app/api/admin/gallery/prompt/route.ts`, `src/lib/admin-gallery-prompt-service.ts`, `scripts/test-admin-gallery-prompt-service.mjs` | Console-only workflow for editing public gallery `works.prompt`. Admins must send an email notification to the author; the service sends email before updating the prompt and logs metadata without storing full prompt text. |
| History persistence | `src/app/api/creation-history/route.ts`, `src/lib/creation-history-store.ts` | User-private completed works, `thumbnailUrl`, and published state. Missing image thumbnails are queued for background backfill instead of blocking the history response. Single-record deletion is server-first when logged in; detail dialogs call the same store path and then refresh local history. |
## Admin Console
@@ -125,6 +126,7 @@ Use this document to jump directly to code before broad searching.
| Payment | `src/components/admin/payment-tab.tsx` | `src/app/api/admin/payment-methods/route.ts`, `src/lib/server-payment-config.ts` |
| Orders | `src/components/admin/order-management-tab.tsx` | `src/app/api/admin/orders/route.ts` |
| Announcements | `src/components/admin/announcement-tab.tsx` | `src/app/api/announcements/route.ts` |
| Gallery management | `src/components/admin/gallery-management-tab.tsx` | `src/app/api/admin/gallery/works/route.ts`, `src/app/api/admin/gallery/prompt/route.ts`, `src/lib/admin-gallery-prompt-service.ts`. Lists public works, edits prompt text, opens a required notification email dialog with built-in reason templates, and only completes the update after email send success. |
| Data import/export | `src/components/admin/data-management-tab.tsx` | `src/app/api/admin/data-export/route.ts`, `src/app/api/admin/data-import/route.ts`, `scripts/migration-integrity-check.mjs`. Export bundles storage URLs from works/site config into `_media`; import restores those files through `src/lib/local-storage.ts`, maps old IDs, merges duplicate works only within the same `user_id`, and runs DB writes in a transaction. Import preserves password hashes, encrypted API keys, `manifest_path`, system API pricing fields, and `redeem_codes` state so users, credentials, works, intelligent API configs, and unused/used redemption state survive migration. Run `pnpm run migration:check` before and after production migration. |
| System upgrade | `src/components/admin/system-upgrade-tab.tsx` | `src/app/api/admin/upgrade/route.ts`, `scripts/admin-upgrade-runner.mjs` |
| Logs/tasks | `src/components/admin/log-management-tab.tsx`, `src/components/admin/task-management-tab.tsx` | `src/lib/platform-logs.ts`, `src/app/api/admin/logs/route.ts`, `src/app/api/admin/generation-jobs/route.ts` |