import http from 'http'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PORT = process.env.PORT || 8080; const DIST_DIR = path.join(__dirname, 'dist'); const CONFIG_DIR = path.join(__dirname, 'data'); const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json'); // Ensure config dir exists if (!fs.existsSync(CONFIG_DIR)) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } const MIME_TYPES = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', }; const server = http.createServer((req, res) => { // CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`); const pathname = parsedUrl.pathname; // API Config GET if (pathname === '/api/config' && req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); if (fs.existsSync(CONFIG_FILE)) { try { const data = fs.readFileSync(CONFIG_FILE, 'utf-8'); res.end(data); return; } catch (err) { // Fallback } } res.end(JSON.stringify({})); return; } // API Config POST if (pathname === '/api/config' && req.method === 'POST') { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { try { const parsed = JSON.parse(body); fs.writeFileSync(CONFIG_FILE, JSON.stringify(parsed, null, 2), 'utf-8'); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true })); } catch (err) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Invalid JSON or write error' })); } }); return; } // --- CATEGORIES CRUD --- const CATEGORIES_FILE = path.join(CONFIG_DIR, 'categories.json'); const readCategories = () => { if (fs.existsSync(CATEGORIES_FILE)) { try { return JSON.parse(fs.readFileSync(CATEGORIES_FILE, 'utf-8')); } catch (e) {} } return []; }; if (pathname === '/api/categories') { if (req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(readCategories())); return; } if (req.method === 'POST' || req.method === 'PUT') { let body = ''; req.on('data', chunk => body += chunk.toString()); req.on('end', () => { try { const category = JSON.parse(body); let categories = readCategories(); if (req.method === 'POST') { category.id = Date.now().toString(); categories.push(category); } else { const index = categories.findIndex(c => c.id === category.id); if (index !== -1) categories[index] = { ...categories[index], ...category }; } fs.writeFileSync(CATEGORIES_FILE, JSON.stringify(categories, null, 2)); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(category)); } catch (err) { res.writeHead(400); res.end(JSON.stringify({ error: 'Invalid payload' })); } }); return; } if (req.method === 'DELETE') { const id = parsedUrl.searchParams.get('id'); if (id) { let categories = readCategories(); categories = categories.filter(c => c.id !== id); fs.writeFileSync(CATEGORIES_FILE, JSON.stringify(categories, null, 2)); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true })); } else { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing id' })); } return; } } // --- END CATEGORIES CRUD --- // --- STANDARDS CRUD --- const STANDARDS_FILE = path.join(CONFIG_DIR, 'standards.json'); const readStandards = () => { if (fs.existsSync(STANDARDS_FILE)) { try { return JSON.parse(fs.readFileSync(STANDARDS_FILE, 'utf-8')); } catch (e) {} } return []; }; if (pathname === '/api/standards') { if (req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); const allStandards = readStandards(); const catId = parsedUrl.searchParams.get('categoryId'); if (catId) { res.end(JSON.stringify(allStandards.filter(s => s.categoria_id === catId))); } else { res.end(JSON.stringify(allStandards)); } return; } if (req.method === 'POST' || req.method === 'PUT') { let body = ''; req.on('data', chunk => body += chunk.toString()); req.on('end', () => { try { const standard = JSON.parse(body); // Sanitização numérica para parametros_controle if (standard.parametros_controle) { for (const key of Object.keys(standard.parametros_controle)) { const grupo = standard.parametros_controle[key]; if (typeof grupo === 'object' && grupo !== null) { for (const prop of Object.keys(grupo)) { const val = grupo[prop]; if (val !== null && val !== undefined && val !== '') { const num = parseFloat(val); if (!isNaN(num)) { grupo[prop] = num; } } } } } } let standards = readStandards(); if (req.method === 'POST') { standard.id = Date.now().toString(); standards.push(standard); } else { const index = standards.findIndex(s => s.id === standard.id); if (index !== -1) standards[index] = { ...standards[index], ...standard }; } fs.writeFileSync(STANDARDS_FILE, JSON.stringify(standards, null, 2)); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(standard)); } catch (err) { res.writeHead(400); res.end(JSON.stringify({ error: 'Invalid payload' })); } }); return; } if (req.method === 'DELETE') { const id = parsedUrl.searchParams.get('id'); if (id) { let standards = readStandards(); standards = standards.filter(s => s.id !== id); fs.writeFileSync(STANDARDS_FILE, JSON.stringify(standards, null, 2)); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true })); } else { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing id' })); } return; } } // --- END STANDARDS CRUD --- // Serve static files let filePath = path.join(DIST_DIR, req.url === '/' ? 'index.html' : req.url); // If path doesn't exist, fallback to index.html (SPA routing) if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) { filePath = path.join(DIST_DIR, 'index.html'); } const ext = path.extname(filePath); const contentType = MIME_TYPES[ext] || 'application/octet-stream'; fs.readFile(filePath, (err, content) => { if (err) { res.writeHead(500); res.end('Server Error'); } else { res.writeHead(200, { 'Content-Type': contentType }); res.end(content); } }); }); server.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });