# Bug Location Guide

Last source audit: 2026-05-12, based on git commit `8ee86a9`.

Use this guide when the user reports behavior. Start from the symptom row, inspect the listed files, then verify with the current runtime.

## First Five Checks

1. Confirm whether the bug is frontend-only, API-only, persistence, generation worker, or deployment/runtime.
2. Read the task row below and open the smallest likely file set.
3. Search for the exact UI text, endpoint path, DB table, or log prefix with `rg`.
4. Check auth state and request payload before changing business logic.
5. After a fix, run the narrowest useful verification first, then `pnpm run ts-check` and `pnpm run build` when source changes affect TypeScript/runtime.

## Auth, Login, Profile

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| User cannot log in | `src/app/auth/login/page.tsx`, `src/lib/auth-store.ts`, `src/app/api/auth/login/route.ts`, `src/lib/session-auth.ts` | Request body fields (`account`, `email`, `phone`, `password`), password hash verification, bearer token storage, inactive profile. |
| Admin console opens without login or redirects incorrectly | `src/app/console/page.tsx`, `src/modules/console/pages/console-login-page.tsx`, `src/modules/console/pages/console-dashboard-page.tsx`, `src/app/api/auth/login/route.ts` | `adminOnly` behavior, admin role check, route redirect logic. |
| Registration fails or registration verification email arrives blank | `src/app/auth/register/page.tsx`, `src/app/api/auth/register/route.ts`, `src/app/api/email/send-register-code/route.ts`, `src/lib/email-service.ts` | `acceptedTerms`, email code, password strength, invite code, duplicate profile, SMTP settings, send logs, and MIME body encoding. Verification emails use HTML plus plain-text multipart content; base64 bodies must be folded to 76-character lines and multipart blank separators must be preserved. Do not use `.filter(Boolean)` on MIME message arrays because it removes required empty separator lines and can make mailbox clients render a blank email despite SMTP accepting it. |
| Profile changes disappear after refresh | `src/app/profile/page.tsx`, `src/app/api/profile/route.ts`, `src/lib/auth-store.ts` | PUT writes both `profiles` and `auth.users` where needed; client refreshes returned profile. |
| Navbar or gallery shows login username instead of public nickname | `src/lib/auth-store.ts`, `src/app/api/profile/route.ts`, `src/app/api/gallery/route.ts`, `src/lib/user-profile-defaults.ts` | `profiles.nickname` is login username; public UI should use returned `nickname` from `profiles.display_nickname`. Gallery SQL should select `display_nickname` first. |
| Navbar user avatar is missing and only shows an initial | `src/components/navbar.tsx`, `src/lib/auth-store.ts`, `src/app/api/profile/route.ts` | The navbar user button should read `AuthUser.avatarUrl`; confirm `/api/profile` or login returns `avatar_url`, `parseApiUser` maps it, and `UserAvatar` only falls back to initials after image load failure. |
| Existing users have blank/default avatar after display-profile migration | `src/lib/user-profile-defaults.ts`, `scripts/backfill-user-display-profile.mjs`, `src/app/api/auth/login/route.ts` | Run the backfill script with `LOCAL_DB_URL`; login also lazily fills missing `avatar_url` with a generated SVG data URL. |
| Theme does not persist | `src/components/account-theme-sync.tsx`, `src/app/api/profile/theme/route.ts`, `src/lib/profile-preferences.ts` | `preferred_theme` schema, token auth, theme normalization. |

## Site Config, Footer, Policies, Announcements

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| Footer content missing or not Markdown-rendered | `src/components/site-footer.tsx`, `src/components/site-policy-page.tsx`, `src/lib/site-config.ts`, `src/app/api/site-config/route.ts` | Config response fields, Markdown renderer, fallback defaults, PUT persistence. |
| Policy pages start mid-page after navigation | `src/components/site-policy-page.tsx`, `src/app/about/page.tsx`, `src/app/terms/page.tsx`, `src/app/privacy/page.tsx`, `src/app/help/page.tsx` | Scroll reset behavior and shared policy page wrapper. |
| Site name/logo/favicon not updating | `src/components/site-config-sync.tsx`, `src/components/site-brand.tsx`, `src/app/api/site-config/route.ts`, `src/lib/local-storage.ts` | `site_config` row, base64 image save, generated `/api/local-storage/*` URL. |
| Page content leaves large unused horizontal margins, or wide screens look like the UI was simply enlarged | `src/components/app-shell.tsx`, `src/components/navbar.tsx`, `src/components/site-footer.tsx`, page-level wrappers under `src/app/*/page.tsx`, `src/components/site-policy-page.tsx` | The viewport/background can be `w-full`, but product content should keep the original component scale and readable containers such as `max-w-7xl`, `max-w-4xl`, or `max-w-3xl`. Do not fix this by removing all max widths or scaling controls up on wide monitors. |
| Scrollbars look native, stay visible when idle, or do not match glass UI in dialogs/pages | `src/app/globals.css`, `src/components/app-shell.tsx` | Global scrollbar styling is hidden by default and becomes visible only while wheel/touch scrolling through the `scrollbars-visible` class on `<html>`. `globals.css` owns both the hidden state and the rounded glass visible state for light/dark themes; `app-shell` owns the short-lived wheel/touch listener. Avoid adding one-off scrollbar styles to individual components unless there is a real exception. |
| Disabled canvas/`画布` appears again in public UI | `src/components/navbar.tsx`, `src/app/canvas/page.tsx`, `docs/codex-miaojing/feature-code-index.md` | Navbar should not include `/canvas`, and `/canvas` should continue to call `notFound()` unless the product explicitly re-enables the legacy canvas feature. |
| Announcement not popping up | `src/components/announcement-popup.tsx`, `src/app/api/announcements/route.ts`, `src/components/app-shell.tsx` | App shell includes popup, active date range, local/session dismissal behavior, GET payload shape. |
| Announcement admin edit fails | `src/components/admin/announcement-tab.tsx`, `src/app/api/announcements/route.ts` | Admin token, required fields, `starts_at`/`expires_at` compatibility. |
| Third-party platform iframe shows `miaojing.toplee.cn refused to connect` | `src/proxy.ts`, `.env.example`, reverse-proxy response headers | Check `Content-Security-Policy frame-ancestors` and `X-Frame-Options`. External iframe embedding requires the parent origin in `MIAOJING_FRAME_ANCESTORS` or the default mozheAPI allowlist, and `X-Frame-Options` must not be sent when third-party ancestors are allowed. Also verify the outer nginx/CDN is not adding its own stricter frame headers. |

## Creation And Generation

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| Create button does nothing | `src/components/create/text-to-image.tsx`, `src/components/create/image-to-image.tsx`, `src/components/create/text-to-video.tsx`, `src/components/create/image-to-video.tsx`, `src/lib/generation-job-client.ts` | Client validation, auth token, `/api/generation-jobs` POST response, UI disabled/loading state. |
| Refreshing `/create` resets to the wrong creation tab | `src/app/create/page.tsx` | Active tab should persist in `miaojing:create-active-tab` and mirror to `/create?type=...`. Verify all creation tabs (`text2img`, `img2img`, `text2video`, `img2video`, `reversePrompt`) restore after refresh and query-param links still override storage. |
| Cannot submit a new generation job while another job is running, or active job cards overflow horizontally | `src/components/create/text-to-image.tsx`, `src/components/create/image-to-image.tsx`, `src/components/create/text-to-video.tsx`, `src/components/create/image-to-video.tsx`, `src/components/create/generation-task-list.tsx` | Create panels should keep the submit button enabled while models are available; active job cards should render inside the results column with wrapping vertical growth, not outside the result area. |
| Job remains queued | `src/app/api/generation-jobs/route.ts`, `src/lib/generation-job-worker.ts`, `src/lib/generation-job-runner.ts` | `processNextGenerationJob()` invoked, stale job handling, DB locks/status, internal base URL. |
| Job remains running forever | `src/app/api/generation-jobs/[id]/route.ts`, `src/lib/generation-job-worker.ts`, `src/lib/generation-job-estimates.ts` | Stale timeout updates, `updated_at`, worker exceptions swallowed into error field. |
| Image generation returns upstream error | `src/app/api/generate/image/route.ts`, `src/lib/custom-api-fetch.ts`, `src/lib/server-api-config.ts` | Resolved custom/system API credentials, endpoint URL, New API normalization, timeout, stream/progress parser. |
| Video generation returns upstream error | `src/app/api/generate/video/route.ts`, `src/lib/custom-api-fetch.ts`, `src/lib/server-api-config.ts` | Reference image upload/compression, endpoint URL, response parser, persistence timeout. |
| Wrong image size or aspect ratio | `src/lib/model-config.ts`, `src/app/api/generate/image/route.ts` | `resolveImageSize`, `resolveCustomApiImageSize`, New API/DALL-E size normalization, prompt aspect hint. |
| Reference image upload too large or fails | `src/components/create/image-to-image.tsx`, `src/components/create/image-to-video.tsx`, `src/lib/browser-image-compression.ts`, `src/lib/server-image-compression.ts`, `src/app/api/generate/image/route.ts`, `src/app/api/generate/video/route.ts` | Browser compression, `MAX_UPSTREAM_REFERENCE_IMAGE_BYTES`, data URL conversion. Uploaded reference thumbnails should single-click into the no-container `BareImagePreview`; blank area closes it. |
| Generated result previews but does not persist | `src/app/api/generate/image/route.ts`, `src/app/api/generate/video/route.ts`, `src/lib/local-storage.ts`, `src/app/api/creation-history/route.ts` | Media copied to local storage, presigned URL returned, history POST called. |
| Fullscreen/preview/download/right-click image actions broken | `src/components/fullscreen-preview.tsx`, `src/components/lightbox.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/app/api/download/route.ts` | Dialog state, URL type, download proxy supports local/remote URL. Image result and history/detail previews should open on single click. Right-click copy, download, edit, and share actions must use the uncompressed original image URL, not a thumbnail, preview cache, or compressed reference blob. Share links should open `/image-viewer?url=...` as a standalone original-image fullscreen page. Image result and history/detail previews should show upper-right actual aspect ratio and natural resolution via `ImageMetadataBadge`. |
| Creation detail, gallery one-click reuse, or inspiration reuse buttons do not fill create forms or switch tabs | `src/components/creation-detail-dialog.tsx`, `src/app/gallery/page.tsx`, `src/components/create/inspiration-gallery-dialog.tsx`, `src/lib/creation-reuse.ts`, `src/app/create/page.tsx`, `src/components/create/text-to-image.tsx`, `src/components/create/image-to-image.tsx`, `src/components/create/text-to-video.tsx`, `src/components/create/image-to-video.tsx` | Reuse actions should write the shared draft key/event for `text2img`, `img2img`, `text2video`, or `img2video`, route to the matching `/create?type=...` when leaving gallery, and already-mounted create panels should react to the event. Image-to-image and image-to-video reuse should include reference images from the work, falling back to the output image or thumbnail only when stored references are missing. |
| Image generation count dropdown too wide, options missing, or manual count input unavailable | `src/components/create/image-count-combobox.tsx`, `src/components/create/text-to-image.tsx`, `src/components/create/image-to-image.tsx` | Use the shared compact combobox instead of browser `datalist`; verify manual numeric entry and dropdown options in both text-to-image and image-to-image panels. |
| Style presets are hardcoded, missing, or not ordered by usage | `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/app/api/generation-jobs/route.ts` | Presets should come from `image_style_presets`; `generation-jobs` increments `usage_count`; GET `/api/style-presets` should return active presets sorted by usage count. |
| Reverse prompt option missing | `src/components/create/reverse-prompt-panel.tsx`, `src/app/api/generate/reverse-prompt/route.ts` | UI option list and server `outputMode` handling both updated, app rebuilt/restarted if deployed. |
| Prompt optimization fails | `src/app/api/generate/suggest-prompt/route.ts`, `src/lib/server-api-config.ts`, `src/lib/custom-api-fetch.ts` | Text-capable system/custom API, chat response shape, JSON parsing fallback. |

## Models, Providers, API Keys

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| Model list empty in create/profile | `src/app/api/model-config/route.ts`, `src/lib/model-config.ts`, `src/lib/managed-model-store.ts`, `src/lib/custom-api-store.ts` | Public model config response, admin recommendations, local client store mapping. |
| System API saved but not used | `src/app/api/admin/system-apis/route.ts`, `src/lib/server-api-config.ts`, `src/app/api/generate/image/route.ts`, `src/app/api/generate/video/route.ts` | `systemApiId` in request payload, active config, decrypted key, type matches image/video/text. |
| User custom API saved but not used | `src/app/api/user-api-keys/route.ts`, `src/lib/custom-api-store.ts`, `src/lib/server-api-config.ts` | `customApiKeyId`, owner auth, encrypted key exists, `is_active`. |
| New API image endpoint incompatible | `src/app/api/generate/image/route.ts`, `src/lib/custom-api-fetch.ts` | Provider is `newapi`/`new api`, endpoint normalization, model-specific size/count/quality handling. |
| API key leaked in UI/API | `src/app/api/user-api-keys/route.ts`, `src/app/api/admin/system-apis/route.ts`, `src/lib/server-crypto.ts`, `src/lib/server-api-config.ts` | Response mapping must return preview/empty key only. |

