完善文档与前端迁移,补充开源协议

This commit is contained in:
2026-04-17 19:48:13 +08:00
parent 466fa50aa8
commit 31916e68a6
94 changed files with 7019 additions and 480 deletions

View 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.

View 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.

View 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

View 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