# Dev Agent Memory
## 2026-03-12 Bug Fix Session
### Fixed Bugs
**BUG-001: GenerationCard model name hardcoded**
- File: `src/components/GenerationCard.tsx:54`
- Root cause: Model display was hardcoded as `Seedance 2.0` instead of using `task.model`
- Fix: Changed to `{task.model === 'seedance_2.0' ? 'Seedance 2.0' : 'Seedance 2.0 Fast'}`
**BUG-002: File upload missing size validation**
- Files: `UniversalUpload.tsx`, `InputBar.tsx` (drag-drop), `KeyframeUpload.tsx`
- Root cause: No file size checks anywhere in the upload pipeline
- PRD requirement: images < 20MB, videos < 100MB
- Fix: Added size validation with toast messages in all 3 upload entry points:
- `UniversalUpload.handleFileChange` — filters files before `addReferences`
- `InputBar.handleDrop` — filters after type check, before `addReferences`/`setFirstFrame`
- `KeyframeUpload.handleFirstChange`/`handleLastChange` — checks before `setFirstFrame`/`setLastFrame`
### Previously Fixed Bugs (by prior session)
- canSubmit selector in Toolbar.tsx — was getting function reference instead of calling it
- Drag-drop accepting audio files in InputBar.tsx — now filters to image/video only
### Notes
- Model type values: `'seedance_2.0'` | `'seedance_2.0_fast'` (defined in `src/types/index.ts`)
- Dead code remains: audio type in `UploadedFile.type`, audio icon in `UniversalUpload`/`GenerationCard` — flagged for cleanup but not addressed (out of scope for bug fix)
- Build: `tsc -b && vite build` — passes cleanly
---
## Bug修复 [2026/3/12 14:40:50]
- **触发**: fix_code_bugs
- **耗时**: 123s
- **花费**: $0.0000
- **退出码**: 0
- **工作摘要**: Both CODE_BUGs have been fixed and the build passes cleanly.
---
## 2026-03-12 Phase 2 Implementation Session
### What was done
Implemented the full Phase 2 features as defined in PRD v2.1:
**BUG-001 (audio dead code) — Fixed**
- Removed `.audioThumb` CSS from `UniversalUpload.module.css` and `GenerationCard.module.css`
- TypeScript audio code was already cleaned in a prior session
**Django Backend (`backend/`)**
- Django project with `config/` settings, URLs, WSGI/ASGI
- `apps/accounts/` — Custom User model (extends AbstractUser with daily_limit, monthly_limit), register/login/me API views, JWT auth
- `apps/generation/` — GenerationRecord + QuotaConfig models, video generate API (with quota checking), admin stats/ranking/quota APIs
- SQLite for local dev (USE_MYSQL env var to switch to Aliyun RDS MySQL)
- Django admin configured for both apps
- Migrations created and applied, superuser: admin / admin123
- Virtual environment in `backend/venv/`
**Frontend Auth & Routing**
- `src/lib/api.ts` — Axios instance with JWT interceptor + auto token refresh
- `src/store/auth.ts` — Zustand auth store (login, register, logout, fetchUserInfo, initialize)
- `src/types/index.ts` — Added User, Quota, AuthTokens, AdminStats, UserRanking types
- `src/pages/LoginPage.tsx` — Login form with validation
- `src/pages/RegisterPage.tsx` — Registration form with all PRD validations
- `src/pages/AdminDashboard.tsx` — Stats cards, bar chart trend, user ranking table with inline quota editing
- `src/components/ProtectedRoute.tsx` — Auth guard with `requireAdmin` prop
- `src/components/UserInfoBar.tsx` — Top bar showing username, daily quota, admin link, logout
- `src/App.tsx` — BrowserRouter with routes: `/login`, `/register`, `/`, `/admin/dashboard`
**Backend Integration**
- `src/store/generation.ts` — addTask now calls `videoApi.generate()` and refreshes quota
- `vite.config.ts` — Proxy `/api` to `http://127.0.0.1:8000`
### Technical decisions
- Used unified `ProtectedRoute` with `requireAdmin` prop instead of separate AdminRoute
- Django 6.0 (latest) used instead of 4.2 LTS — API is compatible
- SQLite as default for dev, MySQL for production (via USE_MYSQL env var)
- Frontend API calls are fire-and-forget — the UI still uses mock progress simulation
- UserInfoBar is `position: fixed` at top of VideoGenerationPage
### Build verification
- `npm run build` — passes cleanly (tsc + vite)
- `python manage.py check` — no issues
- `python manage.py migrate` — all migrations applied
### How to run
- Frontend: `npm run dev` (port 5173)
- Backend: `cd backend && source venv/bin/activate && python manage.py runserver` (port 8000)
- Vite proxy forwards `/api/*` to backend
### Known limitations
- mysqlclient not installed in venv (requires MySQL client libs); use SQLite for dev
- Backend video generation is just a record keeper — no actual video generation
- Token refresh in axios interceptor redirects to /login on failure
---
## 代码开发 [2026/3/12 15:11:48]
- **触发**: develop_from_prototype
- **耗时**: 798s
- **花费**: $0.0000
- **退出码**: 0
- **工作摘要**: Phase 2 implementation complete (see above)
---
## 2026-03-12 Phase 2 Verification & Cleanup Session
### What was done
- **BUG-001 (audio dead code) — Fully fixed in source code**:
- `src/types/index.ts`: Changed `'image' | 'video' | 'audio'` → `'image' | 'video'` in both UploadedFile and ReferenceSnapshot
- `src/store/inputBar.ts`: Removed audio classification branch in `addReferences`
- `src/components/UniversalUpload.tsx`: Removed AudioIcon component and audio thumbnail branch
- `src/components/GenerationCard.tsx`: Removed audio thumbnail rendering branch
- **Removed redundant `src/components/auth/` directory** (ProtectedRoute and AdminRoute) — unified ProtectedRoute at `src/components/ProtectedRoute.tsx` with `requireAdmin` prop is used
- **Removed duplicate Q import** in `backend/apps/generation/views.py` (line 163)
- **Downgraded Django to 4.2 LTS** — was 6.0, now 4.2.29 (matches PRD requirement)
- **Verified all endpoints**: register (201), login (200), me with quota (200), video generate with quota tracking (202)
- **Frontend build passes**: `tsc -b && vite build` clean
- **Django check passes**: `python manage.py check` — 0 issues
---
## 代码开发 [2026/3/12 15:18:57]
- **触发**: develop_from_prototype
- **耗时**: 1392s
- **花费**: $0.0000
- **退出码**: 0
- **工作摘要**: All Phase 2 work is complete. Here's a summary: ## Completed ### BUG-001 Fixed (audio dead code) - Removed `'audio'` from type unions in `src/types/index.ts` - Removed audio classification in `src/store/inputBar.ts` - Removed `AudioIcon` and audio rendering branches in `UniversalUpload.tsx` and `GenerationCard.tsx` ### Frontend Phase 2 - **react-router-dom + axios** installed - **Auth store** (`src/store/auth.ts`) — login, register, logout, token refresh, user info - **API service** (`src/lib/ap...
---
## 代码开发 [2026/3/12 15:34:29]
- **触发**: develop_from_prototype
- **耗时**: 195s
- **花费**: $0.0000
- **退出码**: 0
- **工作摘要**: The project is fully implemented with all PRD requirements met. Here's the verification summary: ## Verification Results ### Build & Tests - **Frontend build**: `tsc -b && vite build` passes cleanly (124 modules, 619ms) - **Unit tests**: 151/151 pass (8 test files) - **Django check**: 0 issues ### Phase 1 (Frontend) - All 17 acceptance criteria met - InputBar with dark theme, correct styles (#16161e bg, #2a2a38 border, 20px radius) - Universal/Keyframe mode switching with full linkage - All drop...
---
## 2026-03-12 Bug Fix Session (Round 3)
### Fixed Bugs
**BUG-001: API 拦截器在登录端点 401 响应时错误重定向**
- File: `src/lib/api.ts:22-44`
- Root cause: Axios 401 response interceptor applied to ALL endpoints including `/auth/login`, `/auth/register`, `/auth/token/refresh`. When login credentials were wrong (401), the interceptor would try token refresh and then redirect to `/login`, causing a full page reload that cleared the error state before the user could see the error message.
- Fix: Added `isAuthEndpoint` check — the interceptor now skips token refresh and redirect for auth endpoints (`/auth/login`, `/auth/register`, `/auth/token/refresh`), allowing the 401 error to propagate normally to the calling component which can display the error message.
- Build: `tsc -b && vite build` passes cleanly
---
## Bug修复 [2026/3/12 15:38:53]
- **触发**: fix_code_bugs
- **耗时**: 62s
- **花费**: $0.0000
- **退出码**: 0
- **工作摘要**: **BUG-001 已修复** — `src/lib/api.ts` **问题**: Axios 401 响应拦截器对所有端点无差别处理,包括 `/auth/login` 自身。当用户输入错误密码时,拦截器会尝试 token 刷新,失败后执行 `window.location.href = '/login'` 全页面重定向,导致 React 应用重新加载,错误提示信息被清除。 **修复**: 在 401 拦截器入口添加 `isAuthEndpoint` 检查,排除 `/auth/login`、`/auth/register`、`/auth/token/refresh` 三个认证端点。这些端点的 401 错误会正常 reject 给调用方(LoginPage 等),由组件自行展示错误信息。 构建验证通过,无其他 CODE_BUG。
---
## 2026-03-12 Phase 3 Implementation Session
### What was done
Implemented Phase 3 features as defined in PRD v3.0: **Quota System Refactor + Admin Dashboard Redesign + User Profile Page**
**Backend Changes**
- `accounts/models.py`: Renamed `daily_limit` → `daily_seconds_limit` (default=600), `monthly_limit` → `monthly_seconds_limit` (default=6000)
- `generation/models.py`: Added `seconds_consumed` FloatField to GenerationRecord; QuotaConfig updated with seconds-based defaults + `announcement`/`announcement_enabled` fields
- `accounts/views.py`: me_view returns seconds-based quota using `Sum('seconds_consumed')` instead of `.count()`
- `generation/views.py`: Complete rewrite with all Phase 3 endpoints:
- `video_generate_view`: Seconds-based quota checking
- `admin_stats_view`: Seconds consumed today/month, change percentages, 30-day trend, top 10 users
- `admin_users_list_view`: Paginated with search/status filter, annotated seconds
- `admin_user_detail_view`, `admin_user_quota_view`, `admin_user_status_view`
- `admin_records_view`: Paginated with date range and search
- `admin_settings_view`: GET/PUT for QuotaConfig singleton
- `profile_overview_view`, `profile_records_view`: User's own data
- `generation/urls.py`: 10 URL patterns for all Phase 3 endpoints
- `generation/serializers.py`: QuotaUpdateSerializer, UserStatusSerializer, SystemSettingsSerializer
- Django admin updated for new field names
- Migrations created and applied
**Frontend Changes**
- Installed `echarts` + `echarts-for-react`
- `src/types/index.ts`: All types updated for seconds-based fields (Quota, AdminStats, AdminUser, AdminUserDetail, AdminRecord, SystemSettings, ProfileOverview, PaginatedResponse)
- `src/lib/api.ts`: Full Phase 3 API client (adminApi with 8 methods, profileApi with 2 methods)
- `src/index.css`: Phase 3 design tokens (sidebar, card, success/danger/warning colors)
- `src/components/UserInfoBar.tsx`: Shows seconds remaining, added "个人中心" button
- `src/store/generation.ts`: Updated for seconds-based API response
- `src/App.tsx`: Phase 3 routing with nested admin routes via Outlet
**New Pages (all with CSS modules)**
- `AdminLayout.tsx` — Sidebar + Outlet layout with collapsible nav, 4 menu items
- `DashboardPage.tsx` — Stats cards, ECharts line chart (30-day trend), ECharts horizontal bar chart (top users), mock data for dev
- `UsersPage.tsx` — User table with search/filter, pagination, quota edit modal, user detail drawer
- `RecordsPage.tsx` — Records table with date range + username search, CSV export (with injection prevention)
- `SettingsPage.tsx` — Quota config form + announcement management with toggle
- `ProfilePage.tsx` — ECharts gauge for daily quota, progress bars, sparkline with 7d/30d toggle, consumption records list
**Removed**
- Old `AdminDashboard.tsx` + `AdminDashboard.module.css` (Phase 2 single-page admin replaced by multi-page)
### Technical decisions
- Used `echarts/core` tree-shaking (import only needed chart types) — `EChartsCoreOption` type, not `EChartsOption`
- AdminLayout uses React Router nested routes with `` for clean code splitting
- ProfilePage has `height: 100vh; overflow-y: auto` to handle scrolling within `#root { overflow: hidden }`
- Mock data generation in DashboardPage when backend returns empty data — ensures charts always display
- CSV export escapes formulas starting with `=`, `+`, `-`, `@` for CSV injection prevention
### Build verification
- `npm run build` — passes cleanly (741 modules, 2.16s)
- `python manage.py check` — 0 issues
- All migrations applied
---
## 代码开发 [2026/3/12 17:26:11]
- **触发**: develop_from_prototype
- **耗时**: 1178s
- **花费**: $0.0000
- **退出码**: 0
- **工作摘要**: Phase 3 implementation is complete. Here's a summary of everything done: **Backend (Django)** - Quota system refactored from call-count to seconds-based (`daily_seconds_limit=600`, `monthly_seconds_limit=6000`) - Added `seconds_consumed` field to GenerationRecord model - QuotaConfig updated with announcement fields - 10 new API endpoints covering admin stats/users/records/settings and user profile - Migrations created and applied, `manage.py check` passes **Frontend (React + TypeScript)** - Inst...
---
## 2026-03-13 Phase 4: TOS 存储 + Seedance API 集成(手动开发)
### What was done
实现了完整的火山引擎 TOS 对象存储 + Seedance 视频生成 API 集成,替换原有的 mock 进度模拟。
**新增文件**
- `backend/utils/tos_client.py` — 火山引擎 TOS (S3 兼容) 文件上传工具,使用 boto3
- `backend/utils/seedance_client.py` — Seedance/ARK API 客户端(创建任务、查询任务、提取视频URL、状态映射)
- `backend/apps/generation/migrations/0003_generationrecord_ark_task_id_and_more.py` — 新增字段迁移
**Backend Changes**
- `requirements.txt`: 新增 `boto3>=1.34`, `requests>=2.31`
- `config/settings.py`: 新增 TOS 配置(AK/SK/Bucket/Endpoint/CDN域名)+ ARK API 配置(API Key/Base URL)
- `apps/generation/models.py`: GenerationRecord 新增 4 个字段:
- `ark_task_id` CharField — 火山 ARK 任务 ID(如 `cgt-2026xxxx`)
- `result_url` CharField(1000) — 生成的视频结果 URL
- `error_message` TextField — 错误信息
- `reference_urls` JSONField — 参考素材信息(URL、类型、角色、标签)
- `apps/generation/serializers.py`: VideoGenerateSerializer 新增 `references` ListField
- `apps/generation/views.py`: 完全重写,新增 3 个视图:
- `upload_media_view` POST /media/upload — 上传文件到 TOS,返回公网 URL
- `video_tasks_list_view` GET /video/tasks — 用户最近任务列表(页面刷新持久化)
- `video_task_detail_view` GET /video/tasks/ — 单个任务状态(如仍在处理中则轮询 Seedance API)
- `video_generate_view` 改写 — 接收 JSON(含 TOS URLs)→ 创建 Seedance 任务 → 存储 ark_task_id
- `apps/generation/urls.py`: 新增 3 条路由
**Frontend Changes**
- `src/types/index.ts`:
- `UploadedFile` 新增 `tosUrl` 字段
- `ReferenceSnapshot` 新增 `role` 字段
- `GenerationTask` 新增 `taskId`(后端 UUID)、`errorMessage` 字段
- 新增 `BackendTask` 接口(后端返回的任务数据结构)
- `src/lib/api.ts`:
- 新增 `mediaApi.upload()` — 上传文件到后端 → TOS
- `videoApi.generate()` 改为发送 JSON(不再是 FormData)
- 新增 `videoApi.getTasks()` — 获取用户任务列表
- 新增 `videoApi.getTaskStatus()` — 获取单个任务状态
- `src/store/generation.ts`: **完全重写** — 核心变更:
- 删除 `simulateProgress()` mock 进度模拟
- `addTask()` 改为 async: 上传文件 → TOS → 调用生成 API → 轮询状态
- 新增 `loadTasks()`: 从后端加载任务列表(页面刷新后恢复)
- 新增 `startPolling()` / `stopPolling()`: 每 5 秒轮询 Seedance 任务状态
- `regenerate()`: 复用已有 TOS URL 避免重复上传
- 使用 `Map` 管理轮询计时器
- `src/components/VideoGenerationPage.tsx`: 挂载时调用 `loadTasks()` 恢复历史任务
- `src/components/GenerationCard.tsx`: 结果区域从 `
` 改为 `