## Gallery And Creation History

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| History missing after generation or login/account switch | `src/lib/creation-history-store.ts`, `src/app/api/creation-history/route.ts`, create panel component | History POST, `works` insert, URL not data URL except reverse prompt placeholder, and `miaojing_auth_updated` triggers a fresh server fetch. |
| Detail delete removes only local history, skips confirmation, or record reappears after refresh | `src/components/creation-detail-dialog.tsx`, `src/components/ui/alert-dialog.tsx`, `src/lib/creation-history-store.ts`, `src/app/api/creation-history/route.ts`, `src/components/profile/creation-history-tab.tsx` | The detail action is labeled `删除作品` and must open a confirmation dialog warning that deletion cannot be recovered. Logged-in deletion should call `DELETE /api/creation-history?id=...` first, then refresh local history from the server. Check bearer token availability and route ownership filter (`id` + `user_id`). |
| Published work not in gallery | `src/lib/creation-history-store.ts`, `src/app/api/gallery/publish/route.ts`, `src/app/api/gallery/route.ts`, `src/app/gallery/page.tsx` | `is_public = true`, `status = completed`, media copied to gallery folder, filters. |
| Imported gallery images do not render after production data import | `src/app/api/admin/data-export/route.ts`, `src/app/api/admin/data-import/route.ts`, `src/lib/local-storage.ts`, `src/app/api/local-storage/[...path]/route.ts`, DB `works.result_url` | New exports should include `_media`; import should persist media to local storage. If using an older export without `_media`, DB rows alone cannot recreate missing `/api/local-storage/*` files. |
| Gallery delete does not remove public item | `src/app/api/gallery/route.ts`, admin UI route using it | DELETE unpublishes by setting `is_public = false`, not hard delete. |
| Search/filter/sort wrong | `src/app/api/gallery/route.ts`, `src/app/gallery/page.tsx` | Query params `type`, `limit`, `offset`, `sort`, `q/search`; SQL where/order. |
| Gallery search box looks inconsistent with the rest of the UI | `src/app/gallery/page.tsx` | The search field is a custom glass panel with an inner focused input surface; avoid reverting it to a plain transparent input row. |
| Gallery hover makes images muddy, covers the image with prompt text, shows only a single-color/static glow, has transparent gaps, does not match image colors, misses the card corners, moves too fast, looks too hard-edged, or action buttons disappear on dark/light images | `src/app/gallery/page.tsx`, `src/app/globals.css` | Gallery cards should not use a full-image dark hover overlay, center prompt text, transparent border gaps, generated unrelated colors, broad square glow under the card, or a separate outer halo layer. Keep hover feedback on the card container with scale plus a real `gallery-card-border-frame` wrapper using 3-5 sampled image colors in a single blurred 3px continuous clockwise border around the full work-card container, including all four corners and the prompt/footer area, and keep like/download buttons legible through sampled image brightness inversion. |

## Canvas

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| Canvas projects not listed | `src/app/canvas/page.tsx`, `src/components/canvas/infinite-canvas-workspace.tsx`, `src/app/api/canvas/projects/route.ts`, `src/lib/canvas-store.ts` | Legacy-only while `/canvas` is disabled. First confirm the product intentionally re-enabled canvas; otherwise keep the route hidden/404. |
| Canvas save drops nodes/groups/connections | `src/lib/canvas-store.ts`, `src/lib/canvas-types.ts`, `src/components/canvas/infinite-canvas-workspace.tsx` | `normalizeCanvasState` preserves all state fields and types. |
| Connection lines missing/duplicated/ugly | `src/components/canvas/react-flow-canvas.tsx`, `src/lib/canvas-types.ts` | Single render source, no duplicate React Flow edges plus overlay lines unless intentional. |
| Canvas image proportions wrong | `src/lib/canvas-types.ts`, `src/components/canvas/react-flow-canvas.tsx`, `src/components/canvas/infinite-canvas-workspace.tsx` | Stored image dimensions and render sizing. |

