// ============================================================ // MERMAID DIAGRAMS ROUTES — per-user clinical pathway diagrams. // Pure CRUD, auth-gated. Title/source/notes encrypted at rest // using the same crypto helper as personal_notes. // ============================================================ var express = require('express'); var router = express.Router(); var db = require('../db/database'); var { authMiddleware } = require('../middleware/auth'); var logger = require('../utils/logger'); var cryptoUtil = require('../utils/crypto'); router.use(authMiddleware); var MAX_TITLE = 200; var MAX_SOURCE = 50000; var MAX_NOTES = 10000; var MAX_DIAGRAMS_PER_USER = 200; function decryptRow(row) { if (!row) return row; try { row.title = cryptoUtil.decryptString(row.title); } catch (e) {} try { row.source = cryptoUtil.decryptString(row.source); } catch (e) {} try { row.notes = cryptoUtil.decryptString(row.notes); } catch (e) {} return row; } function clip(s, n) { return String(s == null ? '' : s).trim().substring(0, n); } router.get('/diagrams', async function (req, res) { try { var rows = await db.all( 'SELECT id, title, source, notes, created_at, updated_at FROM mermaid_diagrams WHERE user_id = $1 ORDER BY updated_at DESC', [req.user.id] ); rows.forEach(decryptRow); res.json({ success: true, diagrams: rows }); } catch (err) { console.error('[diagrams] list failed:', err.message); res.status(500).json({ error: 'Request failed' }); } }); router.get('/diagrams/:id', async function (req, res) { try { var row = await db.get( 'SELECT id, title, source, notes, created_at, updated_at FROM mermaid_diagrams WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id] ); if (!row) return res.status(404).json({ error: 'Diagram not found' }); decryptRow(row); res.json({ success: true, diagram: row }); } catch (err) { res.status(500).json({ error: 'Request failed' }); } }); router.post('/diagrams', async function (req, res) { try { var title = clip(req.body.title, MAX_TITLE) || 'Untitled diagram'; var source = clip(req.body.source, MAX_SOURCE); var notes = clip(req.body.notes, MAX_NOTES); var count = await db.get('SELECT COUNT(*) as cnt FROM mermaid_diagrams WHERE user_id = $1', [req.user.id]); if (count && Number(count.cnt) >= MAX_DIAGRAMS_PER_USER) { return res.status(400).json({ error: 'Maximum ' + MAX_DIAGRAMS_PER_USER + ' diagrams per user' }); } var result = await db.run( 'INSERT INTO mermaid_diagrams (user_id, title, source, notes) VALUES ($1, $2, $3, $4) RETURNING id', [req.user.id, cryptoUtil.encryptString(title), cryptoUtil.encryptString(source), cryptoUtil.encryptString(notes)] ); logger.audit(req.user.id, 'diagram_create', 'Created mermaid diagram', req, { category: 'clinical' }); res.json({ success: true, id: result.lastInsertRowid }); } catch (err) { console.error('[diagrams] create failed:', err.message); res.status(500).json({ error: 'Request failed' }); } }); router.put('/diagrams/:id', async function (req, res) { try { var existing = await db.get('SELECT id FROM mermaid_diagrams WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id]); if (!existing) return res.status(404).json({ error: 'Diagram not found' }); var title = clip(req.body.title, MAX_TITLE) || 'Untitled diagram'; var source = clip(req.body.source, MAX_SOURCE); var notes = clip(req.body.notes, MAX_NOTES); await db.run( 'UPDATE mermaid_diagrams SET title = $1, source = $2, notes = $3, updated_at = NOW() WHERE id = $4 AND user_id = $5', [cryptoUtil.encryptString(title), cryptoUtil.encryptString(source), cryptoUtil.encryptString(notes), req.params.id, req.user.id] ); res.json({ success: true }); } catch (err) { console.error('[diagrams] update failed:', err.message); res.status(500).json({ error: 'Request failed' }); } }); router.delete('/diagrams/:id', async function (req, res) { try { await db.run('DELETE FROM mermaid_diagrams WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id]); logger.audit(req.user.id, 'diagram_delete', 'Deleted mermaid diagram', req, { category: 'clinical' }); res.json({ success: true }); } catch (err) { res.status(500).json({ error: 'Request failed' }); } }); module.exports = router;