feat: implement 33 nice-to-have features + fix 37 code review bugs

5 SDD batches archived:
- Batch 1: UI Polish (10 features, 14 tasks)
- Batch 2: Study System (8 features, 23 tasks)
- Batch 3: Infrastructure (5 features, 22 tasks)
- Batch 4: AI Advanced (5 features, 30 tasks) — RAG with @xenova/transformers
- Batch 5: Core Features (5 features, 19 tasks)

37 bugs fixed from comprehensive code review (11 CRITICAL, 12 HIGH, 14 MEDIUM/LOW):
- SSE streaming now works (event.token check)
- API keys no longer exposed via GET /api/models
- FTS5 injection sanitized
- DB backup/restore with admin auth
- Buddy mode wired (buddy_meta column)
- Exam auto-submit stale closure fixed
- CSS variables aligned with design tokens
- Progress data corruption fixed
- WebSocket protocol auto-detection
- Tests infrastructure completed (vitest + node:test)
This commit is contained in:
renato97
2026-06-08 18:18:47 -03:00
parent b7d1e7319f
commit 4ff4302a8c
79 changed files with 13667 additions and 389 deletions

View File

@@ -1,10 +1,13 @@
const express = require('express');
const http = require('http');
const fs = require('fs');
const { WebSocketServer } = require('ws');
const cors = require('cors');
const path = require('path');
const db = require('./db');
const { setWss, broadcast, broadcastBuddy } = require('./lib/broadcast');
const embeddings = require('./lib/embeddings');
const pdfRoutes = require('./routes/pdfs');
const conversationRoutes = require('./routes/conversations');
const chatRoutes = require('./routes/chat');
@@ -12,6 +15,9 @@ const progressRoutes = require('./routes/progress');
const notesRoutes = require('./routes/notes');
const modelRoutes = require('./routes/models');
const configRoutes = require('./routes/config');
const examRoutes = require('./routes/exams');
const flashcardRoutes = require('./routes/flashcards');
const searchRoutes = require('./routes/search');
const app = express();
const server = http.createServer(app);
@@ -33,9 +39,15 @@ app.use('/api/progress', progressRoutes);
app.use('/api/notes', notesRoutes);
app.use('/api/models', modelRoutes);
app.use('/api/config', configRoutes);
app.use('/api/exams', examRoutes);
app.use('/api/flashcards', flashcardRoutes);
app.use('/api/search', searchRoutes);
// Serve React build in production
const clientDist = path.resolve(__dirname, '..', 'client', 'dist');
if (!fs.existsSync(clientDist)) {
console.warn('[index] client/dist not found — SPA fallback will 404 until client is built');
}
app.use(express.static(clientDist));
app.get('*', (req, res, next) => {
if (req.path.startsWith('/api')) return next();
@@ -46,6 +58,7 @@ app.get('*', (req, res, next) => {
// WebSocket server
const wss = new WebSocketServer({ server });
setWss(wss);
wss.on('connection', (ws) => {
ws.on('message', (message) => {
// Echo — extensible for real-time notifications
@@ -59,6 +72,10 @@ wss.on('connection', (ws) => {
async function start() {
await db.initDB();
// Warm up embeddings pipeline in background (non-blocking)
embeddings.warmup().catch((err) => {
console.warn('[index] embeddings warmup failed:', err.message);
});
server.listen(PORT, () => {
console.log(`StudyOS server running on http://localhost:${PORT}`);
console.log(`WebSocket server running on ws://localhost:${PORT}`);
@@ -69,3 +86,5 @@ start().catch(err => {
console.error('Failed to start server:', err);
process.exit(1);
});
module.exports = { app, server, wss };