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:
@@ -3,10 +3,12 @@ const db = require('../db');
|
||||
const { streamCompletion } = require('../lib/llm');
|
||||
const router = express.Router();
|
||||
|
||||
const MODEL_PUBLIC_COLS = 'id, name, api_base, provider, is_default_main, is_default_fork, is_default_exam';
|
||||
|
||||
// GET /api/models — list all
|
||||
router.get('/', (req, res) => {
|
||||
try {
|
||||
const rows = db.prepare('SELECT * FROM models ORDER BY id').all();
|
||||
const rows = db.prepare(`SELECT ${MODEL_PUBLIC_COLS} FROM models ORDER BY id`).all();
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
console.error('[models] list error:', err.message);
|
||||
@@ -14,6 +16,24 @@ router.get('/', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/models/:id — get single model (no api_key)
|
||||
router.get('/:id', (req, res) => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) {
|
||||
return res.status(400).json({ error: 'Invalid model id' });
|
||||
}
|
||||
try {
|
||||
const row = db.prepare(`SELECT ${MODEL_PUBLIC_COLS} FROM models WHERE id = ?`).get(id);
|
||||
if (!row) {
|
||||
return res.status(404).json({ error: 'Model not found' });
|
||||
}
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
console.error('[models] get error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/models — create
|
||||
router.post('/', (req, res) => {
|
||||
const { name, api_base, api_key, provider, is_default_main, is_default_fork, is_default_exam } = req.body;
|
||||
@@ -41,7 +61,7 @@ router.post('/', (req, res) => {
|
||||
if (is_default_fork) unsetOtherDefaults(newId, 'is_default_fork');
|
||||
if (is_default_exam) unsetOtherDefaults(newId, 'is_default_exam');
|
||||
|
||||
const row = db.prepare('SELECT * FROM models WHERE id = ?').get(newId);
|
||||
const row = db.prepare(`SELECT ${MODEL_PUBLIC_COLS} FROM models WHERE id = ?`).get(newId);
|
||||
res.status(201).json(row);
|
||||
} catch (err) {
|
||||
console.error('[models] create error:', err.message);
|
||||
@@ -75,10 +95,10 @@ router.put('/:id', (req, res) => {
|
||||
is_default_exam = COALESCE(?, is_default_exam)
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
name ?? null,
|
||||
api_base ?? null,
|
||||
name || null,
|
||||
api_base || null,
|
||||
api_key !== undefined ? (api_key === null ? '' : api_key) : null,
|
||||
provider ?? null,
|
||||
provider || null,
|
||||
is_default_main !== undefined ? (is_default_main ? 1 : 0) : null,
|
||||
is_default_fork !== undefined ? (is_default_fork ? 1 : 0) : null,
|
||||
is_default_exam !== undefined ? (is_default_exam ? 1 : 0) : null,
|
||||
@@ -89,7 +109,7 @@ router.put('/:id', (req, res) => {
|
||||
if (is_default_fork) unsetOtherDefaults(id, 'is_default_fork');
|
||||
if (is_default_exam) unsetOtherDefaults(id, 'is_default_exam');
|
||||
|
||||
const row = db.prepare('SELECT * FROM models WHERE id = ?').get(id);
|
||||
const row = db.prepare(`SELECT ${MODEL_PUBLIC_COLS} FROM models WHERE id = ?`).get(id);
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
console.error('[models] update error:', err.message);
|
||||
|
||||
Reference in New Issue
Block a user