Files
NianAIGC/findings.md
2026-07-03 11:25:25 +08:00

303 lines
36 KiB
Markdown

# Findings & Decisions
## Requirements
- User asked: "了解整个项目" — inspect and understand the whole project, then explain it clearly.
- Expected output: concise but useful project map in Chinese, including purpose, stack, structure, runtime flow, commands, and notable risks.
## Research Findings
- Top-level project is not a Git repository; `git status --short` returned "fatal: not a git repository".
- Root contains orchestration/docs files plus a `removed extracted runtime` application directory.
- `removed extracted runtime/node_modules` is present, so dependencies appear already installed for the embedded runtime app.
- The initial full file scan showed many bundled media assets under `removed extracted runtime/public`, especially starter/planning/Seedance examples.
- `README.md` states this was extracted from the `智念助手` desktop app into an independent `智念创作助手` project.
- The project is based on an existing extracted runtime Next.js standalone runtime; original source was deleted, so this is not a full source restoration.
- Root `package.json` only orchestrates scripts: `start`/`dev` call `removed runtime start script`, `health` calls `scripts/health-check.mjs`, and `info` calls `removed runtime info script`.
- Runtime package uses Next `^15.1.4`, React `^19.0.0`, Supabase client, Ali OSS, lucide-react, TypeScript, Vitest, and ESLint, but it is treated as generated runtime.
- Runtime state should be written to root `.runtime/`, not under `removed extracted runtime`.
- `removed runtime start script` loads `.env` and `.env.local`, optionally bundled `.env.runtime` only when `ZHINIAN_LOAD_BUNDLED_ENV=1`.
- Startup creates `.runtime/data`, `.runtime/uploads`, and `.runtime/generated-results`, then launches `removed extracted runtime/server.js` with `NODE_ENV=production`.
- Health check targets `/api/desktop/health` and expects JSON with `appId: "removed-runtime"` and `ok: true`.
- `.env.example` shows the real generation path depends on Seedance / Volcengine Ark plus Aliyun OSS configuration.
- Extraction notes confirm copied assets include Next standalone server runtime, `.next` output, runtime `node_modules`, public/reference media, content manifests, and planning cases; secrets, user uploads, generated results, and Electron host/process manager code were excluded.
- `npm run info` succeeded and reports runtime app id `removed-runtime`, bundle timestamp `2026-05-14T04:01:58.653Z`, entry `server.js`, and size `949,760,759` bytes.
- App routes include: `/`, `/studio`, `/studio/[mode]`, `/planning`, `/projects`, `/projects/[id]`, and `/billing`.
- API routes include: `/api/assets`, `/api/assets/upload`, `/api/billing`, `/api/desktop/health`, `/api/generations`, `/api/generations/[id]`, `/api/generations/[id]/retry`, `/api/projects`, `/api/projects/[id]`, `/api/prompt/assemble`, and `/api/reference-templates`.
- File-serving routes expose runtime uploads and generated results via `/uploads/[...path]` and `/generated-results/[...path]`.
- Creation modes currently report one mode: `video_studio` / `宣传片创作台`, editor type `storyboard_cards`.
- Starter catalog has 14 cases across `storefront_avatar_storyboard`, `music_sync_ad`, and `creative_remix`; planning cases include five visible categories such as short-video promo and premium-brand.
- Compiled API code reveals a local JSON store at `app-state.json` with `users`, `assets`, `projects`, `generation_jobs`, and `credit_transactions`.
- The default local user is `demo-merchant` / `demo@localmerchant.ai` with 9999 demo credits.
- `/api/projects` returns projects with their jobs; project creation is coupled to generation creation and deducts credits based on duration/resolution/ratio.
- `/api/assets/upload` accepts multipart `file`, `role`, and optional `promptLabel`; it stores to Ali OSS when OSS env is complete, otherwise writes to local uploads and records an asset.
- `/api/prompt/assemble` builds a Chinese Seedance-style video prompt from shop/project details, selected template, storyboard, and optional avatar/outfit selection.
- Seedance client defaults: base URL `https://ark.cn-beijing.volces.com/api/v3`, model `doubao-seedance-2-0-260128`, ratio `9:16`, duration `15`, resolution `720p`.
- Real generation creation posts to `/contents/generations/tasks`; query uses `/contents/generations/tasks/{id}`.
- Missing `SEEDANCE_API_KEY` causes a user-facing error and the project/job path refunds credits after marking the job failed.
- Health response includes `services.seedanceConfigured` and `services.objectStorageConfigured`.
- `GET /api/generations/:id` reads a local job, polls Seedance when `provider_job_id` exists, and backs up successful result videos to OSS or local generated-results.
- `POST /api/generations/:id/retry` loads the original project settings and re-runs the generation creation flow.
- `GET /api/projects/:id` returns one project with jobs; `DELETE /api/projects/:id` removes the project, jobs, and related credit transactions.
- `GET /api/assets` returns local assets for the demo owner; `GET /api/billing` returns demo user and credit transactions.
- `GET /api/reference-templates?mode=...` returns starter templates filtered by mode.
- Content definition has one canonical creation mode: `video_studio`, product name `宣传片创作台`, editor type `storyboard_cards`, reference-first workflow, and six asset slot labels.
- Starter catalog contains 14 selectable templates and 85 local asset records from the Seedance guide plus local promo examples.
- Template categories represented in content are `storefront_avatar_storyboard`, `music_sync_ad`, and `creative_remix`; docs say legacy mode arguments are accepted for compatibility.
- Planning page content has five planning cases: `短视频宣传类`, `剧情宣传类`, `热门玩梗类`, `卡通 IP 类`, and `品质高级类`.
- Avatar presets currently include one default digital-human model and one default outfit pairing.
- Runtime verification passed: `npm run info` succeeded, `npm start` launched Next on `http://127.0.0.1:3000`, and `npm run health` returned `ok: true`.
- Health verification reports `seedanceConfigured: false` and `objectStorageConfigured: false`, matching the empty local env configuration.
- Direct curl verification with `--noproxy '*'` returned `200 OK` for `/studio`, `{"projects":[]}` for `/api/projects`, one template for `/api/reference-templates?mode=video_studio`, and the demo billing user.
- Starting the runtime created `.runtime/data/app-state.json` with the demo user and initial credit transaction.
## Technical Decisions
| Decision | Rationale |
|----------|-----------|
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| `git status` cannot run because the project root has no `.git` metadata | Treat this as a plain project folder and avoid Git-based assumptions |
| zsh treats `[id]` in file paths as a glob pattern | Quote bracketed Next.js dynamic route paths when reading them |
| Initial plain `curl` calls hit a local proxy and returned 502/empty output | Use `curl --noproxy '*'` for localhost verification |
## Resources
- Project root: `/Users/inmanx/Documents/zhinian-creation-assistant`
- Runtime app: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime`
- Root README: `/Users/inmanx/Documents/zhinian-creation-assistant/README.md`
- Runtime README: `/Users/inmanx/Documents/zhinian-creation-assistant/runtime/README.md`
- Startup script: `/Users/inmanx/Documents/zhinian-creation-assistant/removed runtime start script`
- Health script: `/Users/inmanx/Documents/zhinian-creation-assistant/scripts/health-check.mjs`
- Runtime info script: `/Users/inmanx/Documents/zhinian-creation-assistant/removed runtime info script`
- Extraction notes: `/Users/inmanx/Documents/zhinian-creation-assistant/docs/EXTRACTION_NOTES.md`
- App paths manifest: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/.next/server/app-paths-manifest.json`
- Compiled projects API: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/.next/server/app/api/projects/route.js`
- Compiled upload API: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/.next/server/app/api/assets/upload/route.js`
- Compiled prompt assembly API: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/.next/server/app/api/prompt/assemble/route.js`
- Compiled generation polling API: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/.next/server/app/api/generations/[id]/route.js`
- Compiled generation retry API: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/.next/server/app/api/generations/[id]/retry/route.js`
- Creation modes JSON: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/content/seedance-starter/creation-modes.json`
- Starter catalog JSON: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/content/seedance-starter/catalog.json`
- Planning cases JSON: `/Users/inmanx/Documents/zhinian-creation-assistant/removed extracted runtime/content/removed planning case manifest.json`
- Runtime local state file: `/Users/inmanx/Documents/zhinian-creation-assistant/.runtime/data/app-state.json`
## Visual/Browser Findings
- 2026-05-29 UI polish verification:
- `/create`, `/create?mode=video`, `/assets`, and `/settings` were checked during the product polish work.
- `375`, `768`, `1024`, and `1440` width checks showed no horizontal overflow after the compact header and mobile control changes.
- The video duration dropdown on `/create?mode=video` exposes only `4 秒` through `15 秒`.
- The header logo loads from `public/logo/zhinian-logo.png`; after the final branding pass it has no border, background, or box shadow.
## 2026-05-29 UI/UX and Branding Findings
- The current source app is now a Web app in the repository root, not the old `removed extracted runtime` standalone-only flow described in the earliest findings.
- GSAP is used through `lib/ui/motion.ts` rather than directly sprinkled across components.
- The UI direction is a professional creation workspace, not a marketing landing page.
- Visible English eyebrows/descriptions were removed from module headers per user preference.
- Topbar should remain compact and avoid horizontal scrolling below it.
- Product name is now `智念AIGC平台`.
- Logo source folder: `/Users/inmanx/Documents/icon/logo`.
- Current logo asset: `public/logo/zhinian-logo.png`.
- Current logo was generated from `/Users/inmanx/Documents/icon/logo/2d5b992caa14db16f594c4933e92e37e.png` by removing the white background and cropping whitespace.
- Avoid using the white transparent logo on the light topbar unless the topbar itself becomes dark; wrapping the logo in a dark frame changes the brand feel.
## 2026-05-29 Seedance Findings
- Official Volcengine Ark "创建视频生成任务 API" docs say Seedance 2.0 `duration` supports integer seconds in `[4, 15]`, or `-1` for model-chosen duration.
- Seedance 2.0 and Seedance 1.5 Pro support `adaptive` ratio behavior.
- Supported ratios include `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, and `adaptive`.
- Supported resolutions include `480p`, `720p`, and `1080p`, but Seedance 2.0 fast does not support `1080p`.
- `frames` is not supported for Seedance 2.0 / Seedance 1.5 Pro, so the current app should continue using integer `duration`.
- `generate_audio` defaults true in the API and remains enabled in the app payload.
## 2026-05-29 Operational Findings
- For localhost verification, continue using `curl --noproxy '*'` because local proxy settings can interfere with direct checks.
- Before running `npm run build`, stop the dev server (`screen -S zhinian-dev-ui -X quit` and `pkill -f 'next dev --hostname 127.0.0.1 --port 3000'`) to avoid stale Next dev chunk issues.
- After build verification, restart the dev server in screen session `zhinian-dev-ui` on `127.0.0.1:3000`.
## 2026-05-29 Deployment Findings
- Server one-command deployment is now `bash scripts/deploy.sh`.
- Docker Compose service name is `zhinian-aigc`.
- Docker Compose defaults to exposing host port `3000`; set `APP_PORT` in `.env.local` or shell to change it.
- `NEXT_PUBLIC_APP_URL` should be set to the public domain or server URL in production so generated local file URLs are correct.
- Persistent runtime data is bind-mounted through `./.runtime:/app/.runtime`; this folder should be backed up on real servers.
- `.env.local` is intentionally used as the compose `env_file` and remains ignored by Git.
- Current local machine does not have the Docker CLI available, so Docker build was not run here; script syntax, app tests, production build, and local health were verified instead.
## 2026-05-29 Public API and Task Management Findings
- User confirmed multi-task support should be task management logic, not an external message queue.
- Public API v1 now uses `ZHINIAN_API_KEYS`, supporting `Authorization: Bearer <key>` and `X-Zhinian-Api-Key`.
- Task creation and provider execution are now split: submit routes enqueue `GenerationJob` records; Worker ticks claim and process jobs.
- `generation_jobs` now carries external client, idempotency, priority, attempts, lock, schedule, timing, and webhook fields.
- Supabase/Postgres production mode expects the `claim_generation_jobs` function from `supabase/schema.sql` for atomic task claiming.
- Local JSON mode serializes task claiming through the existing local write queue and is intended for single-instance development.
- Worker execution can run as `npm run worker`, `npm run worker:once`, or the `zhinian-worker` Docker Compose service.
- Internal Worker processing goes through `/api/internal/worker/tick` protected by `ZHINIAN_INTERNAL_WORKER_TOKEN` in production.
- API v1 routes are `/api/v1/capabilities`, `/api/v1/assets`, `/api/v1/jobs`, `/api/v1/jobs/:id`, `/api/v1/jobs/:id/cancel`, and `/api/v1/openapi.json`.
- Local verification created a public API job and processed it to `succeeded` through `npm run worker:once` in mock mode.
- Public API asset access now includes `/api/v1/assets/:id` and `/api/v1/assets/:id/download`.
- Uploaded and generated assets created through public API flows are tagged as `api-client:<clientId>` so integrations can query and download their own results later.
- OpenAPI is generated dynamically from the current deployment origin at `/api/v1/openapi.json`.
- Operations handoff docs live in `docs/DEPLOYMENT.md`; partner API docs live in `docs/API.md`.
## 2026-05-29 Image Tuning Findings
- Jimeng image generation supports the current `scale` parameter, so UI presets should submit numeric text-influence values for that engine.
- EvoLink image generation does not use Jimeng `scale`; the per-request engine-aware control should submit EvoLink `quality` instead.
- Current local `/api/health` reports `image.generate` using EvoLink, so `/create` should show `生成质量` options rather than `文本影响`.
## 2026-05-29 Account Login / SSO Findings
- User requested account login before release so the project is safe to use.
- Provided SSO guide recommends OAuth2 Authorization Code for Web SSO: redirect to `${AUTH_BASE}/oauth2/authorize`, receive `code` and `state`, then exchange code server-side at `${AUTH_BASE}/oauth2/token`.
- OAuth client defaults in the guide use `client_id=customPC` and `scope=server`; `client_secret` must stay on the server.
- Access tokens are JWTs; resource services should verify locally with JWKS from `${AUTH_BASE}/oauth2/jwks` rather than calling auth on every request.
- Minimum JWT checks from the guide: RS256 signature, `exp`, `nbf`/`iat`, issuer `https://pig4cloud.com`, OAuth client id, scope/authority requirements.
- Logout endpoint is `DELETE ${AUTH_BASE}/token/logout`, but local session deletion remains required because existing JWTs may stay valid until `exp`.
- Current app has no login middleware or session helper; pages are client components under a global shell.
- Current local data store defaults all assets/jobs to `DEFAULT_OWNER_ID = "demo-merchant"`, so account login must also address per-user owner IDs for first-party UI APIs.
- Public API v1 already has separate API key auth through `ZHINIAN_API_KEYS`; SSO should preserve that server-to-server surface.
- First-party UI APIs that currently need session ownership include `/api/assets`, `/api/assets/upload`, `/api/assets/:id/*`, `/api/generations/image*`, `/api/generations/video*`, and `/api/settings`.
- Generation services already accept optional `ownerId`, so route handlers can pass the authenticated owner without rewriting provider dispatch or worker logic.
- Retry helpers currently preserve the original request payload; they need to override `ownerId` on retry so a user cannot retry another user's job if they know the id.
- `/uploads/*` and `/generated-results/*` serve local runtime files directly; middleware should protect these paths for cookie-authenticated browser sessions.
- `/api/v1/*` and `/api/internal/worker/tick` must remain outside browser SSO middleware because they use API keys and worker tokens.
- Implemented browser SSO with signed HttpOnly `zhinian_session` cookies; middleware validates the signed session instead of exposing JWTs to client JavaScript.
- JWT access tokens are verified in the callback using RS256 and configured JWKS, with issuer, client id, expiry, not-before, issued-at, and scope checks.
- Local file routes now resolve `storagePath` back to an asset record and require the current owner to match before serving bytes.
- Public API v1 remains API-key based and can still read/download its assets through public API routes even when browser SSO protects the Web UI.
## 2026-05-29 Password Captcha Login Findings
- User provided live auth configuration and a password grant sample; real secret values remain only in the ignored local environment file.
- `${AUTH_BASE}/code/image?randomStr=...` returns a PNG captcha image.
- The password grant sample successfully returns a JWT access token, refresh token, expected client/user claims, tenant id, and `server` scope.
- The local `/api/auth/password` endpoint verified the returned JWT with JWKS and created a signed browser session for the authenticated user.
- Browser form login with the displayed math captcha succeeded and redirected to `/create`; the topbar showed the authenticated username and logout button.
- Logout cleared the session and returned to `/auth/login?loggedOut=1`.
## 2026-05-29 Standalone Login Page Findings
- Login routes under `/auth/*` should not render the shared app topbar; the login page is a standalone entry surface.
- The login page now intentionally presents only the NIANXX logo, `智念AIGC平台`, and the account/password/captcha form.
- The visible `统一认证中心` OAuth entry was removed from the login page after user feedback.
- The standalone login page uses the existing GSAP motion helper layer (`runScopedMotion`, `revealChildren`, `pulseFeedback`) for consistent app motion.
- Browser viewport checks passed at 1280x800 and 390x844: no topbar, no SSO link text, logo and platform name present, login panel present, and no horizontal overflow.
## 2026-06-09 Repository and Runtime Status Findings
- The current repository root is a Git checkout on `main` tracking `origin/main`.
- The remote is `https://git.nianxx.cn/wangxuming/NianAIGC.git`.
- The workspace was overwritten from remote and is currently clean at `d98e58a docs: update public api docs`.
- The project uses npm (`package-lock.json`) and Next.js dev startup through `npm run dev`.
- Local Node version during verification was `v22.22.1`, satisfying the `package.json` `>=20` engine.
- During startup verification, ports `3000`, `3001`, and `3002` were already occupied by local Node/Next processes or listeners; use a confirmed-free alternate port rather than assuming `3000`.
- `npm run dev -- --hostname 127.0.0.1 --port 3003` successfully started the current project and reached Next Ready in 2.9 seconds.
- A smoke request to `http://127.0.0.1:3003/` returned `307 Temporary Redirect` to `/auth/login?next=%2F`, confirming the login-protected app flow.
- Current status check on 2026-06-09 found no `next dev` / `next-server` process and no listener on `3003`; the project is verified startable but not currently running.
## 2026-07-01 Account ID Partitioning Findings
- First-party browser APIs already pass the authenticated session user id into asset and generation flows.
- Public API v1 currently authenticates by `clientId:key`, but jobs and assets are still created with `ownerId = DEFAULT_OWNER_ID`.
- Public API listing/detail access filters with `externalClientId` and `api-client:<clientId>` tags, which works as an access check but keeps all API account data in one default owner partition.
- The data store and Supabase schema already support `owner_id` indexes and idempotency uniqueness by `(owner_id, external_client_id, idempotency_key)`, so the change can be made by deriving a stable account owner id from the API client id.
- Job detail and cancel routes currently check only `externalClientId`; they should also require the account owner id so a client id collision cannot cross account partitions.
- Implemented owner derivation as `publicApiOwnerId(client)`, yielding `api:<sanitizedAccountId>`.
- Public API job creation, idempotency lookup, job listing/detail/cancel, asset upload/register/list/detail/download now use the derived API account owner.
- Documentation now states that `ZHINIAN_API_KEYS` uses `账号ID:key` and that records are partitioned under `api:<账号ID>`.
## 2026-07-01 Password Login Captcha Finding
- `origin/main` is up to date at `d98e58a`; relevant history includes `ce358df 修改认证中心对接方式` and `288e31d 移除验证码输入框`.
- Current `components/auth-login-panel.tsx` does not render captcha controls and submits only `username`, `password`, and `next`.
- Current `app/api/auth/password/route.ts` no longer requires captcha; it only forwards `code` and `randomStr` if they are present in the request body.
- Current local `.env.local` points password login at `https://onefeel.brother7.cn/ingress/auth` with client id `customPC`.
- A local login probe with fake credentials returned `验证码不能为空`; a direct token request to the configured auth center with the same non-secret dummy credentials also returned `验证码不能为空`.
- Therefore the observed captcha error is coming from the configured auth center instance, not from the current AIGC frontend or password route validation.
- The operations SSO guide defaults external projects to OAuth client `app/app`, recommends adding token-flow clients to `security.ignore-clients`, and notes `customPC` is a special platform-user compatibility client that skips AES password decrypt.
- Dummy token probes showed `app/app` reaches normal credential validation (`用户名或密码错误`) while `customPC` is intercepted by captcha validation on the current auth center instance.
- Updated the project defaults, docs, `.env.example`, and local `.env.local` to use `app/app` plus `ZHINIAN_AUTH_PASSWORD_ENC_KEY=thanks,pig4cloud`.
- After restarting the dev server, local `/api/auth/password` with dummy credentials returns `用户名或密码错误`, confirming the captcha interception is bypassed for the configured client.
## 2026-07-01 Internal RBAC / Account Management Findings
- Initial requested guide `/Users/inmanx/Desktop/organization-external-api.md` was missing; updated guide `/Users/inmanx/Desktop/organization-external-api(1).md` is available and has 1310 lines.
- Existing JWT session already stores `authorities` and `scope` on `AuthUser`, making it suitable for internal RBAC.
- Before the RBAC change, navigation exposed `创作`, `结果`, `日志`, and `设置` to every logged-in user.
- Before the RBAC change, middleware only checked whether a user was authenticated and did not distinguish ordinary users from administrators.
- Before the RBAC change, `/api/settings` checked `requireAppUser()` but not admin permission.
- Before the RBAC change, `/api/logs` relied on middleware authentication only and did not call `requireAppUser()` or admin checks inside the route.
- RBAC now hides admin navigation for ordinary users and protects `/logs`, `/settings`, `/accounts`, `/api/logs`, `/api/settings`, and `/api/admin/*`.
- Updated guide covers organization, department, role, member, and enterprise user operation APIs.
- Organization/member APIs are served by `basic-capability-services-biz`; enterprise user management APIs are exposed by `hotel-staff-server-biz`.
- Member list APIs remain `@Inner` and require header `from: Y`.
- Enterprise user APIs require `Authorization` and a token with administrator role `1`; otherwise they return `仅管理员角色允许调用`.
- Account creation should use `/adminOrganization/organizationMember/addOrganizationMemberAndCreatePlatformUser` to create the enterprise user and bind the organization member in one call.
- Password maintenance is available through `/adminPcUser/resetPlatformUserPassword`, with `tenantId`, numeric `userId`, `newPassword`, and optional `mustChangePassword`.
- Updated `/Users/inmanx/Desktop/organization-external-api(2).md` confirms the external member list path is `/adminOrganization/organizationMember/organizationMemberList` through `hotelStaff`; only the basic service internal `/organizationMember/organizationMemberList` path needs `from: Y`.
- The updated `hotelStaff` member-list proxy is reachable from the app, but the current login token can still be rejected by the upstream service with `仅管理员角色允许调用`.
- Local RBAC allow-listing `ceshiop` as a platform admin only controls this app's pages and APIs; it does not grant upstream `hotelStaff` administrator role `1` inside the authentication center token.
- Account management now treats upstream member-list administrator denial like a recoverable member-list-only limitation: the page can still load configured organization data and keep account creation/password maintenance available when those upstream endpoints allow the token.
- The updated organization guide exposes department creation at `POST /organizationGroup/createOrganizationGroup` with `organizationId`, `groupName`, optional `groupDesc`, and optional `parentId`.
- The account page can create a department from the member form and then refresh/select the created department, so admins do not need to leave account management before creating members.
## 2026-07-02 Image Template Findings
- User requested image-generation templates with selection-time effect preview and preset prompt support.
- This should be account-managed data, not global environment configuration.
- Existing browser image generation API already uses `requireAppUser()` and submits `ownerId: user.id`, so template APIs should use the same owner boundary.
- Template configuration should remain inside the image generation module rather than the global settings page.
- The create page already keeps image/video prompt state client-side, detects the active image engine from `/api/health`, and submits prompt, materials, size, force-single, and engine-specific tuning to `/api/generations/image`.
- Implemented image templates with fields for name, category, description, preset prompt, preview image URL, width/height, force-single, and sort order.
- New first-party routes are `/api/image-templates` and `/api/image-templates/:id`; both require the app user and use the authenticated user id as owner id.
- Local JSON state now normalizes `imageTemplates`; Supabase deployments need the new `image_templates` table from `supabase/schema.sql`.
- Browser verification used a temporary auth-disabled dev server to create one demo template, confirm the `/create` left template rail and template application behavior, then deleted the demo template.
- Final dev server was restarted with the normal `.env.local` auth-enabled environment on `127.0.0.1:3001`.
## 2026-07-02 Large Auth Session Finding
- A new real account could receive a successful `/api/auth/password` response and still be redirected back to `/auth/login?next=/create`, which indicates the browser did not persist a valid session rather than an upstream password failure.
- The session cookie stores the signed session plus access token so the app can forward the current login token to organization/staff APIs.
- Accounts with larger JWT or authority payloads can exceed a single browser cookie's practical size limit; splitting `zhinian_session` into chunked cookies keeps the same signed payload while allowing middleware and server helpers to reassemble it.
- Settings no longer exposes a template tab.
- In image mode, `/create` now renders a left vertical `模板选择` rail with an icon `添加模板` button and thumbnail/name-only template cards.
- The add-template entry opens an in-module template form modal; it is not exposed from settings.
- Clicking a template selects it and brings its preset prompt, image size, and force-single value into the generation console on the right.
- Desktop and narrow browser verification confirmed the template picker is an independent left-side panel beside the generation panel, not a child of the generation panel, with no horizontal overflow.
- The image/video/edit mode switch is now contained by the generation panel, not placed above or across the far-left template module.
- Template preview image upload now uses the same `/api/assets/upload` route as workbench materials, so configured OSS storage is reused instead of accepting a manually typed preview URL.
- New template configuration no longer exposes category or remark fields; the user-facing free text field is `简介`, backed by the existing template description data.
- Template preset prompts can contain `@图片1` style placeholders. The generation console parses those tokens and shows missing upload slots below the prompt.
- Placeholder upload slots bind the uploaded asset to the exact requested token, so clicking the `@图片2` slot creates/updates the `@图片2` material instead of relying on upload order.
- Material labels are preserved after removal to avoid silently breaking prompt references created by templates.
- UI/UX skill guidance for the add-template modal was applied as a focused SaaS workspace form: strong preview region, fewer visible decisions at once, clear bottom actions, and responsive stacking on narrow screens.
- While editing a template prompt, parsed placeholder chips are shown below the textarea so users can confirm which upload slots the template will request after selection.
- Image templates now store the target image generation engine in `settings.engine`, with Jimeng using `settings.scale` and Image2/EvoLink using `settings.quality`.
- First-party image generation now accepts a per-request `engine` override and passes it into `submitImageJob()`, so template engine choices are honored by the provider payload.
- The generation console exposes the current image engine selector; applying a template updates this selector plus the matching parameter control.
- Material placeholders now require an explicit number, so `@图片1` and `@图2` create upload slots while bare `@图片` or `@图片这种普通文字` stay plain prompt text.
- Template prompt editing includes explicit placeholder insertion controls for image/video/audio slots, making the placeholder confirmation action deliberate instead of relying on unfinished `@` typing.
- Existing image templates can be reopened from the top-right edit action on each template card and saved through the account-scoped PATCH route.
- The template rail is intentionally wider on desktop and each card separates image preview, template selection, and template editing into distinct controls.
- Clicking a template thumbnail opens a larger preview dialog; choosing from that dialog applies the template and closes the preview.
- Typing `@` in prompt editors now opens a temporary material draft slot instead of immediately writing a partial token into the prompt body.
- A material draft is confirmed into the prompt only by Enter or explicit option selection; confirmed values are inserted with text boundaries so subsequent Chinese text is not absorbed into the token.
- The temporary draft slot is shared by the generation prompt and template preset prompt; invalid text such as `图片这种普通文字` stays in the slot and does not create upload requirements.
- Confirmed numbered tokens keep using the existing visual token overlay, placeholder chips, and missing upload-slot UI.
- The temporary material draft slot should remain visually inside the prompt editor surface, not below the input, so `@` entry feels like an inline editing affordance.
- Template preset prompts use the same token overlay as the generation prompt; confirmed placeholders such as `@视频1` are visibly colored inside the input area before saving the template.
- The image template rail now uses a wider desktop/tablet track (`390-480px` desktop, `315-375px` tablet) and stacks above the generation panel on mobile so reference previews stay browseable.
- Template thumbnails use a `4:3` viewport with contained images, allowing both `9:16` and `16:9` reference images to be inspected without cropping.
- Template card application now requires the explicit `选择模板` button; thumbnail clicks open preview and template name/description text no longer selects the template.
- The template rail remains visible in image and video generation modes; only the image-editing modes (`局部重绘`, `智能超清`) use the standalone editor layout without the template rail.
- Template cards are fixed compact items (`138px x 184px`) inside the rail list, so each template has a consistent smaller footprint.
- The template rail height is synchronized from the right generation panel through a `ResizeObserver`; the rail body is split into fixed header/action rows and a scrolling template-list row.
- The scrollbar belongs only to `.image-template-rail-list`; `模板选择` and `添加模板` stay outside the scroll container.
- Template rail cards now use their preview image as a full-bleed card surface with `object-fit: cover`.
- Template card title, intro, and select action sit inside a bottom floating frosted-glass overlay; the select button remains the only action that applies the template.
- The zoom icon was removed from template cards, while the edit icon remains on the top-right corner above the image.
- Selected template cards use a heavier layered shadow and slight upward transform to create stronger depth.
- The template intro/description field is no longer part of the create/edit template UI or card display; old stored descriptions may still exist in data but are ignored by this surface.
- The template edit icon now sits inside the card's frosted-glass overlay instead of on the image corner.
- Template selection is a toggle: the first click captures the current right-side image generation state and applies the template; clicking the selected template again restores that snapshot and clears the active template state.
- The compact full-bleed template card target is `132px x 176px`; the widened desktop rail should fit three cards per row.
- The card footer uses a two-row frosted-glass grid: title spans the first row, while the select/cancel button and edit icon share the second row to avoid cramped text or overlap.
## 2026-07-02 Create Task Module Findings
- The creation console can reuse existing first-party APIs for a task list: `/api/generations/image`, `/api/generations/video`, and `/api/assets`.
- `GenerationJob` already has the needed task fields: prompt/name fallback, status, created/updated timestamps, capability, and output asset ids.
- Generated task thumbnails can be resolved by mapping `GenerationJob.outputAssetIds` to assets from `/api/assets`; queued/running tasks need a placeholder when no output asset exists yet.
- The existing results page already has a task view and detail panel, so the create-page `查看详情` action deep-links to `/assets?view=tasks&taskId=<id>` instead of creating a separate task page.
- Desktop create layout now supports three independent modules: left template rail, center generation console, and right task module. At narrower widths the task module drops below the main modules, and at mobile widths all modules stack without horizontal overflow.
- The right task module now targets `560-640px` on desktop/wide screens so task name, status, elapsed time, and `查看详情` can stay on one horizontal row.
- Queued/running jobs without output assets should render a clear `生成中` thumbnail placeholder instead of a generic empty asset icon.
- Completed image-task thumbnails are preview actions inside `/create`; clicking them opens the same large asset-preview surface while `查看详情` remains the route to the full task page.