Initial commit — Mek-Tech AI Consultancy Framework v2.0
This commit is contained in:
+257
@@ -0,0 +1,257 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { getDb } = require('../db');
|
||||
const { parseDiagramJson, summarizeDiagram, rackDevicesToNodes, DIAGRAM_TYPE_LABELS, buildStudioDemo, getWorkspaceTab } = require('../lib/rack-diagram');
|
||||
|
||||
function loadRackWorkspace(db, rackId) {
|
||||
const rack = db.prepare('SELECT r.*, c.name as client_name FROM racks r JOIN clients c ON c.id = r.client_id WHERE r.id = ?').get(rackId);
|
||||
if (!rack) return null;
|
||||
const rackDevices = db.prepare('SELECT * FROM rack_devices WHERE rack_id = ? ORDER BY position_u ASC').all(rackId);
|
||||
const linkedDiagrams = db.prepare('SELECT id, name, diagram_type, updated_at FROM data_diagrams WHERE rack_id = ? ORDER BY updated_at DESC').all(rackId);
|
||||
const flowDiagram = linkedDiagrams.find(d => d.diagram_type === 'application_flow') || null;
|
||||
const landscapeDiagram = linkedDiagrams.find(d => d.diagram_type === 'application_landscape') || null;
|
||||
return { rack, rackDevices, linkedDiagrams, flowDiagram, landscapeDiagram };
|
||||
}
|
||||
|
||||
function renderArchEditor(res, opts) {
|
||||
const diagram = opts.diagram || null;
|
||||
const rack = opts.rack;
|
||||
const workspaceTab = opts.workspaceTab || (diagram ? getWorkspaceTab(diagram.diagram_type) : opts.defaultDiagramType === 'application_landscape' ? 'landscape' : 'flow');
|
||||
res.render('arch-editor', {
|
||||
diagram,
|
||||
clients: opts.clients,
|
||||
clientId: opts.clientId || (rack ? rack.client_id : null),
|
||||
rack,
|
||||
rackDevices: opts.rackDevices || [],
|
||||
linkedDiagrams: opts.linkedDiagrams || [],
|
||||
flowDiagram: opts.flowDiagram || null,
|
||||
landscapeDiagram: opts.landscapeDiagram || null,
|
||||
defaultDiagramType: opts.defaultDiagramType || null,
|
||||
workspaceTab,
|
||||
embed: !!opts.embed,
|
||||
error: opts.error || null
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
const db = getDb();
|
||||
const racks = db.prepare(`
|
||||
SELECT r.*, c.name as client_name,
|
||||
(SELECT COUNT(*) FROM rack_devices d WHERE d.rack_id = r.id) as device_count
|
||||
FROM racks r JOIN clients c ON c.id = r.client_id ORDER BY r.updated_at DESC
|
||||
`).all();
|
||||
res.render('racks-list', { racks });
|
||||
});
|
||||
|
||||
router.get('/new', (req, res) => {
|
||||
const db = getDb();
|
||||
const clients = db.prepare('SELECT id, name FROM clients ORDER BY name').all();
|
||||
res.render('racks-form', { rack: null, clients, selected: req.query.client_id ? parseInt(req.query.client_id, 10) : null, error: null });
|
||||
});
|
||||
|
||||
router.get('/new/:clientId', (req, res) => {
|
||||
const db = getDb();
|
||||
const clients = db.prepare('SELECT id, name FROM clients ORDER BY name').all();
|
||||
res.render('racks-form', { rack: null, clients, selected: parseInt(req.params.clientId), error: null });
|
||||
});
|
||||
|
||||
router.post('/new', (req, res) => {
|
||||
const db = getDb();
|
||||
const { client_id, name, location, datacenter, total_units, notes } = req.body;
|
||||
if (!name || !client_id) return res.redirect('/racks');
|
||||
db.prepare('INSERT INTO racks (client_id, name, location, datacenter, total_units, notes) VALUES (?,?,?,?,?,?)')
|
||||
.run(client_id, name, location, datacenter, total_units || 42, notes);
|
||||
res.redirect('/racks');
|
||||
});
|
||||
|
||||
router.get('/:id', (req, res) => {
|
||||
const db = getDb();
|
||||
const ws = loadRackWorkspace(db, req.params.id);
|
||||
if (!ws) return res.redirect('/racks');
|
||||
const hardwareLibrary = require('../data/hardware-library');
|
||||
if (req.query.embed === '1') {
|
||||
return res.render('racks-view', {
|
||||
...ws,
|
||||
devices: ws.rackDevices,
|
||||
hardwareLibrary,
|
||||
embed: true,
|
||||
workspaceTab: 'rack'
|
||||
});
|
||||
}
|
||||
res.render('racks-view', {
|
||||
rack: ws.rack,
|
||||
devices: ws.rackDevices,
|
||||
hardwareLibrary,
|
||||
linkedDiagrams: ws.linkedDiagrams,
|
||||
flowDiagram: ws.flowDiagram,
|
||||
landscapeDiagram: ws.landscapeDiagram,
|
||||
workspaceTab: 'rack',
|
||||
embed: false
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/:id/edit', (req, res) => {
|
||||
const db = getDb();
|
||||
const rack = db.prepare('SELECT * FROM racks WHERE id = ?').get(req.params.id);
|
||||
if (!rack) return res.redirect('/racks');
|
||||
const clients = db.prepare('SELECT id, name FROM clients ORDER BY name').all();
|
||||
res.render('racks-form', { rack, clients, selected: rack.client_id, error: null });
|
||||
});
|
||||
|
||||
router.post('/:id/edit', (req, res) => {
|
||||
const db = getDb();
|
||||
const { name, location, datacenter, total_units, notes } = req.body;
|
||||
db.prepare("UPDATE racks SET name=?, location=?, datacenter=?, total_units=?, notes=?, updated_at=datetime('now') WHERE id=?")
|
||||
.run(name, location, datacenter, total_units, notes, req.params.id);
|
||||
res.redirect('/racks/' + req.params.id);
|
||||
});
|
||||
|
||||
router.post('/:id/delete', (req, res) => {
|
||||
const db = getDb();
|
||||
db.prepare('DELETE FROM racks WHERE id = ?').run(req.params.id);
|
||||
res.redirect('/racks');
|
||||
});
|
||||
|
||||
router.post('/:id/devices', (req, res) => {
|
||||
const db = getDb();
|
||||
const { name, device_type, model, manufacturer, position_u, height_u, specs, mgmt_ip, notes, status } = req.body;
|
||||
if (!name) return res.redirect('/racks/' + req.params.id);
|
||||
db.prepare('INSERT INTO rack_devices (rack_id, name, device_type, model, manufacturer, position_u, height_u, specs, mgmt_ip, notes, status) VALUES (?,?,?,?,?,?,?,?,?,?,?)')
|
||||
.run(req.params.id, name, device_type || 'server', model, manufacturer, parseInt(position_u), parseInt(height_u) || 1, specs, mgmt_ip, notes, status || 'active');
|
||||
res.redirect('/racks/' + req.params.id);
|
||||
});
|
||||
|
||||
router.post('/devices/:deviceId/edit', (req, res) => {
|
||||
const db = getDb();
|
||||
const { name, device_type, model, manufacturer, position_u, height_u, specs, mgmt_ip, notes, status } = req.body;
|
||||
const device = db.prepare('SELECT rack_id FROM rack_devices WHERE id = ?').get(req.params.deviceId);
|
||||
if (!device) return res.redirect('/racks');
|
||||
db.prepare("UPDATE rack_devices SET name=?, device_type=?, model=?, manufacturer=?, position_u=?, height_u=?, specs=?, mgmt_ip=?, notes=?, status=?, updated_at=datetime('now') WHERE id=?")
|
||||
.run(name, device_type, model, manufacturer, position_u, height_u, specs, mgmt_ip, notes, status, req.params.deviceId);
|
||||
res.redirect('/racks/' + device.rack_id);
|
||||
});
|
||||
|
||||
router.post('/devices/:deviceId/delete', (req, res) => {
|
||||
const db = getDb();
|
||||
const device = db.prepare('SELECT rack_id FROM rack_devices WHERE id = ?').get(req.params.deviceId);
|
||||
if (device) {
|
||||
db.prepare('DELETE FROM rack_devices WHERE id = ?').run(req.params.deviceId);
|
||||
res.redirect('/racks/' + device.rack_id);
|
||||
} else {
|
||||
res.redirect('/racks');
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:id/flow/new', (req, res) => {
|
||||
const db = getDb();
|
||||
const ws = loadRackWorkspace(db, req.params.id);
|
||||
if (!ws) return res.redirect('/racks');
|
||||
const clients = db.prepare('SELECT id, name FROM clients ORDER BY name').all();
|
||||
renderArchEditor(res, {
|
||||
...ws,
|
||||
clients,
|
||||
diagram: null,
|
||||
defaultDiagramType: 'application_flow',
|
||||
workspaceTab: 'flow',
|
||||
embed: req.query.embed === '1'
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/:id/landscape/new', (req, res) => {
|
||||
const db = getDb();
|
||||
const ws = loadRackWorkspace(db, req.params.id);
|
||||
if (!ws) return res.redirect('/racks');
|
||||
const clients = db.prepare('SELECT id, name FROM clients ORDER BY name').all();
|
||||
renderArchEditor(res, {
|
||||
...ws,
|
||||
clients,
|
||||
diagram: null,
|
||||
defaultDiagramType: 'application_landscape',
|
||||
workspaceTab: 'landscape',
|
||||
embed: req.query.embed === '1'
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/:id/diagram/:diagramId', (req, res) => {
|
||||
const db = getDb();
|
||||
const ws = loadRackWorkspace(db, req.params.id);
|
||||
if (!ws) return res.redirect('/racks');
|
||||
const diagram = db.prepare('SELECT d.*, c.name as client_name FROM data_diagrams d JOIN clients c ON c.id = d.client_id WHERE d.id = ? AND d.rack_id = ?').get(req.params.diagramId, req.params.id);
|
||||
if (!diagram) return res.redirect('/racks/' + req.params.id);
|
||||
const clients = db.prepare('SELECT id, name FROM clients ORDER BY name').all();
|
||||
renderArchEditor(res, {
|
||||
...ws,
|
||||
clients,
|
||||
diagram,
|
||||
workspaceTab: getWorkspaceTab(diagram.diagram_type),
|
||||
embed: req.query.embed === '1'
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/:id/studio', (req, res) => {
|
||||
const db = getDb();
|
||||
const ws = loadRackWorkspace(db, req.params.id);
|
||||
if (!ws) return res.redirect('/racks');
|
||||
res.render('rack-studio', { ...ws, workspaceTab: 'studio' });
|
||||
});
|
||||
|
||||
router.post('/:id/studio/demo', (req, res) => {
|
||||
const db = getDb();
|
||||
const ws = loadRackWorkspace(db, req.params.id);
|
||||
if (!ws) return res.status(404).json({ error: 'Rack not found' });
|
||||
const demo = buildStudioDemo(ws.rack, ws.rackDevices);
|
||||
let flowId = ws.flowDiagram ? ws.flowDiagram.id : null;
|
||||
let landscapeId = ws.landscapeDiagram ? ws.landscapeDiagram.id : null;
|
||||
if (flowId) {
|
||||
db.prepare("UPDATE data_diagrams SET name=?, nodes=?, edges=?, updated_at=datetime('now') WHERE id=?")
|
||||
.run(demo.flow.name, demo.flow.nodes, demo.flow.edges, flowId);
|
||||
} else {
|
||||
const r = db.prepare('INSERT INTO data_diagrams (client_id, name, description, diagram_type, nodes, edges, rack_id) VALUES (?,?,?,?,?,?,?)')
|
||||
.run(ws.rack.client_id, demo.flow.name, 'Studio demo flow', 'application_flow', demo.flow.nodes, demo.flow.edges, ws.rack.id);
|
||||
flowId = r.lastInsertRowid;
|
||||
}
|
||||
if (landscapeId) {
|
||||
db.prepare("UPDATE data_diagrams SET name=?, nodes=?, edges=?, updated_at=datetime('now') WHERE id=?")
|
||||
.run(demo.landscape.name, demo.landscape.nodes, demo.landscape.edges, landscapeId);
|
||||
} else {
|
||||
const r = db.prepare('INSERT INTO data_diagrams (client_id, name, description, diagram_type, nodes, edges, rack_id) VALUES (?,?,?,?,?,?,?)')
|
||||
.run(ws.rack.client_id, demo.landscape.name, 'Studio demo landschap', 'application_landscape', demo.landscape.nodes, demo.landscape.edges, ws.rack.id);
|
||||
landscapeId = r.lastInsertRowid;
|
||||
}
|
||||
res.json({ success: true, flowId, landscapeId, redirect: '/racks/' + ws.rack.id + '/studio' });
|
||||
});
|
||||
|
||||
router.get('/:id/devices.json', (req, res) => {
|
||||
const db = getDb();
|
||||
const rack = db.prepare('SELECT * FROM racks WHERE id = ?').get(req.params.id);
|
||||
if (!rack) return res.status(404).json({ error: 'Rack not found' });
|
||||
const devices = db.prepare('SELECT * FROM rack_devices WHERE rack_id = ? ORDER BY position_u ASC').all(req.params.id);
|
||||
res.json({ rack, devices, nodes: rackDevicesToNodes(devices) });
|
||||
});
|
||||
|
||||
router.get('/:id/report', (req, res) => {
|
||||
const db = getDb();
|
||||
const rack = db.prepare('SELECT r.*, c.name as client_name FROM racks r JOIN clients c ON c.id = r.client_id WHERE r.id = ?').get(req.params.id);
|
||||
if (!rack) return res.redirect('/racks');
|
||||
const devices = db.prepare('SELECT * FROM rack_devices WHERE rack_id = ? ORDER BY position_u ASC').all(req.params.id);
|
||||
const diagrams = db.prepare(`
|
||||
SELECT d.*, c.name as client_name FROM data_diagrams d
|
||||
JOIN clients c ON c.id = d.client_id WHERE d.rack_id = ?
|
||||
ORDER BY CASE d.diagram_type WHEN 'application_flow' THEN 0 WHEN 'application_landscape' THEN 1 ELSE 2 END, d.updated_at DESC
|
||||
`).all(req.params.id);
|
||||
const diagramSummaries = diagrams.map(d => ({
|
||||
...d,
|
||||
...summarizeDiagram(d),
|
||||
typeLabel: DIAGRAM_TYPE_LABELS[d.diagram_type] || d.diagram_type
|
||||
}));
|
||||
const usedU = devices.reduce((s, d) => s + (d.height_u || 1), 0);
|
||||
res.render('rack-infra-report', {
|
||||
rack,
|
||||
devices,
|
||||
diagrams: diagramSummaries,
|
||||
usedU,
|
||||
typeLabels: DIAGRAM_TYPE_LABELS
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user