docs: design admin gallery prompt notifications
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
# Admin Gallery Prompt Notification Design
|
||||
|
||||
## Goal
|
||||
|
||||
Add an admin-only workflow for editing prompts on public gallery works and notifying the work author by email. The feature is for moderation tasks such as removing sensitive words, privacy details, or misleading public display text.
|
||||
|
||||
The confirmed product rule is: an admin prompt edit must send an email successfully before the prompt change is completed. There is no skip-notification path.
|
||||
|
||||
## Current Context
|
||||
|
||||
- Public gallery UI lives in `src/app/gallery/page.tsx`.
|
||||
- Public gallery API lives in `src/app/api/gallery/route.ts`; admin DELETE currently unpublishes works.
|
||||
- Admin console shell lives in `src/modules/console/pages/console-dashboard-page.tsx`.
|
||||
- Admin tabs live under `src/components/admin/*`.
|
||||
- Email sending is centralized in `src/lib/email-service.ts`.
|
||||
- Existing admin email APIs include `src/app/api/admin/send-email/route.ts` and `src/app/api/email/send-notification/route.ts`.
|
||||
- Admin auth helpers live in `src/lib/admin-auth.ts` and `src/lib/session-auth.ts`.
|
||||
- Platform logs are written through `src/lib/platform-logs.ts`.
|
||||
- The `works` table already stores `prompt`, `is_public`, `user_id`, `result_url`, `thumbnail_url`, and related work metadata.
|
||||
|
||||
## Recommended Approach
|
||||
|
||||
Add a dedicated admin console page instead of extending the public gallery page. This keeps moderation work out of the public browsing experience and avoids further growth in the already-large public gallery component.
|
||||
|
||||
The feature will add:
|
||||
|
||||
- `src/components/admin/gallery-management-tab.tsx`
|
||||
- `GET /api/admin/gallery/works`
|
||||
- `PUT /api/admin/gallery/prompt`
|
||||
- `src/lib/admin-gallery-prompt-service.ts`
|
||||
- A small TDD script for the service layer.
|
||||
|
||||
## Admin UI
|
||||
|
||||
Add a new `gallery` console view in `src/modules/console/pages/console-dashboard-page.tsx`.
|
||||
|
||||
Navigation:
|
||||
|
||||
- Group: creation/admin content group.
|
||||
- Label: `画廊管理`.
|
||||
- Icon: use a lucide gallery/image icon.
|
||||
|
||||
`src/components/admin/gallery-management-tab.tsx` will provide:
|
||||
|
||||
- Search input for prompt, author email/nickname, and work ID.
|
||||
- Type filter for all/image/video/text2img/img2img/text2video/img2video where practical.
|
||||
- Refresh button.
|
||||
- Paginated or load-more public works list.
|
||||
- Rows showing preview thumbnail, work type, author, public time, prompt summary, likes, and actions.
|
||||
- `编辑提示词` action.
|
||||
|
||||
Edit flow:
|
||||
|
||||
1. Admin opens the prompt editor for a public work.
|
||||
2. Dialog shows media preview, author email, original prompt, and editable new prompt.
|
||||
3. Admin clicks save.
|
||||
4. UI opens a required email notification dialog.
|
||||
5. Admin selects a reason template or writes custom email subject/body.
|
||||
6. UI submits one request to `PUT /api/admin/gallery/prompt`.
|
||||
7. On success, dialogs close, the row prompt updates, and a success toast is shown.
|
||||
8. On failure, the dialog stays open and shows the server error.
|
||||
|
||||
Built-in reason templates:
|
||||
|
||||
- `remove_sensitive_words`: 删除敏感词,确保公开展示合规
|
||||
- `improve_wording`: 优化提示词表述,避免误导或不适内容
|
||||
- `remove_private_info`: 移除个人信息或隐私相关描述
|
||||
- `platform_policy_adjustment`: 根据平台内容规范调整公开展示文案
|
||||
|
||||
Templates fill title/body quickly, but admins may manually edit or replace both fields.
|
||||
|
||||
## API Design
|
||||
|
||||
### `GET /api/admin/gallery/works`
|
||||
|
||||
Auth:
|
||||
|
||||
- Requires admin session via existing admin auth helpers.
|
||||
|
||||
Query params:
|
||||
|
||||
- `q`: optional search text.
|
||||
- `type`: optional work type/category filter.
|
||||
- `limit`: bounded page size.
|
||||
- `offset`: page offset.
|
||||
- `sort`: `newest` by default; optionally `popular`.
|
||||
|
||||
Selection rules:
|
||||
|
||||
- Only `works.is_public = true`.
|
||||
- Only completed works.
|
||||
- Only rows with a non-empty result URL.
|
||||
|
||||
Response shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"works": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"type": "text2img",
|
||||
"title": null,
|
||||
"prompt": "current prompt",
|
||||
"negativePrompt": null,
|
||||
"url": "/api/local-storage/...",
|
||||
"thumbnailUrl": "/api/local-storage/thumbnails/...",
|
||||
"likes": 0,
|
||||
"authorId": "uuid",
|
||||
"authorEmail": "user@example.com",
|
||||
"authorNickname": "display name",
|
||||
"publishedAt": "2026-05-20T00:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"nextOffset": 1,
|
||||
"hasMore": false
|
||||
}
|
||||
```
|
||||
|
||||
### `PUT /api/admin/gallery/prompt`
|
||||
|
||||
Auth:
|
||||
|
||||
- Uses `requireAdminUser(request)` so the handler gets the admin user ID for logging.
|
||||
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"workId": "uuid",
|
||||
"prompt": "new public prompt",
|
||||
"emailSubject": "邮件标题",
|
||||
"emailBody": "邮件正文",
|
||||
"reasonKey": "remove_sensitive_words"
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- `workId` must be a UUID-like string.
|
||||
- `prompt` must be non-empty after trim.
|
||||
- `emailSubject` and `emailBody` must be non-empty after trim.
|
||||
- The work must still be public.
|
||||
- The author must have a valid email address.
|
||||
- The new prompt must differ from the old prompt after trim.
|
||||
|
||||
Processing order:
|
||||
|
||||
1. Authenticate admin and parse input.
|
||||
2. Load work plus author profile.
|
||||
3. Validate public status and author email.
|
||||
4. Send email through `sendTemplatedEmail`.
|
||||
5. Only after email success, update `works.prompt`.
|
||||
6. Write a platform log with moderation metadata.
|
||||
7. Return the updated work summary.
|
||||
|
||||
The SMTP send should not be wrapped in a long database transaction. Consistency is enforced by order: email failure blocks prompt update. If email succeeds but the database update fails, return an error and write an error log; the admin can retry or inspect logs.
|
||||
|
||||
Platform log metadata should include work ID, author ID/email, reason key, old/new prompt lengths, and whether the notification was sent. It must not store the full original or new prompt.
|
||||
|
||||
## Service Boundary
|
||||
|
||||
Create `src/lib/admin-gallery-prompt-service.ts` for the core workflow. The API route should remain thin: auth, request parsing, service call, JSON response.
|
||||
|
||||
The service accepts injected dependencies for tests:
|
||||
|
||||
- query-capable database client or adapter.
|
||||
- email sender function.
|
||||
- log writer function.
|
||||
|
||||
This keeps the rule "email success before prompt update" testable without SMTP or a real database.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Expected errors:
|
||||
|
||||
- 401/403 when not admin.
|
||||
- 400 for invalid request body.
|
||||
- 400 when prompt is unchanged.
|
||||
- 400 when author email is missing or invalid.
|
||||
- 404 when the work does not exist or is not public.
|
||||
- 502 or 400 when email sending fails, with prompt unchanged.
|
||||
- 500 when update/logging has unexpected failures.
|
||||
|
||||
Frontend behavior:
|
||||
|
||||
- Keep the email dialog open on failure.
|
||||
- Preserve typed subject/body so the admin can retry.
|
||||
- Refresh the row after success.
|
||||
|
||||
## Testing
|
||||
|
||||
The project currently has no first-party test script. Add a minimal service-level TDD script rather than introducing a large framework.
|
||||
|
||||
Add:
|
||||
|
||||
- `scripts/test-admin-gallery-prompt-service.mjs`
|
||||
- `package.json` script: `test:admin-gallery-prompt`
|
||||
|
||||
TDD sequence:
|
||||
|
||||
1. Write failing tests for `src/lib/admin-gallery-prompt-service.ts`.
|
||||
2. Implement the service until tests pass.
|
||||
3. Add API route and UI.
|
||||
4. Run `pnpm run test:admin-gallery-prompt`.
|
||||
5. Run `pnpm run ts-check`.
|
||||
6. Run `pnpm run build` if environment allows.
|
||||
|
||||
Service tests must cover:
|
||||
|
||||
- Non-public works cannot be modified.
|
||||
- Missing or invalid author email blocks modification.
|
||||
- Unchanged prompt is rejected.
|
||||
- Email failure leaves prompt unchanged.
|
||||
- Email success updates prompt.
|
||||
- Logs include metadata but not full prompt text.
|
||||
|
||||
## Documentation Updates
|
||||
|
||||
Because this is a source/API/UI change, update project docs after implementation:
|
||||
|
||||
- `docs/codex-miaojing/api-reference.md`
|
||||
- `docs/codex-miaojing/feature-code-index.md`
|
||||
- `docs/codex-miaojing/architecture.md` if the moderation workflow needs architecture context.
|
||||
- `docs/codex-miaojing/bug-location-guide.md` if implementation reveals useful troubleshooting notes.
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
This is a cold-update candidate because it changes source, UI, and API routes. Final delivery must mention:
|
||||
|
||||
- Create backup before production upgrade.
|
||||
- Do not overwrite production `.env.local`, `ecosystem.config.cjs`, runtime storage, database, or secrets.
|
||||
- Run type/build checks before deploy.
|
||||
- Restart/reload PM2 as required by the production process.
|
||||
- Health check `/api/health`.
|
||||
- Smoke check `/console`, the new gallery management tab, and `/gallery`.
|
||||
- Roll back with previous code/build and PM2 config if admin API, email, or gallery display fails.
|
||||
Reference in New Issue
Block a user