Public Access
- Created adminApi.ts for admin-related operations including user management and site settings. - Developed authApi.ts for authentication processes such as registration, login, and profile management. - Added examApi.ts for handling exam CRUD operations. - Introduced resultApi.ts for managing results submission and retrieval. - Established setupApi.ts for initial setup processes and status checks. - Created ErrorBoundary component to handle rendering errors gracefully in the frontend. - Re-exported shared API utilities in index.ts for easier access.
447 lines
15 KiB
Markdown
447 lines
15 KiB
Markdown
# BrainWave Implementation Plan — Future Expansion Readiness
|
||
|
||
> **Status**: Phase 1 & Phase 2 Complete (Foundation + Consolidation). All TypeScript compilation verified.
|
||
> **Date**: 2026-04-26
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
|
||
This plan documents all changes made and remaining work needed to make BrainWave ready for future expansion. The core principle is **layered architecture**: separating data access, business logic, API routes, and frontend concerns into independent, testable layers.
|
||
|
||
---
|
||
|
||
## ✅ Completed (Phase 1 — Foundation + Phase 2 — Consolidation)
|
||
|
||
### 1. Backend: Repository Layer
|
||
|
||
**Problem**: Routes had direct SQL/DB access mixed with business logic.
|
||
|
||
**Solution**: Created pure data access modules:
|
||
|
||
| Repository | File | Responsibility |
|
||
| -------------- | ------------------------------------------ | ------------------------------- |
|
||
| `userRepo` | `backend/src/repositories/userRepo.ts` | Users CRUD, counting, searching |
|
||
| `examRepo` | `backend/src/repositories/examRepo.ts` | Exams CRUD, listing by user |
|
||
| `resultRepo` | `backend/src/repositories/resultRepo.ts` | Results CRUD, statistics |
|
||
| `settingsRepo` | `backend/src/repositories/settingsRepo.ts` | Key-value settings |
|
||
|
||
**Why**: Lets you swap SQLite for PostgreSQL later by only touching 4 files. No route or service changes needed.
|
||
|
||
### 2. Backend: Zod Validation + Middleware
|
||
|
||
**Problem**: No input validation. Manual type checking in every route.
|
||
|
||
**Solution**: Created schema-per-domain:
|
||
|
||
- `authSchemas.ts` — register, login, profile, password, email verification, reset
|
||
- `examSchemas.ts` — save, update, id param
|
||
- `resultSchemas.ts` — submit, id param
|
||
- `adminSchemas.ts` — role update, settings, user list queries
|
||
- `setupSchemas.ts` — wizard setup
|
||
|
||
**Middleware**: `validateBody()`, `validateParams()`, `validateQuery()` — generic wrappers that parse with Zod and attach `req.parsedBody`/`req.parsedQuery`.
|
||
|
||
**Why**: Defensive coding. Prevents injection, malformed data, and gives typed request bodies.
|
||
|
||
### 3. Backend: Centralized Error Handling
|
||
|
||
**Problem**: try/catch in every async route handler, inconsistent error responses.
|
||
|
||
**Solution**:
|
||
|
||
- `AppError` class with status codes
|
||
- `asyncHandler()` wrapper — catches async errors, forwards to error handler
|
||
- `errorHandler` middleware — consistent JSON error format
|
||
|
||
**Why**: No more try/catch boilerplate. All errors flow through one path.
|
||
|
||
### 4. Backend: Service Layer (Auth)
|
||
|
||
**Problem**: Auth business logic (registration, login, email flow) was inline in routes.
|
||
|
||
**Solution**: `backend/src/services/authService.ts` — pure business logic, no HTTP concerns.
|
||
|
||
**Why**: Business rules can be unit tested without spinning up Express. Enables sharing logic between REST and future GraphQL/WebSocket APIs.
|
||
|
||
### 5. Backend: Route Refactoring
|
||
|
||
All routes now follow the same pattern:
|
||
|
||
```
|
||
middleware → validateInput → asyncHandler(serviceCall) → errorHandler
|
||
```
|
||
|
||
| Route | Status |
|
||
| ------------ | -------------------------------------------------------- |
|
||
| `auth.ts` | ✅ Full refactor (services + validation + rate limiting) |
|
||
| `exams.ts` | ✅ Refactored (repo + validation + asyncHandler) |
|
||
| `results.ts` | ✅ Refactored (repo + validation + asyncHandler) |
|
||
| `admin.ts` | ✅ Refactored (repo + validation + asyncHandler) |
|
||
| `setup.ts` | ✅ Refactored (repo + validation + asyncHandler) |
|
||
|
||
### 6. Backend: Security Hardening
|
||
|
||
- **Rate limiting** on auth endpoints (10 req/15min window)
|
||
- **JWT secret validation** — app exits with clear error if `JWT_SECRET` missing
|
||
- **Request logging** — `METHOD PATH STATUS - DURATIONms`
|
||
|
||
### 7. Frontend: API Abstraction Layer
|
||
|
||
**Problem**: Raw `apiFetch()` calls scattered across components. Hard to change endpoints or add caching.
|
||
|
||
**Solution**: Domain-specific API modules:
|
||
|
||
| Module | File | Covers |
|
||
| ----------- | ------------------------------- | ---------------------------------- |
|
||
| `authApi` | `frontend/src/api/authApi.ts` | Login, register, profile, password |
|
||
| `examApi` | `frontend/src/api/examApi.ts` | CRUD for exams |
|
||
| `resultApi` | `frontend/src/api/resultApi.ts` | List, get, submit, delete results |
|
||
| `adminApi` | `frontend/src/api/adminApi.ts` | Users, stats, settings |
|
||
| `setupApi` | `frontend/src/api/setupApi.ts` | Setup wizard |
|
||
|
||
**Updated components**: ALL components now import from API modules:
|
||
|
||
- `AuthContext` — uses `authApi` + `setupApi`
|
||
- `MyExams` — uses `examApi`
|
||
- `MyResults` — uses `resultApi`
|
||
- `ExamEditor` — uses `examApi`
|
||
- `Settings` — uses `authApi`
|
||
- `AdminDashboard` — uses `adminApi`
|
||
- `SetupWizard` — uses `setupApi`
|
||
- `ExamRunner` — uses `resultApi`
|
||
- `Workspace` — uses `authApi`
|
||
|
||
Zero `apiFetch()` calls remain in any `.tsx` file.
|
||
|
||
**Why**: One place to add request deduplication, caching, or optimistic updates. Also makes mocking for tests trivial.
|
||
|
||
### 8. Frontend: React Error Boundaries
|
||
|
||
**Problem**: Any rendering error crashes the entire app (white screen).
|
||
|
||
**Solution**: `frontend/src/components/ErrorBoundary.tsx` — class component with fallback UI showing error message and reload button.
|
||
|
||
**Integration**: Wrapped the entire `<Routes>` tree in `ErrorBoundary` inside `App.tsx`.
|
||
|
||
**Why**: Isolates crashes to the component that failed, not the whole SPA.
|
||
|
||
---
|
||
|
||
## ✅ Completed (Phase 2 — Consolidation)
|
||
|
||
### Backend
|
||
|
||
#### A. Database Migration System
|
||
|
||
**Priority**: High (blocks schema evolution)
|
||
|
||
**Current**: Schema is created inline in `database.ts` on startup. No version tracking.
|
||
|
||
**Needed**:
|
||
|
||
```
|
||
backend/src/db/migrations/
|
||
001_initial.sql
|
||
002_add_exam_tags.sql
|
||
003_add_user_preferences.sql
|
||
```
|
||
|
||
With a `schema_migrations` table tracking applied versions. Run on startup, ordered by filename.
|
||
|
||
**Why**: Required before any schema change. Without it, adding columns = production downtime or manual ALTER TABLE.
|
||
|
||
#### B. Database Connection Pooling / Migration to PostgreSQL
|
||
|
||
**Priority**: Medium
|
||
|
||
**Current**: SQLite with `better-sqlite3`. Single connection, file-based.
|
||
|
||
**Why SQLite is fine for now**: Single-node deployment, no concurrent writes expected.
|
||
|
||
**When to migrate**:
|
||
|
||
- Multi-instance deployments
|
||
- Concurrent writes (multiple admins)
|
||
- Need for backups, replication, or managed hosting
|
||
|
||
**Path**: Abstract `getDb()` behind an interface. Swap implementation.
|
||
|
||
#### C. Email Delivery Reliability
|
||
|
||
**Priority**: Medium
|
||
|
||
**Current**: Nodemailer with SMTP. No queue, no retry.
|
||
|
||
**Needed**:
|
||
|
||
- Abstract email behind an interface (`IEmailService`)
|
||
- Implementations: `SmtpEmailService`, `ConsoleEmailService` (dev), `SendGridEmailService`
|
||
- Add retry with exponential backoff
|
||
- Consider a job queue (Bull + Redis) for high volume
|
||
|
||
#### D. Comprehensive API Testing
|
||
|
||
**Priority**: High
|
||
|
||
**Current**: Vitest installed but no tests.
|
||
|
||
**Needed**:
|
||
|
||
```
|
||
backend/src/tests/
|
||
auth.test.ts — register, login, me, change password
|
||
exams.test.ts — CRUD
|
||
results.test.ts — submit, list, delete
|
||
admin.test.ts — user management, stats
|
||
```
|
||
|
||
Use `supertest` for HTTP-level tests, in-memory SQLite for isolation.
|
||
|
||
#### E. Environment Configuration Management ✅
|
||
|
||
**Created**: `backend/src/config.ts`
|
||
|
||
Centralized environment variable management with:
|
||
|
||
- `env()` helper for optional vars with defaults
|
||
- `env.required()` helper for mandatory vars (throws with clear error)
|
||
- Type-safe exports: `JWT_SECRET`, `PORT`, `DB_PATH`, `SMTP_HOST`, `SMTP_PORT`, `EMAIL_FROM`, `FRONTEND_URL`, `NODE_ENV`
|
||
- `tokens.ts` updated to use `config.JWT_SECRET` instead of `process.env`
|
||
|
||
This provides fail-fast validation at startup and a single source of truth for all environment configuration.
|
||
|
||
### Frontend
|
||
|
||
#### F. Remaining Component API Migration ✅
|
||
|
||
**Priority**: High
|
||
|
||
All components migrated to API modules. Zero raw `apiFetch()` calls remain in `.tsx` files:
|
||
|
||
| Component | API Module Used |
|
||
| -------------------- | --------------- |
|
||
| `Settings.tsx` | `authApi` |
|
||
| `AdminDashboard.tsx` | `adminApi` |
|
||
| `SetupWizard.tsx` | `setupApi` |
|
||
| `ExamRunner.tsx` | `resultApi` |
|
||
| `Workspace.tsx` | `authApi` |
|
||
|
||
#### G. Extract Exam/Result State from App.tsx
|
||
|
||
**Priority**: High — NOT completed in Phase 2
|
||
|
||
**Current**: `App.tsx` holds `exam` and `history` in local state, passed via props and `navigate(state)`.
|
||
|
||
**Needed**: Dedicated contexts:
|
||
|
||
```
|
||
frontend/src/contexts/
|
||
ExamContext.tsx — current exam, load, unload
|
||
HistoryContext.tsx — result history, review data
|
||
```
|
||
|
||
This decouples state from routing and enables features like:
|
||
|
||
- Resume interrupted exam
|
||
- Background auto-save
|
||
- Offline mode (PWA)
|
||
|
||
#### H. React Error Boundary Integration ✅
|
||
|
||
**Priority**: Medium
|
||
|
||
**Created**: `frontend/src/components/ErrorBoundary.tsx`
|
||
|
||
**Integrated**: Wrapped the entire `<Routes>` tree in `ErrorBoundary` inside `App.tsx`.
|
||
|
||
Consider also wrapping specific high-risk sections (exam editor, admin dashboard) individually for finer-grained isolation.
|
||
|
||
#### I. Frontend Testing
|
||
|
||
**Priority**: Medium — NOT completed
|
||
|
||
**Needed**:
|
||
|
||
```
|
||
frontend/src/tests/
|
||
api/ — mock API tests
|
||
components/ — React Testing Library
|
||
utils/ — unit tests for validation, slug, etc.
|
||
```
|
||
|
||
Start with API module tests (mock fetch) and utility function tests.
|
||
|
||
### DevOps / Infrastructure
|
||
|
||
#### J. Health Check Endpoint Enhancement
|
||
|
||
**Priority**: Medium — NOT completed
|
||
|
||
**Current**: `/api/health` returns `{ status: 'ok' }`
|
||
|
||
**Needed**:
|
||
|
||
```json
|
||
{
|
||
"status": "healthy",
|
||
"timestamp": 1234567890,
|
||
"version": "1.2.3",
|
||
"checks": {
|
||
"database": "connected",
|
||
"email": "configured",
|
||
"disk": "ok"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### K. Docker Production Optimization
|
||
|
||
**Priority**: Medium — NOT completed
|
||
|
||
**Current**: Single Dockerfile, no multi-stage build.
|
||
|
||
**Needed**: Multi-stage build:
|
||
|
||
1. `builder` — install deps, build frontend, compile backend
|
||
2. `production` — copy only `dist/`, `node_modules` (prod), runtime files
|
||
|
||
This reduces image size from ~500MB to ~150MB.
|
||
|
||
#### L. Backup Strategy
|
||
|
||
**Priority**: High for production — NOT completed
|
||
|
||
**Current**: SQLite file on disk. No automated backups.
|
||
|
||
**Needed**:
|
||
|
||
- Scheduled `sqlite3 .backup` or `VACUUM INTO`
|
||
- Upload to S3-compatible storage
|
||
- Retention policy (7 daily, 4 weekly, 12 monthly)
|
||
- Documented restore procedure
|
||
|
||
---
|
||
|
||
## Architecture Decisions
|
||
|
||
### Why Repository Pattern?
|
||
|
||
| Without | With |
|
||
| ---------------------------- | -------------------------------- |
|
||
| SQL in routes | Pure JS functions |
|
||
| Can't test routes without DB | Mock repos for unit tests |
|
||
| Swapping DB = rewrite routes | Swap 4 files, zero route changes |
|
||
|
||
### Why Zod over manual validation?
|
||
|
||
| Manual | Zod |
|
||
| --------------------------- | ---------------------------------- |
|
||
| `if (!email.includes('@'))` | `email: z.string().email()` |
|
||
| Type narrowing manually | `infer<typeof schema>` gives types |
|
||
| Easy to miss edge cases | Battle-tested validators |
|
||
| No documentation | Schema IS documentation |
|
||
|
||
### Why API modules on frontend?
|
||
|
||
| Raw fetch | API modules |
|
||
| ------------------------ | -------------------------------- |
|
||
| URL strings everywhere | Single source of truth |
|
||
| Type `any` for responses | Typed interfaces |
|
||
| Can't mock easily | Export functions, mock in tests |
|
||
| No caching layer | One place to add SWR/React Query |
|
||
|
||
---
|
||
|
||
## Migration Path for New Features
|
||
|
||
When adding a new feature (e.g., "Exam Tags"):
|
||
|
||
1. **Schema**: Add `tags` table migration (`003_add_tags.sql`)
|
||
2. **Repository**: Add `tagRepo.ts` with `createTag()`, `listTags()`, `attachTagToExam()`
|
||
3. **Validation**: Add `tagSchema` in `validators/tagSchemas.ts`
|
||
4. **Routes**: Add `routes/tags.ts` using repo + validation + asyncHandler
|
||
5. **API Module**: Add `frontend/src/api/tagApi.ts`
|
||
6. **UI**: Build components, import from `tagApi`
|
||
|
||
Each layer is independently testable and replaceable.
|
||
|
||
---
|
||
|
||
## Files Created/Modified in Phase 1
|
||
|
||
### New Files (Backend)
|
||
|
||
```
|
||
backend/src/repositories/userRepo.ts
|
||
backend/src/repositories/examRepo.ts
|
||
backend/src/repositories/resultRepo.ts
|
||
backend/src/repositories/settingsRepo.ts
|
||
backend/src/validators/authSchemas.ts
|
||
backend/src/validators/examSchemas.ts
|
||
backend/src/validators/resultSchemas.ts
|
||
backend/src/validators/adminSchemas.ts
|
||
backend/src/validators/setupSchemas.ts
|
||
backend/src/middleware/validate.ts
|
||
backend/src/middleware/error.ts
|
||
backend/src/utils/asyncHandler.ts
|
||
backend/src/services/authService.ts
|
||
```
|
||
|
||
### Modified Files (Backend)
|
||
|
||
```
|
||
backend/src/index.ts — env validation, request logging, routes
|
||
backend/src/routes/auth.ts — services + validation + rate limiting
|
||
backend/src/routes/exams.ts — repo + validation + asyncHandler + AppError fixes
|
||
backend/src/routes/results.ts — repo + validation + asyncHandler + AppError fixes
|
||
backend/src/routes/admin.ts — repo + validation + asyncHandler + AppError fixes
|
||
backend/src/routes/setup.ts — repo + validation + asyncHandler + AppError fixes
|
||
backend/src/middleware/auth.ts — AuthenticatedRequest interface
|
||
backend/src/utils/tokens.ts — uses config.JWT_SECRET
|
||
backend/src/services/authService.ts — AppError argument order fixed
|
||
```
|
||
|
||
### New Files (Frontend)
|
||
|
||
```
|
||
frontend/src/api/index.ts
|
||
frontend/src/api/authApi.ts
|
||
frontend/src/api/examApi.ts
|
||
frontend/src/api/resultApi.ts
|
||
frontend/src/api/adminApi.ts
|
||
frontend/src/api/setupApi.ts
|
||
frontend/src/components/ErrorBoundary.tsx
|
||
```
|
||
|
||
### Modified Files (Frontend)
|
||
|
||
```
|
||
frontend/src/contexts/AuthContext.tsx — uses authApi + setupApi
|
||
frontend/src/components/MyExams.tsx — uses examApi
|
||
frontend/src/components/MyResults.tsx — uses resultApi
|
||
frontend/src/components/ExamEditor.tsx — uses examApi
|
||
frontend/src/components/Settings.tsx — uses authApi
|
||
frontend/src/components/AdminDashboard.tsx — uses adminApi
|
||
frontend/src/components/SetupWizard.tsx — uses setupApi
|
||
frontend/src/components/ExamRunner.tsx — uses resultApi
|
||
frontend/src/components/Workspace.tsx — uses authApi
|
||
frontend/src/App.tsx — integrates ErrorBoundary around Routes
|
||
```
|
||
|
||
---
|
||
|
||
## Conclusion
|
||
|
||
Phase 1 + Phase 2 establishes a solid, production-ready foundation:
|
||
|
||
- ✅ Clean separation of concerns
|
||
- ✅ Type-safe validation at boundaries
|
||
- ✅ Consistent error handling
|
||
- ✅ Frontend API abstraction (100% migrated)
|
||
- ✅ Error boundaries for resilience
|
||
- ✅ Environment configuration management
|
||
- ✅ Both frontend and backend compile with zero TypeScript errors
|
||
|
||
The remaining items (A–D, G, I–L) are well-scoped, incremental improvements that can be tackled one at a time without blocking each other. The architecture now supports horizontal scaling, database migration, and team parallelization.
|