50 KiB
50 KiB
Feature Code Location Index
Last source audit: 2026-05-20, based on git commit 632c94b.
Use this document to jump directly to code before broad searching.
Global Application Shell
| Feature | Primary Files | Notes |
|---|---|---|
| Root layout and providers | src/app/layout.tsx, src/components/app-shell.tsx, src/app/globals.css |
App shell wires navbar, site config sync, visit tracking, theme/account sync, toaster, full-width page mounting, and transient scrollbar visibility. Keep product content at the original component scale; use centered responsive containers instead of stretching all content to viewport edges. Global scrollbars are hidden by default and briefly show the rounded glass style when wheel/touch scrolling adds scrollbars-visible on <html>. |
| Home page | src/app/page.tsx |
Landing/dashboard-like public entry. Check site config dependencies when changing brand text. |
| Navbar | src/components/navbar.tsx, src/components/site-brand.tsx |
Navigation, brand display, auth-aware links. User-facing nav should not include removed feature routes. Logged-in desktop/mobile user buttons should show AuthUser.avatarUrl first and fall back to the display nickname initial only when the avatar is missing or fails to load. Avoid eager all-route router.prefetch(...) from the navbar; on production web it can compete with visible route/resource loading. The initial logged-in profile refresh is idle and one-shot so it does not compete with the first route transition. Mobile bottom navigation must render as a sibling after the sticky header, not inside it, because header backdrop/filter contexts can trap position: fixed children away from the viewport bottom. |
| Footer | src/components/site-footer.tsx |
Uses site config for policy/help/about links and filing text; footer background spans browser width while inner content keeps the original max-w-7xl scale. |
| Announcement popup | src/components/announcement-popup.tsx, src/app/api/announcements/route.ts, src/app/globals.css |
Frontend popup behavior plus backend announcement CRUD. Desktop dialog is intentionally wide (max-w-5xl) for long Markdown notices; scrollbar styling is inherited from the global glass scrollbar rules. |
| Site config sync | src/components/site-config-sync.tsx, src/lib/site-config.ts, src/app/api/site-config/route.ts |
Site name, tab title, logo, favicon, policy Markdown, filing, membership switch. useSiteConfig() keeps a shared browser snapshot, skips network refresh while that snapshot is still within the 5-minute TTL, and reuses the in-flight /api/site-config refresh so global consumers such as navbar, footer, site-brand, and policy pages do not each create their own concurrent config request during navigation. The API route runs legacy schema/default compatibility checks once per server process and retries on failure. |
| Visit tracking | src/components/visit-tracker.tsx, src/app/api/site-stats/route.ts |
Public visit counter. The client posts with keepalive after browser idle time so statistics collection does not block first paint or navbar route transitions. |
| Security headers and iframe embedding | src/proxy.ts, .env.example |
CSP is set in the Next proxy. frame-ancestors controls which external platforms may embed MiaoJing in an iframe; MIAOJING_FRAME_ANCESTORS can override the default self + mozheAPI allowlist. When external ancestors are allowed, do not send X-Frame-Options: SAMEORIGIN, because it blocks third-party iframes. |
Public Pages
| Route | Primary Files | Supporting Files |
|---|---|---|
/ |
src/app/page.tsx |
src/components/app-shell.tsx |
/create |
src/app/create/page.tsx |
src/components/create/* |
/gallery |
src/app/gallery/page.tsx |
src/lib/creation-history-store.ts, src/app/api/gallery/route.ts |
/image-viewer |
src/app/image-viewer/page.tsx |
Fullscreen original-image share page opened from image right-click share links. |
/profile |
src/app/profile/page.tsx |
src/components/profile/*, src/app/api/profile/route.ts |
/about |
src/app/about/page.tsx |
src/components/site-policy-page.tsx, src/lib/site-policy-defaults.ts |
/terms |
src/app/terms/page.tsx |
src/components/site-policy-page.tsx |
/privacy |
src/app/privacy/page.tsx |
src/components/site-policy-page.tsx |
/help |
src/app/help/page.tsx |
src/components/site-policy-page.tsx |
Auth And User Account
| Feature | Primary Files | Notes |
|---|---|---|
| Login UI | src/app/auth/login/page.tsx |
Calls /api/auth/login. |
| Register UI | src/app/auth/register/page.tsx, src/components/auth/registration-agreement-dialog.tsx |
Requires accepted terms and email code except admin invite path. |
| Auth store | src/lib/auth-store.ts |
Client auth state and token persistence. AuthUser.username is the login username from profiles.nickname; AuthUser.nickname is the public display nickname from profiles.display_nickname. |
| Session tokens | src/lib/session-auth.ts |
HMAC token format, bearer parsing, admin checks. |
| Login API | src/app/api/auth/login/route.ts |
Handles admin fallback and normal users. |
| Register API | src/app/api/auth/register/route.ts |
Creates auth.users, profiles, initial credits, random Chinese display nickname, and default 3D cartoon avatar. The submitted register nickname is treated as login username for compatibility. |
| Admin exists | src/app/api/auth/admin-exists/route.ts |
Admin setup checks. |
| API test/model fetch | src/app/api/auth/test-api/route.ts, src/app/api/auth/fetch-models/route.ts |
Used by provider/API configuration UI. |
| Profile API | src/app/api/profile/route.ts, src/app/api/profile/theme/route.ts |
Profile edits, password/email/theme. |
Creation Center
| Feature | Primary Files | Server/API Files |
|---|---|---|
| Tab container | src/app/create/page.tsx |
Owns the five creation tabs. Active tab is persisted in localStorage and mirrored to /create?type=..., so refreshes and shared links stay on text-to-image, image-to-image, text-to-video, image-to-video, or reverse-prompt. Keep the five primary creation panels statically imported in this page: production users switch between these modes constantly, and ssr:false dynamic splitting adds visible chunk waits and fallback flashes on direct web access. On phones the mode switch is the single fixed icon row below the navbar; the page title and duplicate text mode strip are hidden. Mobile layout classes in this page and src/app/globals.css turn the create center into a chat-style flow for all five modes: history/status cards render above the fixed composer, hidden desktop result/history regions should not keep loading large media, and src/components/create/mobile-creation-composer.tsx owns the bottom send surface with compact params, optional styles/references, prompt or custom input, and right send button. The mobile thread reserves the measured composer height through --create-mobile-composer-height so fixed-bottom input does not cover prompts or previews. |
| Text to image | src/components/create/text-to-image.tsx |
src/app/api/generation-jobs/route.ts, src/app/api/generate/image/route.ts, src/components/create/use-generation-job-recovery.ts. The create button remains available while other active tasks run; duplicate in-flight submissions are still blocked by activeSubmissionSignaturesRef. Active jobs render through src/components/create/generation-task-list.tsx inside the results column and expose a cancel action that calls PATCH /api/generation-jobs/[id]. Model select items use src/components/create/grouped-model-select-items.tsx so admin global system models appear under 默认模型 and user-added keys appear under 自定义模型. Selected model capabilities from src/lib/model-capabilities.ts can hide unsupported aspect ratio/resolution/format/quality controls as well as filter their options, which is required for built-in 元界 image templates such as GPT Image 2 where the docs expose size pixel values instead of a separate aspect-ratio control. It consumes reuse drafts from src/lib/creation-reuse.ts and opens src/components/create/inspiration-gallery-dialog.tsx from the 获取灵感 action so gallery text-to-image works can fill prompt, negative prompt, model, ratio, resolution, format, quality, count, style, and guidance into the form. The create-panel history hook must stay scoped, e.g. useCreationHistory({ mode: 'text2img', limit: 60 }), so opening /create does not download the user's full history payload. The mobile conversation history should only mount on mobile viewports; CSS-hidden mobile history still runs image effects if mounted on desktop. |
| Image to image | src/components/create/image-to-image.tsx, src/components/create/reference-image-mention-controls.tsx, src/components/reference-preview-image.tsx |
src/app/api/generation-jobs/route.ts, src/app/api/generate/image/route.ts, src/components/create/use-generation-job-recovery.ts, src/lib/reference-image-prompt.ts, src/lib/reference-image-storage.ts. Reference thumbnails single-click into a bare image overlay and use lightweight local preview rendering instead of painting the full uploaded data URL in every card. Active jobs render through src/components/create/generation-task-list.tsx, and the create button remains available while active tasks exist; identical in-flight submissions are still deduped. Model select items use src/components/create/grouped-model-select-items.tsx for 默认模型 versus 自定义模型 grouping. Selected model capabilities from src/lib/model-capabilities.ts can hide unsupported aspect ratio/resolution/format/quality controls as well as filter their options, which is required for built-in 元界 image templates such as GPT Image 2 where the docs expose size pixel values instead of a separate aspect-ratio control. 图生图 removes 自动 from ratio/resolution/count controls, defaults count to 1, and derives ratio from Yuanjie size labels or dimensions when the selected model hides the separate ratio control. It consumes reuse drafts from src/lib/creation-reuse.ts and opens src/components/create/inspiration-gallery-dialog.tsx from the 获取灵感 action so gallery image-to-image works can place reference images and fill prompt, negative prompt, model, ratio, resolution, format, quality, count, style, and strength into the form. 多参考图会显示 @参考图1 等标签,提示词输入框输入 @ 可选择参考图,提交时发送 referenceImageAnnotations,后端把 token 与上传顺序、文件名、尺寸写入上游 prompt;创作历史会把 data URL/远程参考图持久化到 works/references 并写入 referenceImageThumbnails,分享到画廊会携带所有参考图和标注。 Mobile uses the fixed composer with an upload/reference thumbnail strip, mention-aware prompt input, compact ratio/resolution/format/count controls, and a mobile-only status/history flow. |
| Text to video | src/components/create/text-to-video.tsx |
src/app/api/generation-jobs/route.ts, src/app/api/generate/video/route.ts, src/components/create/use-generation-job-recovery.ts. The create button remains available while active tasks exist, active jobs render through src/components/create/generation-task-list.tsx, running tasks can be cancelled, and model select items use src/components/create/grouped-model-select-items.tsx for 默认模型 versus 自定义模型 grouping. It consumes video reuse drafts from src/lib/creation-reuse.ts and opens src/components/create/inspiration-gallery-dialog.tsx from the 获取灵感 action so gallery text-to-video works can fill prompt, negative prompt, model, ratio, duration, camera movement, and style. Mobile uses the fixed composer with compact ratio/duration/resolution/camera controls, a horizontal style strip, and thumbnail/placeholder history cards instead of preloading hidden desktop videos. |
| Image to video | src/components/create/image-to-video.tsx, src/components/create/reference-image-mention-controls.tsx, src/components/reference-preview-image.tsx |
src/app/api/generation-jobs/route.ts, src/app/api/generate/video/route.ts, src/components/create/use-generation-job-recovery.ts, src/lib/reference-image-prompt.ts, src/lib/reference-image-storage.ts. Uploaded reference thumbnails single-click into the same bare image overlay used by image-to-image and use lightweight local preview rendering. Active jobs render through src/components/create/generation-task-list.tsx, the create button remains available while active tasks exist, and running tasks can be cancelled. Model select items use src/components/create/grouped-model-select-items.tsx for 默认模型 versus 自定义模型 grouping. It consumes video reuse drafts from src/lib/creation-reuse.ts and opens src/components/create/inspiration-gallery-dialog.tsx from the 获取灵感 action so gallery image-to-video works can place reference images and fill prompt, negative prompt, model, ratio, duration, and camera movement. 多参考图会显示 @参考图1 等标签,提示词输入框输入 @ 可选择参考图,提交时发送 referenceImageAnnotations,后端把 token 与上传顺序、文件名、尺寸写入上游 prompt;创作历史会把 data URL/远程参考图持久化到 works/references 并写入 referenceImageThumbnails,分享到画廊会携带所有参考图和标注。 Mobile uses the fixed composer with an upload/reference thumbnail strip, mention-aware prompt input, compact video controls, and thumbnail/placeholder history cards instead of preloading hidden desktop videos. |
| Reverse prompt | src/components/create/reverse-prompt-panel.tsx |
src/app/api/generate/reverse-prompt/route.ts, src/app/api/generate/suggest-prompt/route.ts, src/lib/generation-job-client.ts, src/components/create/use-generation-job-recovery.ts. Reverse prompt now runs as a background job, survives refresh/auth change/tab switch, and writes the completed result back into the normal creation history flow instead of relying on an optimistic local-only row. Mobile uses the fixed composer custom input slot for upload/change-image, prompt mode, language controls, and a mobile status/result/history flow. |
| Prompt textarea | src/components/create/expandable-prompt-textarea.tsx |
Shared prompt input. |
| Mobile creation composer | src/components/create/mobile-creation-composer.tsx, src/app/globals.css |
Mobile-only fixed bottom composer used by all five creation panels to match chat-style clients: top parameter strip with compact dropdown buttons, optional reference/upload strip, optional style strip, default prompt input or a custom input slot, and right send button. It uses ResizeObserver to publish --create-mobile-composer-height on the nearest .create-chat-layout; .create-chat-thread must keep a matching bottom padding so the fixed composer stays at the bottom without covering prompts, references, or status cards. The mobile creation center uses one 16px UI font size across selected values, style chips, composer input, and conversation prompts. The mobile text/image parameter strips hide long labels and remove 自动 from compact ratio/resolution/count choices where the panel already defaults to explicit values. The mobile style strip shows only one horizontal row when collapsed and expands upward for search/more presets after tapping 展开; video styles use a horizontal chip strip. Mode selection stays only in the sticky header tabs. Desktop creation forms remain the source for full advanced controls, while mobile result/history/status flows mount only on mobile viewports. |
| Image count input/dropdown | src/components/create/image-count-combobox.tsx |
Shared compact count control for manual image count entry and common dropdown options. |
| Style presets | src/components/create/style-preset-selector.tsx, src/lib/style-presets-client.ts, src/app/api/style-presets/route.ts, src/lib/style-preset-store.ts, src/lib/model-config.ts |
Style presets are stored in image_style_presets, seeded from defaults, sorted by usage_count, and incremented from image generation jobs. The selector exposes stable .style-preset-selector and .style-preset-list classes so mobile create CSS can show a one-row collapsed strip and an expanded list of at least several rows inside the bottom composer. |
| Loading/error panels | src/components/create/generation-loading-panel.tsx, src/components/create/generation-task-list.tsx, src/components/create/generation-error-panel.tsx |
Shared generation status UI. generation-task-list keeps multiple active job cards constrained to the results column, exposes cancel buttons for normal active tasks, and image/video create panels render active tasks plus completed result cards together so earlier finished jobs do not disappear while later jobs keep running. |
| Creation reuse drafts | src/lib/creation-reuse.ts, src/app/create/page.tsx, src/components/create/inspiration-gallery-dialog.tsx |
Shared localStorage/event bridge used by detail, reverse-prompt, gallery, and inspiration actions to prefill create panels. It supports text2img, img2img, text2video, and img2video draft keys/events; /create?type=... changes the active tab after navigation, so callers can route directly to the matching creation mode. If a reuse action intentionally uses a generated output as a new reference image, it must use the original url rather than thumbnailUrl; thumbnails are display-only and must not be sent back into image-to-image or image-to-video generation. The inspiration dialog filters to the current mode, keeps per-card mode labels hidden, and offers a fuzzy search box that animates leftward from the header search icon; empty searches auto-collapse after the pointer leaves the search control for 1 second, while non-empty searches stay open until the dialog closes. |
| Lightbox/fullscreen/detail actions | src/components/lightbox.tsx, src/components/fullscreen-preview.tsx, src/components/creation-detail-dialog.tsx, src/components/image-actions-context-menu.tsx, src/components/image-metadata-badge.tsx, src/app/image-viewer/page.tsx, src/components/create/cached-preview-image.tsx |
Image cards, detail images, reference thumbnails, and generation results should enter fullscreen preview on single click, not double-click. Detail and fullscreen images use the shared right-click image action menu for copy, download, edit-to-image-to-image, and share; these actions must receive the original image URL, not thumbnails or cached display blobs. Fullscreen/lightbox components can receive a thumbnail fallback to display immediately while the original object-storage URL loads. Share copies a /image-viewer?url=... full-display link for the original image. Delete work must use a confirmation dialog warning that deletion cannot be recovered before calling the server delete path. Image previews show actual natural resolution and computed aspect ratio in the upper-right metadata badge; detail dialogs must pass stored width/height with loadMetadata={false} so the badge does not fetch the original image just to compute size. BareImagePreview is the no-container overlay for uploaded reference image previews. CachedPreviewImage generates same-origin cached previews and proxies cross-origin historical URLs through /api/download?disposition=inline to avoid browser CORS failures. |
Generation System
| Responsibility | Primary Files | Notes |
|---|---|---|
| Client-side job polling | src/lib/generation-job-client.ts |
Create/poll jobs from create panels. Active-job recovery skips anonymous list polling and reuses same-token, same-type list requests briefly, so refresh/auth-change recovery does not add duplicate /api/generation-jobs pressure while tasks keep polling individually until success/failure. Created job ids are also stored per logged-in user in browser localStorage until the job reaches succeeded, failed, or cancelled; useGenerationJobRecovery merges that pending list with server queued/running jobs and queries /api/generation-jobs/[id] so jobs that finish while the browser is closed can still reappear with their terminal result/error before being cleared. |
| Job creation API | src/app/api/generation-jobs/route.ts |
Inserts generation_jobs, starts worker, increments selected image style preset usage, and preflights system-default-model credit balance through src/lib/generation-credit-service.ts, including queued/running system-default jobs already waiting for the same user. Active queued/running jobs are semantically deduped while ignoring top-level clientRequestId, so a double-click or fast retry returns the existing job instead of creating a second one. |
| Job status API | src/app/api/generation-jobs/[id]/route.ts |
Owner/admin visibility, stale running job handling. |
| Worker loop | src/lib/generation-job-worker.ts |
Picks and processes queued jobs. After successful system default image/video generation, it calls src/lib/generation-credit-service.ts to deduct credits from profiles.credits_balance, insert credit_transactions, and add creditsCost/creditsBalance to the job result for frontend display. Failed generation jobs do not enter the charge path. |
| Internal runner | src/lib/generation-job-runner.ts |
Calls /api/generate/image or /api/generate/video with internal headers. |
| ETA/progress | src/lib/generation-job-estimates.ts |
Runtime schema, ETA samples, progress payload. |
| Image route | src/app/api/generate/image/route.ts, src/lib/reference-image-prompt.ts, src/lib/layout-composition-skill.ts |
SDK + custom/system API + New API image compatibility, persistence. New image originals persist through src/lib/media-storage.ts into object storage, while local WEBP thumbnails are returned as thumbnails/thumbnailUrls for preview rendering and dimensions maps each original URL to persisted width/height so history detail metadata can avoid loading originals. Generated image originals are normalized to the user-selected output format before upload, so providers that ignore output_format and return PNG still produce .jpg/.webp objects when JPEG/WebP was requested. When site_config.image_composition_skill_enabled is true, src/lib/layout-composition-skill.ts deterministically selects one of the 100 CC BY 4.0 nevertoday/100-layout-compositions references and appends composition guidance before style prompts and upstream requests; it should not add text, logos, or literal poster elements. For admin default system models, image generation resolves all same-type/same-display-name default API candidates, automatically retries stream-timeout failures once with stream:false, and returns actionable upstream timeout/gateway messages when all candidates fail. If a Manifest provider such as 元界 returns result URLs but MiaoJing cannot download or save them, the route reports a platform download/save failure instead of a resolution mismatch. User custom APIs remain single-config and do not use this polling fallback. For image-to-image, optional referenceImageAnnotations are merged into the model prompt so @参考图N maps to the corresponding uploaded reference image. |
| Video route | src/app/api/generate/video/route.ts, src/lib/reference-image-prompt.ts |
SDK + custom/system API video, persistence. Generated video data URLs and upstream video URLs are persisted through localStorage.uploadFileObjectOnly(...) under generated/videos, so production video originals live in object storage when configured. Video create panels must use backend returned creditsCost/creditsBalance after job success; they should not locally predict or deduct credits. For image-to-video, optional referenceImageAnnotations are merged into the model prompt so @参考图N maps to the corresponding uploaded reference image. |
| Custom API transport | src/lib/custom-api-fetch.ts, src/lib/custom-image-fallback.ts |
Headers, one retry for 502/503/504 gateway failures, progress JSON parsing, upstream error parsing, stream-to-sync fallback policy for system image APIs. |
| Server API resolution | src/lib/server-api-config.ts, src/lib/yuanjie-system-manifest.ts |
Resolves user custom API and admin system API IDs into decrypted credentials, enforces system API default visibility plus membership-tier allowlists before generation, and builds default-model polling candidates by media type plus admin display name (system_api_configs.name). For known 元界 system rows with missing or stale manifest_path, both direct system API resolution and default-model polling candidates can rewrite the built-in Manifest and normalize api_url to the 元界 base URL before generation. The upstream model_name remains the per-provider request model only. |
| User API smart import | src/components/profile/api-key-manager.tsx, src/app/api/user-api-keys/smart-import/route.ts, src/lib/user-api-manifest.ts, src/lib/user-api-manifest-executor.ts, src/lib/model-capabilities.ts, src/lib/model-display.ts |
The profile API settings page has an 智能配置 API button next to 添加 API 密钥. It opens a wide viewport-capped Manifest editor, can copy the LLM prompt, shows guidance under the prompt button explaining the copy-to-chat-AI and paste-and-import flow, can paste clipboard JSON without importing, and can paste-and-import in one action. The prompt instructs the LLM to stop and ask the user for the relay API Base URL when the docs do not contain it. Imports create each profile/model as an independent user_api_keys row plus a separate user-api-manifests/<userId>/<keyId>.json file and reject incomplete configs without a resolvable request URL. Imported rows should store a human-readable provider name in the editable provider/supplier fields and resolve the visible API request URL from profile.baseUrl + submit.path for synchronous endpoints. Generic placeholder notes such as 导入的 API Key must not be used as model labels; creation/profile UI should prefer a real note plus model, or provider plus model. Optional profile.capabilities filters or hides create-page aspect ratio, resolution, image format, and quality controls for the selected model. Polling Manifest query values can include {task_id} so task IDs are sent as real query parameters rather than being embedded into pathname strings. Generation routes must use the selected model key's manifest_path; do not merge different request configs under one user-level file. |
| Admin system API smart import | src/components/admin/api-management-tab.tsx, src/app/api/admin/system-apis/smart-import/route.ts, src/app/api/admin/system-apis/route.ts, src/lib/server-api-config.ts, src/lib/user-api-manifest.ts, src/lib/user-api-manifest-executor.ts, src/lib/model-capabilities.ts |
The console API management page has a separate 智能配置 API section for admins, but this section is generic Manifest import only. It supports copy-to-chat-AI and paste-and-import Manifest flow, then creates one independent system API row and system-api-manifests/<systemApiId>.json file per imported profile/model. Imported rows resolve the visible API request URL from the Manifest profile/provider before save, and optional profile.capabilities can constrain or hide create-page image/video parameter choices for the selected system model. Provider-specific built-in template management, including 元界 AI and Agnes AI, belongs in the 系统默认模型 management flow and should not be exposed in the smart import UI. 元界价格/计费方式手动同步 uses src/app/api/admin/system-apis/yuanjie-pricing/route.ts and src/lib/yuanjie-pricing-sync.ts; it updates only existing 元界 image/video rows, tolerates provider spellings such as 元界AI, and leaves mozheAPI/global smart-import configs untouched. |
| Admin console active page persistence | src/modules/console/pages/console-dashboard-page.tsx |
The console active view is stored in sessionStorage, so browser refresh keeps the current admin page/tab. Logout clears the value, and closing/reopening the console starts from the dashboard because sessionStorage is tab-scoped. |
| Manifest input image URLs | src/lib/user-api-manifest-executor.ts, src/app/api/generate/image/route.ts, src/app/api/generate/video/route.ts, src/lib/reference-image-prompt.ts |
Manifest templates can use $inputImages.dataUrls for raw uploaded data and $inputImages.urls for provider-facing public references. The executor converts data URL input images into storage-backed URLs before rendering templates. Image-to-image and image-to-video generation normalize the primary image plus extraImages/images into Manifest inputImages, so multi-reference providers such as Yuanjie GPT Image 2 receive all references. referenceImageAnnotations are not a Manifest variable; routes fold them into $prompt before execution so existing templates inherit the mapping. |
Models And Providers
| Feature | Files | Notes |
|---|---|---|
| Built-in model options | src/lib/model-config.ts, src/lib/model-config-types.ts |
Image/video model lists, ratios, sizes, inference helpers, and fallback style preset seed labels. Runtime style ordering comes from DB. |
| Public model config API | src/app/api/model-config/route.ts, src/app/api/style-presets/route.ts |
Returns model/provider config plus DB-backed image style presets for clients. |
| User custom API keys | src/lib/custom-api-store.ts, src/app/api/user-api-keys/route.ts, src/components/profile/api-key-manager.tsx |
User-owned encrypted API credentials. |
| Admin provider presets | src/app/api/admin/providers/route.ts, src/components/admin/api-management-tab.tsx |
Provider registry, default API URL/model/type. Reads and mutations require admin bearer auth; the admin tab must send Authorization for the initial list fetch too. |
| Admin system API configs | src/components/admin/api-management-tab.tsx, src/app/api/admin/system-apis/route.ts, src/app/api/admin/system-apis/agnes-capabilities/route.ts, src/lib/server-api-config.ts, src/lib/yuanjie-system-manifest.ts, src/lib/agnes-model-templates.ts, src/lib/agnes-template-installer.ts |
Encrypted shared system API credentials, pricing metadata, platform-default visibility, per-model membership-tier allowlists, and default-model polling fields (polling_mode, polling_order). The admin list browses system models by provider, then model type, then individual model rows for editing. When browsing 元界 AI, admins can manually click 同步元界价格 to call /api/admin/system-apis/yuanjie-pricing, which syncs billing mode and a 元界 pricing note from built-in template metadata without overwriting manually entered numeric prices; the sync matches 元界 provider variants such as 元界AI and keeps a provider/model-group guard to avoid mozheAPI. The same system-default page can install Agnes AI free templates through /api/admin/system-apis/agnes-capabilities; Agnes rows are inactive free billing templates with 0 credits, image/video rows use isolated system-api-manifests/<systemApiId>.json files, and text rows use chat/completions directly while waiting for admin-entered API Keys before activation. For legacy 元界 rows without Manifest, built-in capabilities still drive frontend options and generation resolution can write the missing Manifest. Models can be free (free), priced by per-use count (fixed), per-second duration (duration using duration_price_per_second), ratio, or token mode. Token billing input/output prices are configured as credits per 1M tokens in the console UI; the input_price_per_1k/output_price_per_1k DB/API field names are legacy-compatible storage names only. |
| Model recommendations | src/app/api/admin/model-recommendations/route.ts, src/components/admin/api-management-tab.tsx |
Admin-controlled displayed/recommended model lists. Reads and mutations require admin bearer auth. |
Profile, Credits, Orders
| Feature | Files |
|---|---|
| Profile page | src/app/profile/page.tsx |
| Profile API | src/app/api/profile/route.ts, src/app/api/profile/theme/route.ts, src/lib/user-profile-defaults.ts, src/lib/profile-preferences.ts |
| Creation history tab | src/components/profile/creation-history-tab.tsx, src/lib/creation-history-store.ts, src/app/api/creation-history/route.ts |
| Credits tab/store | src/components/profile/credits-tab.tsx, src/lib/credit-records-store.ts, src/app/api/credit-transactions/route.ts, src/app/api/redeem-codes/redeem/route.ts, src/app/api/invitations/me/route.ts, src/lib/invitation-service.ts |
| Orders tab/store | src/components/profile/orders-tab.tsx, src/lib/order-store.ts, src/app/api/admin/orders/route.ts |
| Billing guard | src/components/billing-plan-guard.tsx, src/lib/admin-store.ts |
Gallery
| Feature | Files | Notes |
|---|---|---|
| Public gallery page | src/app/gallery/page.tsx, src/app/globals.css, src/lib/gallery-cache-policy.ts |
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 rows remain usable for instant first paint until the 7-day prune window; page 0 is refreshed in the background for freshness, and a masonry skeleton replaces the old centered 加载中... state when no cache exists. Image cards and detail display use `thumbnailUrl |
| Public gallery API | src/app/api/gallery/route.ts, src/lib/gallery-response.ts |
GET public works with thumbnailUrl, total, nextOffset, and hasMore, queues missing or old-profile image thumbnails plus stale video SVG fallback thumbnails for background backfill without delaying the response, admin DELETE unpublishes. For videos, only video-frame-m1280q86-v1.webp counts as a current thumbnail; video-svg-v1 and video-fallback-svg-v2 are temporary fallback assets and should not block a later real-frame backfill. Gallery author names use profiles.display_nickname first and never expose login username unless no display nickname exists. Public list serialization filters data: and oversized publisherAvatarUrl values so generated default avatars do not bloat the gallery JSON payload or localStorage cache. |
| Publish API | src/app/api/gallery/publish/route.ts, src/lib/gallery-publish-media.ts |
Inserts public work after resolving gallery media. Stable /api/local-storage/... image and video originals are reused instead of synchronously copying object-backed generated media during share; external media is still copied into gallery storage first. Existing image thumbnails are reused so image sharing does not block on object-storage reads or thumbnail recompression; /api/gallery can lazily backfill missing/stale thumbnails. Video publishing first tries to generate a local WEBP frame preview under thumbnails/gallery/videos via ffmpeg-static, and only copies a client-provided thumbnail when real-frame extraction fails. Client code must treat /api/gallery/publish as authoritative and mark local works as shared only after a 2xx response. |
| 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, src/lib/admin-gallery-works-pagination.ts, scripts/test-admin-gallery-prompt-service.mjs |
Console-only workflow for editing public gallery works.prompt. The management table uses page/pageSize pagination while the list API keeps limit/offset compatibility. 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, stored width/height, and published state. Missing image thumbnails and old-profile video thumbnails are queued for background backfill instead of blocking the history response. Video history thumbnails use local WEBP frames when ffmpeg-static can extract one, with SVG as the failure fallback; fallback SVG files are not considered current thumbnails. `mode=text2img |
Admin Console
| Feature | Frontend | API |
|---|---|---|
| Console login | src/app/console/page.tsx, src/modules/console/pages/console-login-page.tsx |
src/app/api/auth/login/route.ts with adminOnly |
| Console dashboard | src/app/console/dashboard/page.tsx, src/modules/console/pages/console-dashboard-page.tsx |
src/app/api/admin/dashboard/route.ts, src/app/api/admin/stats/route.ts. The dashboard page owns the mobile admin shell classes (console-mobile-page, console-mobile-main, console-mobile-content) used by src/app/globals.css to keep cards constrained and admin tables horizontally scrollable on phones. |
| Users | src/components/admin/user-management-tab.tsx |
src/app/api/admin/users/route.ts, src/lib/admin-users-service.ts, src/app/api/admin/clear-users/route.ts, src/app/api/admin/invitations/route.ts. The user-management UI has separate subpages for 用户列表 and 邀请注册记录; invitation records have independent search, pagination, total count, inviter/invitee details, invite code, reward amounts, and creation time. Row actions include recharge, reset password, edit, and delete. Admin reset-password opens a separate reset form and /api/admin/users PUT upserts auth.users.password_hash for the target user. Admins can edit an individual user's 下载无水印 switch, which writes profiles.watermark_disabled through /api/admin/users without changing the membership tier. |
| API/model management | src/components/admin/api-management-tab.tsx |
src/app/api/admin/providers/route.ts, src/app/api/admin/system-apis/route.ts, src/app/api/admin/system-apis/smart-import/route.ts, src/app/api/admin/model-recommendations/route.ts |
| Pricing | src/components/admin/pricing-tab.tsx |
Admin store/site config related routes |
| Redeem codes | src/components/admin/redeem-code-management-tab.tsx |
src/app/api/admin/redeem-codes/route.ts, src/lib/redeem-code-service.ts, src/app/api/site-config/route.ts. Admins can generate one or many unique single-use redeem codes, choose credit-code or membership-code type, set credit amount or membership tier plus duration in days/months/years, copy generated codes, and manage unused code status. The same tab has a 商城链接配置 dialog that saves site_config.redeem_code_mall_url; frontend credit get-code buttons and membership upgrade buttons open that URL. |
| 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, src/lib/admin-gallery-works-pagination.ts. Lists public works with admin page/pageSize pagination, 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, scripts/migration-integrity-check-helpers.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; the checker defaults to port 8000 and counts bounded media probe failures instead of aborting on the first slow URL. |
| 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 |
| Settings | src/components/admin/settings-tab.tsx |
src/app/api/site-config/route.ts, src/app/api/admin/email-settings/route.ts, src/app/api/admin/send-email/route.ts. The feature toggles section includes membership enablement and the 100 Layout Compositions image composition skill switch. |
Storage And Downloads
| Feature | Files | Notes |
|---|---|---|
| Storage adapter | src/lib/local-storage.ts |
Uses stable /api/local-storage/<key> URLs while the backend can be STORAGE_MODE=local, dual, or object. Object mode uses S3-compatible OBJECT_STORAGE_* config; dual mode writes local disk first and mirrors to object storage for safe migration. |
| Rainyun ROS object storage preparation | scripts/rainyun-ros-prepare.mjs |
Uses the Rainyun control-plane API POST /product/ros/bucket to create a bucket from RAINYUN_ROS_BUCKET_NAME and RAINYUN_ROS_INSTANCE_ID, then writes a private .env.rainyun-object.generated file containing standard OBJECT_STORAGE_* variables. Do not use this control-plane API for runtime media reads/writes; runtime storage remains S3-compatible through src/lib/local-storage.ts. |
| Local/object file API | src/app/api/local-storage/[...path]/route.ts, src/lib/media-watermark-policy.ts, src/lib/media-watermark.ts, src/proxy.ts |
Serves storage objects by key without changing existing frontend URLs. Generated work media under generated/, gallery/, imported/works, and generated/gallery/work thumbnails is watermarked on the server before display, including object-backed originals, so page display and browser save-as cannot reach raw generated images/videos. Generated image original display requests should use an existing local works.thumbnail_url redirect before watermarking when available; downloads still use the original through /api/download. Thumbnail keys under thumbnails/... are read from local disk and use long immutable browser cache headers because the filename contains the thumbnail profile; src/proxy.ts must preserve those cache headers instead of applying global /api no-store. Non-generated originals can still redirect to short-lived object-storage signed URLs when configured. |
| Download proxy | src/app/api/download/route.ts |
Supports remote URL, same-origin URL, and /api/local-storage/*. Generated local-storage media returns watermarked bytes unless the request authenticates an admin role or a user whose profiles.watermark_disabled is true; normal users without that flag must not receive raw object-storage redirects for generated images/videos. The frontend passes the session through downloadFile(...) headers or triggerDownloadFile(...) download tokens. For non-generated object-backed local-storage files, it can redirect to a short-lived signed object URL with content-disposition instead of buffering large videos through Next.js. |
| Remote fetch guard | src/lib/remote-fetch.ts |
Use for server-side external fetches. It blocks private/local network targets, sends browser-like public-resource headers by default, and exposes fetchPublicHttpUrlWithRetry for generated image/result URL downloads that may transiently return 403, 429, 5xx, or timeout. |
Database And Persistence
| Area | Files |
|---|---|
| Database connection pool | src/storage/database/local-db.ts |
| Image style preset store | src/lib/style-preset-store.ts, src/app/api/style-presets/route.ts |
| Schema snapshot | src/storage/database/shared/schema.ts |
| Supabase compatibility client | src/storage/database/supabase-client.ts |
| Init SQL | scripts/init-database.sql |
| DB patch runner | scripts/apply-database-patch.sh |
| Migration SQL files | account-profile-migration.sql, model-config-migration.sql, persistence_migration.sql, scripts/database-optimization-patch.sql |
Deployment And Runtime
| Feature | Files |
|---|---|
| Custom Node server | src/server.ts |
| PM2 config | ecosystem.config.cjs |
| Build | scripts/build.sh |
| Start | scripts/start.sh |
| Dev | scripts/dev.sh |
| Deploy/upgrade | scripts/deploy-or-upgrade.sh |
| Backup | scripts/backup-create.sh, scripts/backup-list.sh, scripts/backup-restore.sh. Restore uses pg_restore --single-transaction, validates archive/dump contents, atomically swaps local storage, and keeps a pre-restore safety backup. |
| Object storage migration | scripts/storage-sync-to-object.mjs |
| Admin upgrade API/UI | src/app/api/admin/upgrade/route.ts, src/components/admin/system-upgrade-tab.tsx |
| Admin upgrade runner | scripts/admin-upgrade-runner.mjs. Use this when deciding whether a change can be packaged as a hot update or must be a cold update. Source/API/server/dependency/schema/env/runtime/script changes should be treated as cold-update candidates; static/public asset-only packages are hot-update candidates only if runner preflight accepts them without restart. |
| Boundary checks | scripts/check-boundaries.sh |
| User display profile backfill | scripts/backfill-user-display-profile.mjs |