179 lines
19 KiB
Markdown
179 lines
19 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.
|