zyc 566c3a476f
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m17s
add 存储桶
2026-03-13 15:38:08 +08:00

311 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 `<Outlet />` 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/<uuid> — 单个任务状态(如仍在处理中则轮询 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<string, Timer>` 管理轮询计时器
- `src/components/VideoGenerationPage.tsx`: 挂载时调用 `loadTasks()` 恢复历史任务
- `src/components/GenerationCard.tsx`: 结果区域从 `<img>` 改为 `<video controls>`;新增失败状态显示
### 完整数据流
```
用户上传文件 → POST /media/upload → TOS 存储桶 → 返回公网 URL
用户生成视频 → POST /video/generate (JSON + URLs) → Seedance API 创建任务
前端轮询 → GET /video/tasks/{task_id} → 后端查询 Seedance API → 更新 DB → 返回状态
任务完成 → result_url 存入 DB → 前端显示 <video> 播放器
刷新页面 → GET /video/tasks → 从 DB 加载所有记录 → 恢复展示
```
### TOS 配置
- Bucket: `video-huoshan`
- Region: `cn-guangzhou`
- 外网 Endpoint: `tos-cn-guangzhou.volces.com`
- S3 兼容 Endpoint: `tos-s3-cn-guangzhou.volces.com`
- CDN 域名: `video-huoshan.tos-cn-guangzhou.volces.com`
### Seedance Model ID 映射
- `seedance_2.0``doubao-seedance-2-0-260128`
- `seedance_2.0_fast``doubao-seedance-2-0-fast-260128`
### 关键技术决策
- TOS 使用 S3 兼容协议boto3而非火山原生 SDK — 更通用,依赖更少
- 文件上传和视频生成分两步:先上传到 TOS 拿 URL再用 URL 调 Seedance API
- 前端轮询而非后端轮询(无需 Celery 等任务队列)
- Seedance 结果 URL 24 小时有效(暂未实现下载到 TOS 持久化)
- 如果 ARK_API_KEY 未设置,生成接口仍创建 DB 记录但跳过 Seedance API 调用
### 需要用户操作
1. 设置 `ARK_API_KEY` 环境变量Seedance API 密钥)
2. TOS Bucket 设置公共读Seedance API 需要访问上传的文件)
3. 配置 CORS 允许前端域名
### Build verification
- TypeScript: `tsc --noEmit` 0 errors
- Backend: `python -c "import django; ..."` 全部导入成功
- Migration: 已创建并应用 (0003)