Skip to content

feat: video generation console, admin settings and plugin catalog - #12

Merged
nextroad-dev merged 7 commits into
mainfrom
feat/video-generation-admin-settings
Sep 19, 2026
Merged

nextroad-dev merged 7 commits into
mainfrom
feat/video-generation-admin-settings

Conversation

@nextroad-dev

@nextroad-dev nextroad-dev commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Video generation in the create console. Parameter panels are resolved from the selected model's declared parameter descriptors instead of one hardcoded control set (Veo's string enums and Seedance's integer seconds now share a single control path), the 图像/视频 tab keeps its own model choice, and reference images are staged under the role the model reserves (首帧 / 尾帧 / 参考图). The worker forwards those roles only when the whole list is placeable, and Veo places input images by role rather than array order — otherwise it falls back to the old positional contract instead of being rejected upstream.
  • Failure feedback. jobDto surfaces the already-sanitised provider_error.detail, trimmed and capped at 300 chars; status codes and provider reference ids stay server-side. A new active-jobs board shows queue status, phase, elapsed time, cancel-in-flight and that message; the library renders image and video through a shared media-frame with an 全部/图像/视频 filter.
  • Admin. 站点信息 / SMTP / 对象存储 / 运行时限制 move out of the post-install /setup wizard into /admin/settings, and a new /setup/layout closes the wizard once setup is complete. /admin/plugins catalogs the built-in provider templates and their credential schema with a shared create/edit dialog, and provider-templates.ts mirrors the server's plugin-identity rule so a model form only offers pairings the API would accept.
  • Fixes. /history no longer redirects to the removed /generate?tab=history; the landing page now describes the shipped flow and its real limits. packages/contracts declares @types/node — it typechecks src/endpoints.test.ts (node:test, node:assert/strict) without declaring it, which passed locally only because ambient types resolved through a stray root node_modules/@types/node and failed CI's clean frozen install with TS2307.

Commits

68231ad feat(providers,worker): place video input images by their staged role
4f6f06f feat(api): expose the sanitised provider failure detail on the job DTO
797abb5 feat(web-next): drive the create console from model parameter descriptors
4bc899e feat(web-next): show video assets in the library
1c93d22 feat(web-next): move system settings and provider plugins into the admin shell
67e350f fix(web-next): retarget /history and rewrite the landing page around the shipped flow
49a5233 fix(contracts): declare @types/node so CI can typecheck the endpoint tests

Test plan

  • pnpm typecheck — clean across all workspace projects
  • pnpm test — 0 failures everywhere (api 84, providers 95, worker 25, domain 7, database 5, contracts 3), including the new Veo role-placement / role-fallback tests and the job-DTO leakage + truncation test
  • CI-parity check for the types fix: tsc --noEmit --typeRoots $PWD/node_modules/@types passes in packages/contracts, and an empty typeRoots reproduces the exact CI TS2307 errors
  • pnpm install --frozen-lockfile succeeds with the updated lockfile
  • L3 deep security review on the committed changes — no findings
  • CI green on 49a5233: Media quality (no external services) plus all four image builds and the PostgreSQL rotation check
  • Manual: submit a Veo first/last-frame job against a live provider and confirm the frames land in the right slots (needs a real credential)
  • Manual: /setup redirects to /admin/settings once setupComplete, and a non-admin is sent to /login
  • Manual: library video playback and the kind filter (GET /api/library ignores kind, so filtering is client-side over the fetched 50-row page)

Notes for review

  • admin-provider-credential-dialog.tsx posts a pasted Google service-account JSON (including private_key) as credential. The API side is unchanged in this PR — please confirm it encrypts at rest and never echoes the value back.
  • apps/web-next/src/shared/types/index.ts continues to hand-mirror the API DTOs (errorMessage, Asset.fps/codec/hasAudio/posterAssetId, optional VideoGenerationOutput.imageUrl); web-next has no test suite and pnpm lint is currently a no-op, so drift here is only caught by hand.
  • UI behaviour on this branch is type-checked and CI-green but not runtime-verified — the three manual items above are still open.

The reference-image roles users pick in the console were dropped at the
queue boundary, so video plugins always received images in array order and
a first/last frame pair could silently land in the wrong slot.
MediaInputImage now carries the stored role, the worker forwards it as
extra.imageRoles only when every image has a role the plugin can place
(a partial list, an untagged legacy row, or a prompt_image/source_video
keeps today's positional behaviour instead of being rejected upstream), and
Veo resolves the list into image/lastFrame/referenceImages.
A rejected generation only surfaced an error code, so users saw
PROVIDER_REJECTED with no way to tell what to fix. jobDto now returns the
already-trimmed provider_error.detail as errorMessage, capped at 300 chars,
and reads that one field instead of spreading the object: HTTP status,
provider reference ids and any other transport metadata stay server-side.
…tors

Video controls used to hardcode one dropdown set, which broke as soon as two
plugins spelled the same parameter differently (Veo takes string enums,
Seedance integer seconds). generation-params now resolves every field through
the selected model's declared descriptors with an alias table, so wire keys
and value types come from the descriptor itself and the panel renders only
what a model actually supports; the store holds a videoParams map instead of
four fixed fields, so switching models needs no reset and the model choice is
remembered per 图像/视频 tab.

Reference images are staged under the role the chosen model reserves for
them (首帧 / 尾帧 / 参考图) and reconcileStagedRoles re-assigns roles on
model change, blocking submit rather than silently dropping an overflow.
An active-jobs board reports queue position, phase, elapsed time, cancel-in
flight and the new job errorMessage; media-frame renders image and video
outputs behind one surface.
The grid assumed every asset is a downloadable still. It now renders through
media-frame, adds an 全部/图像/视频 segmented filter and reads the preview
target from the fetched list so deep links survive a page change. The kind
filter is applied client-side over the fetched rows only, because
GET /api/library ignores `kind` and returns a fixed 50-row page.
…min shell

The /setup wizard stayed reachable after onboarding and its steps were the
only editor for site, SMTP, object-storage and runtime limits. Those panels
now live at /admin/settings behind the admin guard, and a new /setup/layout
redirects a completed setup to it (or to /login), closing the wizard.

/admin/plugins catalogs the built-in media provider templates with their
credential schema and hosts the create/edit dialog, leaving /admin/providers
as the list, delete and connectivity test. provider-templates.ts mirrors the
server's plugin-identity matching so a model form only offers a credential
whose plugin and version the API would accept.
…the shipped flow

/history still 301'd to /generate?tab=history, but the console now splits
into 图像/视频 only, so the tab was dead; it points at /library as a
temporary redirect instead. The landing page advertised prompt "thinking",
a 68% progress tile and other states the product never had, and omitted
what it does do; it now walks the real path (model, aspect ratio, reference
images, queue with retry and cancel, library, code login, self-hosted admin)
with the actual limits stated.
…tests

packages/contracts runs tsc over src/endpoints.test.ts, which imports
node:test and node:assert/strict, but it was the only package with a
typecheck script that never declared @types/node. Locally tsc still passed
because ambient types resolve through a stray root node_modules/@types/node;
CI's clean frozen install has none, so the run died with TS2307 before
checking any later package.
@nextroad-dev
nextroad-dev merged commit b6d668d into main Sep 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant