1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
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);
});
|