mirror of
https://github.com/handsomezhuzhu/QQuiz.git
synced 2026-04-18 22:42:53 +00:00
完善文档与前端迁移,补充开源协议
This commit is contained in:
70
docs/audit/architecture.md
Normal file
70
docs/audit/architecture.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# QQuiz Architecture Audit
|
||||
|
||||
## Scope
|
||||
|
||||
This document records the current system shape and the approved target
|
||||
direction for the ongoing refactor.
|
||||
|
||||
Audit date: 2026-04-17
|
||||
|
||||
## Current Architecture
|
||||
|
||||
### Backend
|
||||
|
||||
- Runtime: FastAPI + SQLAlchemy async
|
||||
- Database access: direct ORM session injection per request
|
||||
- Task execution: in-process `BackgroundTasks`
|
||||
- Progress streaming: in-memory `ProgressService`
|
||||
- Schema management: mixed `create_all()` and Alembic placeholders
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
## Target Architecture
|
||||
|
||||
### Backend
|
||||
|
||||
- Keep FastAPI as the system API boundary
|
||||
- Move heavy router logic into typed services
|
||||
- Use Alembic as the only schema migration path
|
||||
- Introduce durable ingestion execution semantics
|
||||
- Replace implicit transaction patterns with explicit service-level boundaries
|
||||
|
||||
### Frontend
|
||||
|
||||
- New app in `web/`
|
||||
- Stack: Next.js App Router + TypeScript + Tailwind + shadcn/ui
|
||||
- Auth: `HttpOnly` session cookie mediated by Next route handlers
|
||||
- Data fetching: `fetch` wrappers for server/client usage
|
||||
- Streaming: Next proxy route for exam progress SSE
|
||||
|
||||
### Deployment
|
||||
|
||||
- Split deployment becomes the primary production shape
|
||||
- Monolith mode remains secondary compatibility mode
|
||||
- Development and production Compose files must be 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.
|
||||
|
||||
## 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.
|
||||
86
docs/audit/backend-findings.md
Normal file
86
docs/audit/backend-findings.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Backend Findings
|
||||
|
||||
## Critical Findings
|
||||
|
||||
### Schema lifecycle is unsafe
|
||||
|
||||
- App startup still calls `create_all()`
|
||||
- Alembic metadata exists but the migration chain is effectively empty
|
||||
- This prevents controlled upgrades and rollbacks
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/main.py`
|
||||
- `backend/database.py`
|
||||
- `backend/alembic/versions/.gitkeep`
|
||||
|
||||
### Parsing tasks are not durable
|
||||
|
||||
- Document ingestion runs inside FastAPI `BackgroundTasks`
|
||||
- Progress state lives in-process only
|
||||
- Process restarts or horizontal scaling can strand exams in `pending` or `processing`
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/routers/exam.py`
|
||||
- `backend/services/progress_service.py`
|
||||
|
||||
### Transaction boundaries are inconsistent
|
||||
|
||||
- `get_db()` performs commit/rollback automatically
|
||||
- Routers and background tasks also call `commit()` directly
|
||||
- SSE endpoints keep a database dependency open for long-lived streams
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/database.py`
|
||||
- `backend/routers/exam.py`
|
||||
|
||||
## High-Priority Bugs
|
||||
|
||||
### Admin config can destroy secrets
|
||||
|
||||
- `GET /api/admin/config` masks API keys
|
||||
- `PUT /api/admin/config` persists whatever the frontend sends back
|
||||
- A round-trip save can replace the real secret with the masked placeholder
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/routers/admin.py`
|
||||
|
||||
### LLM service has import-time side effects
|
||||
|
||||
- `LLMService()` is instantiated at module import time
|
||||
- Missing environment variables can break startup before DB-backed config is loaded
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/services/llm_service.py`
|
||||
|
||||
### Ingestion deduplication is race-prone
|
||||
|
||||
- No unique DB constraint on `(exam_id, content_hash)`
|
||||
- Multiple append operations can race and insert duplicates
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/models.py`
|
||||
- `backend/routers/exam.py`
|
||||
|
||||
### Answer checking degrades incorrectly on infra failure
|
||||
|
||||
- Short-answer grading failures are converted into zero scores
|
||||
- User mistake data can be polluted by provider outages or config errors
|
||||
|
||||
Files:
|
||||
|
||||
- `backend/services/llm_service.py`
|
||||
- `backend/routers/question.py`
|
||||
|
||||
## Refactor Order
|
||||
|
||||
1. Replace runtime schema creation with Alembic-first migrations.
|
||||
2. Move ingestion, config, and answer checking into service classes.
|
||||
3. Introduce explicit transaction boundaries and idempotent ingestion rules.
|
||||
4. Add durable task execution and real status/error semantics.
|
||||
5. Add integration tests for config round-trips, ingestion races, and answer normalization.
|
||||
49
docs/audit/deployment-findings.md
Normal file
49
docs/audit/deployment-findings.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Deployment Findings
|
||||
|
||||
## Current Problems
|
||||
|
||||
### Monolith persistence documentation is wrong
|
||||
|
||||
- Existing `docker run` examples mounted the wrong path
|
||||
- SQLite and upload persistence must target `/app/data` and `/app/uploads`
|
||||
|
||||
### Monolith health check was broken
|
||||
|
||||
- `docker-compose-single.yml` used `curl`
|
||||
- The image does not guarantee `curl` exists
|
||||
- The health check has been switched to Python stdlib HTTP probing
|
||||
|
||||
### Split Compose is development-oriented
|
||||
|
||||
- Source mounts are enabled
|
||||
- Backend runs with `uvicorn --reload`
|
||||
- Frontend runs a dev server
|
||||
- This is not a production deployment model
|
||||
|
||||
### Security posture is weak
|
||||
|
||||
- Compose contains hard-coded MySQL credentials
|
||||
- MySQL is exposed on `3306`
|
||||
- Environment guidance is inconsistent across README, Compose, and `.env.example`
|
||||
|
||||
## Approved Direction
|
||||
|
||||
1. Treat split deployment as the default production topology.
|
||||
2. Keep monolith deployment as a compatibility target only.
|
||||
3. Separate development assets from production assets.
|
||||
4. Validate all release images with smoke checks before publishing.
|
||||
|
||||
## Backlog
|
||||
|
||||
### Short term
|
||||
|
||||
- Create `compose.dev.yml` and `compose.prod.yml`
|
||||
- Remove dev-server assumptions from production documentation
|
||||
- Add backend runtime dependencies explicitly to image builds
|
||||
- Align README with actual mount paths and health checks
|
||||
|
||||
### Medium term
|
||||
|
||||
- Add PR build, typecheck, lint, and smoke-test workflows
|
||||
- Publish separate images for API and Next web app
|
||||
- Document rollback by image tag and Compose profile
|
||||
70
docs/audit/frontend-migration.md
Normal file
70
docs/audit/frontend-migration.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Frontend Migration Plan
|
||||
|
||||
## Decision
|
||||
|
||||
The legacy Vite SPA remains in `frontend/` as a fallback.
|
||||
|
||||
The new frontend is being built in `web/` with:
|
||||
|
||||
- Next.js App Router
|
||||
- TypeScript
|
||||
- Tailwind CSS
|
||||
- shadcn/ui component model
|
||||
|
||||
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
|
||||
|
||||
### Auth
|
||||
|
||||
- Login goes through Next route handlers
|
||||
- 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
|
||||
|
||||
### Streaming
|
||||
|
||||
- Browser connects to a same-origin Next progress route
|
||||
- The route reads the session cookie and proxies backend SSE
|
||||
- Backend URL tokens are hidden from the browser
|
||||
|
||||
## Directory Map
|
||||
|
||||
```text
|
||||
web/
|
||||
src/app/
|
||||
src/components/
|
||||
src/lib/
|
||||
src/middleware.ts
|
||||
```
|
||||
|
||||
## Migration Order
|
||||
|
||||
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
|
||||
|
||||
## Non-Goals for This First Slice
|
||||
|
||||
- No immediate removal of the legacy `frontend/`
|
||||
- No backend contract rewrite yet
|
||||
- No server actions as the primary data mutation layer
|
||||
Reference in New Issue
Block a user