const express = require('express'); const path = require('path'); const fs = require('fs'); const { WebSocketServer } = require('ws'); const chokidar = require('chokidar'); const PORT = process.env.PORT || 3030; const SCHEMAS_ROOT = path.resolve(__dirname, '..'); const app = express(); app.use(express.static(path.join(__dirname, 'public'))); function scanSchemas() { const schemas = []; function walk(dir, category) { if (!fs.existsSync(dir)) return; for (const file of fs.readdirSync(dir)) { const fullPath = path.join(dir, file); if (fs.statSync(fullPath).isDirectory()) { walk(fullPath, file); } else if (file.endsWith('.mmd')) { const relativePath = path.relative(SCHEMAS_ROOT, fullPath).replace(/\\/g, '/'); schemas.push({ name: file.replace('.mmd', ''), category: category || 'root', path: relativePath, fullPath }); } } } walk(path.join(SCHEMAS_ROOT, 'macro'), 'macro'); walk(path.join(SCHEMAS_ROOT, 'micro'), null); return schemas; } app.get('/api/schemas', (req, res) => { const schemas = scanSchemas(); const grouped = {}; for (const s of schemas) { if (!grouped[s.category]) grouped[s.category] = []; grouped[s.category].push({ name: s.name, path: s.path }); } res.json(grouped); }); app.get('/api/schema/:encodedPath', (req, res) => { const schemaPath = decodeURIComponent(req.params.encodedPath); const fullPath = path.join(SCHEMAS_ROOT, schemaPath); const resolved = path.resolve(fullPath); if (!resolved.startsWith(SCHEMAS_ROOT)) { return res.status(403).json({ error: 'Acces interdit' }); } if (!fs.existsSync(resolved)) { return res.status(404).json({ error: 'Schema introuvable' }); } const content = fs.readFileSync(resolved, 'utf-8'); res.json({ path: schemaPath, content }); }); const server = app.listen(PORT, () => { console.log(`\n Araucaria Schemas Viewer`); console.log(` -> http://localhost:${PORT}\n`); console.log(` Schemas root: ${SCHEMAS_ROOT}`); console.log(` Watching for .mmd changes...\n`); }); const wss = new WebSocketServer({ server }); const watcher = chokidar.watch( [path.join(SCHEMAS_ROOT, 'macro'), path.join(SCHEMAS_ROOT, 'micro')], { ignoreInitial: true, persistent: true } ); watcher.on('all', (event, filePath) => { if (!filePath.endsWith('.mmd')) return; const relativePath = path.relative(SCHEMAS_ROOT, filePath).replace(/\\/g, '/'); console.log(` [${event}] ${relativePath}`); const message = JSON.stringify({ type: 'reload', event, path: relativePath }); for (const client of wss.clients) { if (client.readyState === 1) client.send(message); } }); process.on('SIGINT', () => { watcher.close(); server.close(); process.exit(0); });