mirror of
https://github.com/handsomezhuzhu/QQuiz.git
synced 2026-04-18 14:32:54 +00:00
refactor: remove legacy frontend code and implement new Next.js structure
- Deleted the old Register page and utility functions. - Removed Tailwind CSS configuration and Vite configuration files. - Added a new script for starting a single container with FastAPI and Next.js. - Updated README to reflect the current status of the Next.js frontend. - Implemented new login and registration API routes with improved error handling. - Refactored frontend API calls to use the new proxy structure. - Enhanced error handling in API response processing. - Updated components to align with the new API endpoints and structure.
This commit is contained in:
@@ -1,262 +1,140 @@
|
||||
# MySQL 安装与配置指南
|
||||
# MySQL 可选配置指南
|
||||
|
||||
QQuiz 使用 MySQL 8.0 作为数据库,你可以选择 Docker 部署或本地安装。
|
||||
QQuiz 默认部署路径是单容器 + SQLite。README、根目录 `Dockerfile`、`docker-compose-single.yml` 和 GitHub Actions 发布镜像都围绕这个模式设计。
|
||||
|
||||
## 方式一:使用 Docker (推荐)
|
||||
只有在你明确需要把数据库独立出去时,才需要 MySQL。常见原因:
|
||||
|
||||
### 优点
|
||||
- 无需手动安装 MySQL
|
||||
- 自动配置和初始化
|
||||
- 隔离环境,不影响系统
|
||||
- 需要多个应用实例共享同一数据库
|
||||
- 已有 MySQL 运维体系
|
||||
- 希望把应用容器和数据库生命周期分开
|
||||
|
||||
### 使用步骤
|
||||
## 场景一:源码部署时附加 MySQL 容器
|
||||
|
||||
1. **安装 Docker Desktop**
|
||||
- 下载地址:https://www.docker.com/products/docker-desktop/
|
||||
- 安装后启动 Docker Desktop
|
||||
这是当前最直接的 MySQL 用法,适合你已经克隆仓库并接受“应用容器 + MySQL 容器”的可选部署方式。
|
||||
|
||||
2. **运行启动脚本**
|
||||
```bash
|
||||
scripts\fix_and_start.bat
|
||||
```
|
||||
选择 **[1] Use Docker**
|
||||
|
||||
3. **完成!**
|
||||
- Docker 会自动下载 MySQL 镜像
|
||||
- 自动创建数据库和用户
|
||||
- 自动启动服务
|
||||
|
||||
---
|
||||
|
||||
## 方式二:本地安装 MySQL
|
||||
|
||||
### 下载 MySQL
|
||||
|
||||
1. 访问 MySQL 官网下载页面:
|
||||
https://dev.mysql.com/downloads/installer/
|
||||
|
||||
2. 选择 **MySQL Installer for Windows**
|
||||
|
||||
3. 下载 `mysql-installer-community-8.0.x.x.msi`
|
||||
|
||||
### 安装步骤
|
||||
|
||||
1. **运行安装程序**
|
||||
- 双击下载的 .msi 文件
|
||||
|
||||
2. **选择安装类型**
|
||||
- 选择 "Developer Default" 或 "Server only"
|
||||
- 点击 Next
|
||||
|
||||
3. **配置 MySQL Server**
|
||||
- **Config Type**: Development Computer
|
||||
- **Port**: 3306 (默认)
|
||||
- **Authentication Method**: 选择 "Use Strong Password Encryption"
|
||||
|
||||
4. **设置 Root 密码**
|
||||
- 输入并记住 root 用户的密码
|
||||
- 建议密码:`root` (开发环境)
|
||||
|
||||
5. **Windows Service 配置**
|
||||
- ✅ Configure MySQL Server as a Windows Service
|
||||
- Service Name: MySQL80
|
||||
- ✅ Start the MySQL Server at System Startup
|
||||
|
||||
6. **完成安装**
|
||||
- 点击 Execute 开始安装
|
||||
- 等待安装完成
|
||||
- 点击 Finish
|
||||
|
||||
### 验证安装
|
||||
|
||||
打开命令提示符,运行:
|
||||
1. 复制环境变量模板:
|
||||
|
||||
```bash
|
||||
mysql --version
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
应该显示:`mysql Ver 8.0.x for Win64 on x86_64`
|
||||
Windows PowerShell:
|
||||
|
||||
### 配置 QQuiz 数据库
|
||||
|
||||
**方式 A:使用脚本自动创建 (推荐)**
|
||||
|
||||
运行:
|
||||
```bash
|
||||
scripts\fix_and_start.bat
|
||||
```
|
||||
选择 **[2] Use Local MySQL**
|
||||
|
||||
**方式 B:手动创建**
|
||||
|
||||
1. 打开 MySQL 命令行客户端:
|
||||
```bash
|
||||
mysql -u root -p
|
||||
```
|
||||
|
||||
2. 输入 root 密码
|
||||
|
||||
3. 创建数据库和用户:
|
||||
```sql
|
||||
CREATE DATABASE qquiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE USER 'qquiz'@'localhost' IDENTIFIED BY 'qquiz_password';
|
||||
GRANT ALL PRIVILEGES ON qquiz_db.* TO 'qquiz'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据库配置说明
|
||||
|
||||
### .env 文件配置
|
||||
|
||||
确保 `.env` 文件中的数据库连接字符串正确:
|
||||
|
||||
**本地 MySQL:**
|
||||
```env
|
||||
DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@localhost:3306/qquiz_db
|
||||
```powershell
|
||||
Copy-Item .env.example .env
|
||||
```
|
||||
|
||||
**Docker MySQL:**
|
||||
2. 把 `.env` 中的数据库连接改成 MySQL 容器地址:
|
||||
|
||||
```env
|
||||
DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@mysql:3306/qquiz_db
|
||||
```
|
||||
|
||||
### 连接参数说明
|
||||
3. 启动应用和 MySQL:
|
||||
|
||||
- `mysql+aiomysql://` - 使用 aiomysql 异步驱动
|
||||
- `qquiz` - 数据库用户名
|
||||
- `qquiz_password` - 数据库密码
|
||||
- `localhost` 或 `mysql` - 数据库主机地址
|
||||
- `3306` - MySQL 默认端口
|
||||
- `qquiz_db` - 数据库名称
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 端口 3306 被占用
|
||||
|
||||
**错误信息:**
|
||||
```
|
||||
Error: Port 3306 is already in use
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.mysql.yml up -d --build
|
||||
```
|
||||
|
||||
**解决方案:**
|
||||
- 检查是否已经有 MySQL 运行:`netstat -ano | findstr :3306`
|
||||
- 停止现有的 MySQL 服务
|
||||
- 或修改 `.env` 中的端口号
|
||||
4. 访问:
|
||||
|
||||
### 2. 无法连接到 MySQL
|
||||
- 前端:`http://localhost:3000`
|
||||
- 后端:`http://localhost:8000`
|
||||
|
||||
**错误信息:**
|
||||
```
|
||||
Can't connect to MySQL server on 'localhost'
|
||||
```
|
||||
说明:
|
||||
|
||||
**解决方案:**
|
||||
- 这条路径是 MySQL 兼容部署,不是默认发布路径
|
||||
- 默认发布镜像仍然是根目录单容器镜像
|
||||
|
||||
1. **检查 MySQL 服务是否运行**
|
||||
- 按 Win+R,输入 `services.msc`
|
||||
- 查找 "MySQL80" 服务
|
||||
- 确认状态为 "正在运行"
|
||||
## 场景二:单容器应用连接外部 MySQL
|
||||
|
||||
2. **启动 MySQL 服务**
|
||||
```bash
|
||||
net start MySQL80
|
||||
```
|
||||
如果你想继续使用单容器应用镜像,但数据库由外部 MySQL 托管,可以直接让应用容器连接现有数据库。
|
||||
|
||||
3. **检查防火墙设置**
|
||||
- 确保防火墙允许 MySQL 端口 3306
|
||||
### 1. 准备 MySQL 8.0 数据库
|
||||
|
||||
### 3. 密码验证失败
|
||||
执行以下 SQL 创建数据库和账号:
|
||||
|
||||
**错误信息:**
|
||||
```
|
||||
Access denied for user 'qquiz'@'localhost'
|
||||
```
|
||||
|
||||
**解决方案:**
|
||||
|
||||
重新创建用户并设置密码:
|
||||
```sql
|
||||
mysql -u root -p
|
||||
DROP USER IF EXISTS 'qquiz'@'localhost';
|
||||
CREATE USER 'qquiz'@'localhost' IDENTIFIED BY 'qquiz_password';
|
||||
GRANT ALL PRIVILEGES ON qquiz_db.* TO 'qquiz'@'localhost';
|
||||
CREATE DATABASE qquiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE USER 'qquiz'@'%' IDENTIFIED BY 'qquiz_password';
|
||||
GRANT ALL PRIVILEGES ON qquiz_db.* TO 'qquiz'@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
### 4. 字符集问题
|
||||
### 2. 修改 `.env`
|
||||
|
||||
**解决方案:**
|
||||
把 `DATABASE_URL` 改成你的 MySQL 地址,例如:
|
||||
|
||||
```env
|
||||
DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@mysql.example.com:3306/qquiz_db
|
||||
UPLOAD_DIR=/app/uploads
|
||||
```
|
||||
|
||||
### 3. 启动单容器镜像
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/handsomezhuzhu/qquiz:latest
|
||||
|
||||
docker volume create qquiz_uploads
|
||||
|
||||
docker run -d \
|
||||
--name qquiz \
|
||||
--env-file .env \
|
||||
-v qquiz_uploads:/app/uploads \
|
||||
-p 8000:8000 \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/handsomezhuzhu/qquiz:latest
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 这里不需要本地 SQLite 数据卷,因为数据库已经外置到 MySQL
|
||||
- 仍然建议保留上传目录卷,避免容器重建后丢失上传文件
|
||||
|
||||
## 本地开发连接 MySQL
|
||||
|
||||
如果你是在本机直接跑后端,`.env` 中可使用本地 MySQL 地址:
|
||||
|
||||
```env
|
||||
DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@localhost:3306/qquiz_db
|
||||
```
|
||||
|
||||
然后分别启动后端和前端:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
pip install -r requirements.txt
|
||||
alembic upgrade head
|
||||
uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
```bash
|
||||
cd web
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 连接不上 MySQL
|
||||
|
||||
检查以下几项:
|
||||
|
||||
- `DATABASE_URL` 中的主机名、端口、用户名和密码是否正确
|
||||
- MySQL 是否允许对应来源地址连接
|
||||
- 3306 端口是否开放
|
||||
|
||||
### 2. 容器里能连,宿主机里不能连
|
||||
|
||||
这是因为容器内部和宿主机访问地址不同:
|
||||
|
||||
- 容器之间互联时通常使用服务名,例如 `mysql`
|
||||
- 宿主机连接本机 MySQL 时通常使用 `localhost`
|
||||
|
||||
### 3. 字符集异常
|
||||
|
||||
建议数据库和表统一使用 `utf8mb4`:
|
||||
|
||||
确保数据库使用 UTF-8 字符集:
|
||||
```sql
|
||||
ALTER DATABASE qquiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 管理工具推荐
|
||||
|
||||
### 1. MySQL Workbench (官方)
|
||||
- 下载:https://dev.mysql.com/downloads/workbench/
|
||||
- 功能:可视化数据库管理、SQL 编辑器、备份还原
|
||||
|
||||
### 2. DBeaver (免费开源)
|
||||
- 下载:https://dbeaver.io/download/
|
||||
- 功能:多数据库支持、数据导入导出、ER 图
|
||||
|
||||
### 3. phpMyAdmin (Web 界面)
|
||||
- 适合习惯 Web 界面的用户
|
||||
|
||||
---
|
||||
|
||||
## 数据库备份与恢复
|
||||
|
||||
### 备份数据库
|
||||
|
||||
```bash
|
||||
mysqldump -u qquiz -p qquiz_db > backup.sql
|
||||
```
|
||||
|
||||
### 恢复数据库
|
||||
|
||||
```bash
|
||||
mysql -u qquiz -p qquiz_db < backup.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 切换回 PostgreSQL
|
||||
|
||||
如果需要切换回 PostgreSQL:
|
||||
|
||||
1. 修改 `requirements.txt`:
|
||||
```
|
||||
asyncpg==0.29.0 # 替换 aiomysql
|
||||
```
|
||||
|
||||
2. 修改 `.env`:
|
||||
```
|
||||
DATABASE_URL=postgresql+asyncpg://qquiz:qquiz_password@localhost:5432/qquiz_db
|
||||
```
|
||||
|
||||
3. 修改 `docker-compose.yml`:
|
||||
- 将 `mysql` 服务改回 `postgres`
|
||||
|
||||
4. 重新安装依赖:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技术支持
|
||||
|
||||
如遇到其他问题,请:
|
||||
1. 检查 MySQL 错误日志
|
||||
2. 确认防火墙和网络配置
|
||||
3. 查看项目 issues: https://github.com/handsomezhuzhu/QQuiz/issues
|
||||
|
||||
@@ -30,36 +30,17 @@ QQuiz/
|
||||
│ ├── alembic.ini # Alembic 配置
|
||||
│ └── Dockerfile # 后端 Docker 镜像
|
||||
│
|
||||
├── frontend/ # React 前端
|
||||
├── web/ # Next.js 前端
|
||||
│ ├── src/
|
||||
│ │ ├── api/
|
||||
│ │ │ └── client.js # API 客户端(Axios)⭐
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── Layout.jsx # 主布局(导航栏)
|
||||
│ │ │ └── ProtectedRoute.jsx # 路由保护
|
||||
│ │ ├── context/
|
||||
│ │ │ └── AuthContext.jsx # 认证上下文
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── Login.jsx # 登录页
|
||||
│ │ │ ├── Register.jsx # 注册页
|
||||
│ │ │ ├── Dashboard.jsx # 仪表盘
|
||||
│ │ │ ├── ExamList.jsx # 题库列表 ⭐
|
||||
│ │ │ ├── ExamDetail.jsx # 题库详情(追加上传)⭐
|
||||
│ │ │ ├── QuizPlayer.jsx # 刷题核心页面 ⭐
|
||||
│ │ │ ├── MistakeList.jsx # 错题本
|
||||
│ │ │ └── AdminSettings.jsx # 系统设置
|
||||
│ │ ├── utils/
|
||||
│ │ │ └── helpers.js # 工具函数
|
||||
│ │ ├── App.jsx # 应用根组件
|
||||
│ │ ├── index.jsx # 应用入口
|
||||
│ │ └── index.css # 全局样式
|
||||
│ ├── public/
|
||||
│ │ └── index.html # HTML 模板
|
||||
│ │ ├── app/ # App Router 页面、布局、Route Handlers
|
||||
│ │ ├── components/ # 共享 UI 组件
|
||||
│ │ ├── lib/ # API、认证、格式化等公共逻辑
|
||||
│ │ └── middleware.ts # 登录态守卫
|
||||
│ ├── package.json # Node 依赖
|
||||
│ ├── vite.config.js # Vite 配置
|
||||
│ ├── tailwind.config.js # Tailwind CSS 配置
|
||||
│ ├── postcss.config.js # PostCSS 配置
|
||||
│ └── Dockerfile # 前端 Docker 镜像
|
||||
│ ├── next.config.mjs # Next.js 配置
|
||||
│ ├── tailwind.config.ts # Tailwind CSS 配置
|
||||
│ ├── postcss.config.mjs # PostCSS 配置
|
||||
│ └── Dockerfile # 分离部署前端镜像
|
||||
│
|
||||
├── docker-compose.yml # Docker 编排配置 ⭐
|
||||
├── .env.example # 环境变量模板
|
||||
@@ -133,74 +114,31 @@ for q in questions_data:
|
||||
|
||||
### 前端核心
|
||||
|
||||
#### `client.js` - API 客户端
|
||||
封装了所有后端 API:
|
||||
- `authAPI`: 登录、注册、用户信息
|
||||
- `examAPI`: 题库 CRUD、追加文档
|
||||
- `questionAPI`: 获取题目、答题
|
||||
- `mistakeAPI`: 错题本管理
|
||||
- `adminAPI`: 系统配置
|
||||
#### `src/lib/api/server.ts` - 服务端 API 访问
|
||||
用于 Next Server Components 访问后端:
|
||||
- 从 `HttpOnly` Cookie 读取会话令牌
|
||||
- 直接请求 FastAPI `/api/*`
|
||||
- 401 时自动重定向回登录页
|
||||
|
||||
**特性:**
|
||||
- 自动添加 JWT Token
|
||||
- 统一错误处理和 Toast 提示
|
||||
- 401 自动跳转登录
|
||||
#### `src/lib/api/browser.ts` - 浏览器端 API 访问
|
||||
用于客户端交互:
|
||||
- 请求同源 `/frontend-api/proxy/*`
|
||||
- 统一处理错误信息
|
||||
- 默认禁用缓存,保持刷题和后台状态最新
|
||||
|
||||
#### `ExamDetail.jsx` - 题库详情
|
||||
最复杂的前端页面,包含:
|
||||
- **追加上传**: 上传新文档并去重
|
||||
- **状态轮询**: 每 3 秒轮询一次状态
|
||||
- **智能按钮**:
|
||||
- 处理中时禁用「添加文档」
|
||||
- 就绪后显示「开始/继续刷题」
|
||||
- **进度展示**: 题目数、完成度、进度条
|
||||
#### `src/components/exams/exam-detail-client.tsx` - 题库详情
|
||||
负责:
|
||||
- 追加上传文档
|
||||
- 展示解析进度
|
||||
- 通过 `/frontend-api/exams/{examId}/progress` 订阅同源 SSE
|
||||
- 处理解析完成/失败后的页面刷新
|
||||
|
||||
**状态轮询实现:**
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
pollExamStatus() // 轮询状态
|
||||
}, 3000)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [examId])
|
||||
|
||||
const pollExamStatus = async () => {
|
||||
const newExam = await examAPI.getDetail(examId)
|
||||
|
||||
// 检测状态变化
|
||||
if (exam?.status === 'processing' && newExam.status === 'ready') {
|
||||
toast.success('文档解析完成!')
|
||||
await loadExamDetail() // 重新加载数据
|
||||
}
|
||||
|
||||
setExam(newExam)
|
||||
}
|
||||
```
|
||||
|
||||
#### `QuizPlayer.jsx` - 刷题核心
|
||||
实现完整的刷题流程:
|
||||
1. 基于 `current_index` 加载当前题目
|
||||
2. 根据题型显示不同的答题界面
|
||||
3. 提交答案并检查(简答题调用 AI 评分)
|
||||
4. 答错自动加入错题本
|
||||
5. 点击下一题自动更新进度
|
||||
|
||||
**断点续做实现:**
|
||||
```javascript
|
||||
// 始终基于 exam.current_index 加载题目
|
||||
const loadCurrentQuestion = async () => {
|
||||
const question = await questionAPI.getCurrentQuestion(examId)
|
||||
// 后端会根据 current_index 返回对应题目
|
||||
}
|
||||
|
||||
// 下一题时更新进度
|
||||
const handleNext = async () => {
|
||||
const newIndex = exam.current_index + 1
|
||||
await examAPI.updateProgress(examId, newIndex)
|
||||
await loadCurrentQuestion()
|
||||
}
|
||||
```
|
||||
#### `src/components/practice/quiz-player-client.tsx` - 刷题核心
|
||||
负责:
|
||||
- 加载当前题目
|
||||
- 提交答案并展示结果
|
||||
- 推进刷题进度
|
||||
- 管理简答题与错题练习等交互
|
||||
|
||||
---
|
||||
|
||||
@@ -323,17 +261,17 @@ CREATE UNIQUE INDEX ix_user_mistakes_unique ON user_mistakes(user_id, question_i
|
||||
- **OpenAI/Anthropic/Qwen**: AI 解析和评分
|
||||
|
||||
### 前端
|
||||
- **Next.js 14 App Router**: 前端运行时
|
||||
- **React 18**: UI 框架
|
||||
- **Vite**: 构建工具(比 CRA 更快)
|
||||
- **TypeScript**: 类型系统
|
||||
- **Tailwind CSS**: 原子化 CSS
|
||||
- **Axios**: HTTP 客户端
|
||||
- **React Router**: 路由管理
|
||||
- **React Hot Toast**: 消息提示
|
||||
- **TanStack Query**: 客户端缓存和数据同步
|
||||
- **Route Handlers**: 同源认证与代理层
|
||||
|
||||
### 部署
|
||||
- **Docker + Docker Compose**: 容器化部署
|
||||
- **PostgreSQL 15**: 关系型数据库
|
||||
- **Nginx** (可选): 反向代理
|
||||
- **SQLite / MySQL**: 关系型数据库
|
||||
- **FastAPI reverse proxy**: 单容器模式下代理 Next.js
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -19,17 +19,17 @@ Audit date: 2026-04-17
|
||||
|
||||
### Frontend
|
||||
|
||||
- Runtime: React 18 + Vite SPA
|
||||
- Routing: `react-router-dom`
|
||||
- Auth state: client-only `localStorage` token + context
|
||||
- API transport: axios interceptor with browser redirects
|
||||
- Styling: Tailwind CSS with page-local utility classes
|
||||
- Runtime: Next.js App Router + TypeScript
|
||||
- Routing: file-system routing + middleware guards
|
||||
- Auth state: `HttpOnly` cookie managed by Next route handlers
|
||||
- API transport: server/client fetch helpers with same-origin proxy routes
|
||||
- Styling: Tailwind CSS + shadcn/ui patterns
|
||||
|
||||
### Deployment
|
||||
|
||||
- `docker-compose.yml`: development-oriented split stack
|
||||
- `docker-compose-single.yml`: monolith container with SQLite
|
||||
- `Dockerfile`: FastAPI serves the built SPA as static assets
|
||||
- `docker-compose.yml`: split development stack
|
||||
- `docker-compose-single.yml`: default single-container deployment
|
||||
- `Dockerfile`: single image running FastAPI + embedded Next.js
|
||||
|
||||
## Target Architecture
|
||||
|
||||
@@ -51,20 +51,20 @@ Audit date: 2026-04-17
|
||||
|
||||
### Deployment
|
||||
|
||||
- Split deployment becomes the primary production shape
|
||||
- Monolith mode remains secondary compatibility mode
|
||||
- Development and production Compose files must be separated
|
||||
- Single-container deployment is the primary release path
|
||||
- Split deployment remains available for development and compatibility testing
|
||||
- Development and production Compose files must stay explicitly separated
|
||||
|
||||
## Core Constraints
|
||||
|
||||
1. Do not overwrite existing uncommitted user changes in the legacy frontend.
|
||||
2. Keep the legacy `frontend/` app available until the new `web/` app reaches functional parity.
|
||||
3. Preserve backend API contracts where possible during the frontend migration.
|
||||
4. Fix deployment/documentation drift before treating new frontend work as production-ready.
|
||||
1. Preserve backend API contracts where possible across frontend changes.
|
||||
2. Keep single-container and split-stack behavior aligned on the same `web/` frontend.
|
||||
3. Fix deployment/documentation drift before treating changes as production-ready.
|
||||
4. Avoid reintroducing duplicate frontend implementations.
|
||||
|
||||
## Immediate Workstreams
|
||||
|
||||
1. Remove abandoned ESA captcha wiring from the legacy frontend.
|
||||
2. Write audit documents and freeze the migration backlog.
|
||||
3. Scaffold the new `web/` frontend without disturbing the legacy app.
|
||||
4. Fix first-order deployment issues such as health checks and documented mount paths.
|
||||
1. Keep single-container delivery using the same `web/` frontend as split deployment.
|
||||
2. Continue moving backend orchestration into typed services.
|
||||
3. Tighten health checks and deployment docs around the embedded Next runtime.
|
||||
4. Cover remaining functional gaps with smoke tests.
|
||||
|
||||
@@ -1,70 +1,50 @@
|
||||
# Frontend Migration Plan
|
||||
# Frontend Cutover Notes
|
||||
|
||||
## Decision
|
||||
|
||||
The legacy Vite SPA remains in `frontend/` as a fallback.
|
||||
`web/` is now the only frontend in the repository.
|
||||
|
||||
The new frontend is being built in `web/` with:
|
||||
The previous Vite SPA has been removed so that:
|
||||
|
||||
- Next.js App Router
|
||||
- TypeScript
|
||||
- Tailwind CSS
|
||||
- shadcn/ui component model
|
||||
- split deployment and single-container deployment use the same UI
|
||||
- documentation no longer has to describe two competing frontend stacks
|
||||
- future frontend changes only need to be implemented once
|
||||
|
||||
The abandoned ESA captcha integration has been removed from the legacy login page.
|
||||
|
||||
## Why a Rewrite Instead of an In-Place Port
|
||||
|
||||
The legacy frontend mixes too many browser-only assumptions into core runtime
|
||||
boundaries:
|
||||
|
||||
- token storage in `localStorage`
|
||||
- `window.location` redirects inside transport code
|
||||
- client-only route protection
|
||||
- SSE token passing in query strings
|
||||
|
||||
Those patterns do not map cleanly onto Next App Router and server-first auth.
|
||||
|
||||
## New Runtime Model
|
||||
## Runtime Model
|
||||
|
||||
### Auth
|
||||
|
||||
- Login goes through Next route handlers
|
||||
- Login goes through Next route handlers under `/frontend-api/auth/*`
|
||||
- Backend JWT is stored in an `HttpOnly` cookie
|
||||
- Browser code never reads the raw token
|
||||
|
||||
### Data
|
||||
|
||||
- Server pages use server-side fetch helpers
|
||||
- Client mutations use browser-side fetch helpers against Next proxy routes
|
||||
- URL state is used for pagination and filters
|
||||
- Server pages use server-side fetch helpers against FastAPI
|
||||
- Client mutations use browser-side fetch helpers against `/frontend-api/proxy/*`
|
||||
- FastAPI continues to own the public `/api/*` surface
|
||||
|
||||
### Streaming
|
||||
|
||||
- Browser connects to a same-origin Next progress route
|
||||
- Browser connects to `/frontend-api/exams/{examId}/progress`
|
||||
- The route reads the session cookie and proxies backend SSE
|
||||
- Backend URL tokens are hidden from the browser
|
||||
- Backend token query parameters stay hidden from the browser
|
||||
|
||||
## Directory Map
|
||||
## Deployment Outcome
|
||||
|
||||
```text
|
||||
web/
|
||||
src/app/
|
||||
src/components/
|
||||
src/lib/
|
||||
src/middleware.ts
|
||||
```
|
||||
### Split Stack
|
||||
|
||||
## Migration Order
|
||||
- `backend` serves API traffic on `:8000`
|
||||
- `web` serves Next.js on `:3000`
|
||||
|
||||
1. Auth shell, layouts, middleware, and proxy routes
|
||||
2. Dashboard, exams list, questions list, and admin overview
|
||||
3. Exam detail upload and progress streaming
|
||||
4. Quiz and mistake-practice flows
|
||||
5. Cutover, smoke testing, and legacy frontend retirement
|
||||
### Single Container
|
||||
|
||||
## Non-Goals for This First Slice
|
||||
- the container runs both FastAPI and Next.js
|
||||
- FastAPI stays on `:8000`
|
||||
- non-API requests are proxied from FastAPI to the embedded Next server
|
||||
|
||||
- No immediate removal of the legacy `frontend/`
|
||||
- No backend contract rewrite yet
|
||||
- No server actions as the primary data mutation layer
|
||||
## Follow-up Expectations
|
||||
|
||||
1. New frontend work lands only in `web/`
|
||||
2. Single-container smoke tests must validate both UI and API paths
|
||||
3. Deployment docs must continue to describe `web/` as the sole frontend
|
||||
|
||||
Reference in New Issue
Block a user