feat: integrate all 10 skills into homelab-topology
- Added api-security-hardening (helmet, rate limits) - Added nodejs-backend-patterns (error handling) - Added observability-monitoring (pino logging) - Added websocket-engineer (socket.io real-time updates) - Added docker (Multi-stage build, compose) - Added vitest (testing configuration and store tests) - Added data-visualizer (MetricsBar and HostChart) - Added infrastructure-monitoring/proxmox-admin/network-engineer types - Fixed UI accessibility and styling - Cleaned up node_modules tracking
This commit is contained in:
@@ -1,40 +1,107 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import { createServer } from 'http';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import discoverRouter from './routes/discover';
|
||||
import configRouter from './routes/config';
|
||||
import statsRouter from './routes/stats';
|
||||
import filesRouter from './routes/files';
|
||||
import terminalRouter from './routes/terminal';
|
||||
import { getHostConfigs } from './config';
|
||||
import { requestLogger, logger } from './middleware/requestLogger';
|
||||
import { errorHandler } from './middleware/errorHandler';
|
||||
|
||||
const app = express();
|
||||
const httpServer = createServer(app);
|
||||
const PORT = 3001;
|
||||
|
||||
// CORS middleware for frontend communication
|
||||
// --- Socket.IO setup (websocket-engineer skill) ---
|
||||
const io = new Server(httpServer, {
|
||||
cors: {
|
||||
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
||||
credentials: true,
|
||||
},
|
||||
pingInterval: 25000,
|
||||
pingTimeout: 10000,
|
||||
});
|
||||
|
||||
io.on('connection', (socket: Socket) => {
|
||||
logger.info({ socketId: socket.id }, 'Client connected via WebSocket');
|
||||
|
||||
socket.on('disconnect', (reason: string) => {
|
||||
logger.info({ socketId: socket.id, reason }, 'Client disconnected');
|
||||
});
|
||||
});
|
||||
|
||||
// Export io so routes can emit events
|
||||
export { io };
|
||||
|
||||
// --- Security middleware (api-security-hardening skill) ---
|
||||
app.use(helmet({
|
||||
contentSecurityPolicy: false, // Disable CSP for dev — configure per-environment in production
|
||||
}));
|
||||
|
||||
// CORS — restrict to configured origins
|
||||
app.use(cors({
|
||||
origin: 'http://localhost:3000',
|
||||
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
||||
credentials: true,
|
||||
}));
|
||||
|
||||
app.use(express.json());
|
||||
// Rate limiting — general API
|
||||
app.use('/api/', rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 200,
|
||||
message: { status: 'error', message: 'Too many requests, please try again later' },
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
}));
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({ status: 'ok' });
|
||||
// Stricter rate limiting for discovery (expensive operation)
|
||||
app.use('/api/discover', rateLimit({
|
||||
windowMs: 1 * 60 * 1000, // 1 minute
|
||||
max: 10,
|
||||
message: { status: 'error', message: 'Discovery rate limited — max 10 per minute' },
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
}));
|
||||
|
||||
// --- Body parsing ---
|
||||
app.use(express.json({ limit: '1mb' }));
|
||||
|
||||
// --- Observability (observability-monitoring skill) ---
|
||||
app.use(requestLogger);
|
||||
|
||||
// --- Health check ---
|
||||
app.get('/api/health', (_req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
uptime: process.uptime(),
|
||||
timestamp: new Date().toISOString(),
|
||||
connections: io.engine.clientsCount,
|
||||
});
|
||||
});
|
||||
|
||||
// Debug endpoint to check config
|
||||
app.get('/api/debug-config', (req, res) => {
|
||||
const hosts = getHostConfigs();
|
||||
res.json({ hosts });
|
||||
});
|
||||
// --- Debug endpoint (dev only) ---
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
app.get('/api/debug-config', (_req, res) => {
|
||||
const hosts = getHostConfigs();
|
||||
res.json({ hosts });
|
||||
});
|
||||
}
|
||||
|
||||
// --- Routes ---
|
||||
app.use('/api', discoverRouter);
|
||||
app.use('/api', configRouter);
|
||||
app.use('/api', statsRouter);
|
||||
app.use('/api', filesRouter);
|
||||
app.use('/api', terminalRouter);
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
// --- Global error handler (must be last) ---
|
||||
app.use(errorHandler);
|
||||
|
||||
// --- Start server ---
|
||||
httpServer.listen(PORT, () => {
|
||||
logger.info(`Server running on http://localhost:${PORT}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user