## Admin Console And Ops

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| Dashboard stats wrong | `src/modules/console/pages/console-dashboard-page.tsx`, `src/app/api/admin/dashboard/route.ts`, `src/app/api/admin/stats/route.ts` | SQL source tables, safe query fallbacks, admin auth. |
| User management bug | `src/components/admin/user-management-tab.tsx`, `src/app/api/admin/users/route.ts` | Role/tier mapping, active flag, admin auth. |
| Order/payment bug | `src/components/admin/order-management-tab.tsx`, `src/components/admin/payment-tab.tsx`, `src/app/api/admin/orders/route.ts`, `src/app/api/admin/payment-methods/route.ts`, `src/lib/server-payment-config.ts` | Payment config encryption, order status, request shape. |
| Data import/export bug | `src/components/admin/data-management-tab.tsx`, `src/app/api/admin/data-export/route.ts`, `src/app/api/admin/data-import/route.ts` | JSON format, table coverage, admin auth, `_media` coverage, import transaction/savepoints, work dedupe by URL/source URL/media SHA. |
| Admin email send/settings bug, test email blank, or user notification email blank | `src/components/admin/settings-tab.tsx`, `src/app/api/admin/email-settings/route.ts`, `src/app/api/admin/send-email/route.ts`, `src/lib/email-service.ts` | SMTP config, template rendering, send logs, MIME multipart assembly, and body encoding. Keep both text/plain and text/html parts non-empty, preserve required MIME blank separator lines, and fold base64 body lines to MIME-safe lengths before the SMTP DATA terminator. |
| Upgrade page stuck/fails | `src/components/admin/system-upgrade-tab.tsx`, `src/app/api/admin/upgrade/route.ts`, `scripts/admin-upgrade-runner.mjs`, `scripts/backup-create.sh`, `scripts/backup-restore.sh` | State dir, package limits, disk checks, backup validation, restore safety backup, PM2 restart command, stale status. |
| Platform logs missing | `src/components/admin/log-management-tab.tsx`, `src/lib/platform-logs.ts`, routes that call `writePlatformLog` | Runtime schema exists, log retention, API calls actually write logs. |

## Storage, Download, Files

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| `/api/local-storage/...` 404 | `src/app/api/local-storage/[...path]/route.ts`, `src/lib/local-storage.ts` | `LOCAL_STORAGE_DIR`, normalized key, file exists on disk. |
| Download says remote fetch failed | `src/app/api/download/route.ts`, `src/lib/remote-fetch.ts` | URL is http(s), same-origin, or local-storage; upstream reachable; timeout. |
| Path traversal/security concern | `src/lib/local-storage.ts`, `src/app/api/download/route.ts`, `src/app/api/local-storage/[...path]/route.ts` | Keep `normalizeKey`, `path.resolve`, and `..` guards. |

## Deployment And Build

| Symptom | Check Files | What To Verify |
| --- | --- | --- |
| Build or type check fails | `package.json`, `scripts/build.sh`, `next.config.ts`, failing source file, `CODEX_MIAOJING_MEMORY.md` | First run `corepack pnpm install --frozen-lockfile` if dependency modules are missing. Current audited baseline already recorded failures for missing `@/lib/model-display` and canvas type errors. Distinguish pre-existing source errors from your docs/change. |
| PM2 app not updated | `ecosystem.config.cjs`, `scripts/start.sh`, `scripts/deploy-or-upgrade.sh` | Process cwd, role ports, environment variables, `pm2 startOrReload ecosystem.config.cjs --update-env`. |
| Production uses different checkout | `ecosystem.config.cjs`, PM2 process env/cwd | Always verify PM2 cwd before editing production. |
| Production returns 502 after source sync or PM2 reload | `ecosystem.config.cjs`, `scripts/start.sh`, PM2 env, nginx upstream ports | Check whether production `ecosystem.config.cjs` was overwritten by a repo/dev copy. Live production on 2026-05-14 used `/opt/miaojingAI`, Node `/data/miaojingAI/node/node-v24.15.0-linux-x64/bin`, and ports `8000/8100/8200`; a repo copy pointing at `/root/miaojingAI` and `5000/5100/5200` breaks nginx until the production config is restored and PM2 reloaded. |
| Upgrade package cleanup failed | `scripts/deploy-or-upgrade.sh`, `scripts/admin-upgrade-runner.mjs`, `src/app/api/admin/upgrade/route.ts` | Cleanup trap, backup paths, state dir, disk space guards. |
| Unsure whether a change is safe for hot update or needs cold update | `scripts/admin-upgrade-runner.mjs`, `src/app/api/admin/upgrade/route.ts`, `src/components/admin/system-upgrade-tab.tsx`, `docs/codex-miaojing/architecture.md` | Treat source/API/server/dependency/schema/env/runtime/script changes as cold-update candidates. Hot updates should be static/public asset-only and must pass runner preflight without restart. Verify backup, rollback, package limits, disk checks, PM2 restart expectations, and `/api/health` before marking deploy-facing work complete. |

## Useful Search Patterns

```bash
rg -n "export async function (GET|POST|PUT|DELETE)" src/app/api
rg -n "writePlatformLog|console\\.error|console\\.warn" src
rg -n "generation_jobs|works|profiles|site_config|system_api_configs" src scripts *.sql
rg -n "localStorage|/api/local-storage|/api/download" src
rg -n "requireAdmin|getAuthenticatedUser|getAuthenticatedUserId" src/app/api src/lib
```
