const http = require("http"); const fs = require("fs"); const path = require("path"); const url = require("url"); const PORT = process.env.PORT || 3000; const DATA_FILE = path.join(__dirname, "data.json"); const EMPTY_DATA = { fuelCards: [], log: [] }; function load() { try { const content = fs.readFileSync(DATA_FILE, "utf8"); return JSON.parse(content); } catch { return EMPTY_DATA; } } function save(data) { fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2)); } function logAction(action, details) { const data = load(); data.log.push({ when: new Date().toISOString(), what: `${action}: ${details}`, who: "user" }); save(data); } const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url, true); const pathname = parsedUrl.pathname.split("?")[0]; const method = req.method; 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 (method === "OPTIONS") { res.writeHead(204); res.end(); return; } if (pathname === "/api/fuel-cards" && method === "GET") { const data = load(); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(data.fuelCards)); return; } if (pathname === "/api/fuel-cards" && method === "POST") { let body = ""; req.on("data", chunk => { body += chunk; }); req.on("end", () => { try { const card = JSON.parse(body); const data = load(); card.id = Date.now(); card.createdAt = new Date().toISOString(); data.fuelCards.push(card); save(data); logAction("Добавлен талон", `водитель: ${card.driver}, авто: ${card.vehicle}, литраж: ${card.liters}л`); res.writeHead(201, { "Content-Type": "application/json" }); res.end(JSON.stringify(card)); } catch (err) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: err.message })); } }); return; } if (pathname === "/api/fuel-cards" && method === "PUT") { let body = ""; req.on("data", chunk => { body += chunk; }); req.on("end", () => { try { const updated = JSON.parse(body); const data = load(); const index = data.fuelCards.findIndex(c => c.id === updated.id); if (index === -1) { res.writeHead(404, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Не найдено" })); return; } const old = data.fuelCards[index]; data.fuelCards[index] = { ...old, ...updated }; save(data); logAction("Изменён талон", `ID: ${updated.id}, водитель: ${updated.driver}`); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(data.fuelCards[index])); } catch (err) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: err.message })); } }); return; } if (pathname.startsWith("/api/fuel-cards/") && method === "DELETE") { const id = parseInt(parsedUrl.pathname.split("/").pop()); const data = load(); const card = data.fuelCards.find(c => c.id === id); if (!card) { res.writeHead(404, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Не найдено" })); return; } data.fuelCards = data.fuelCards.filter(c => c.id !== id); save(data); logAction("Удалён талон", `водитель: ${card.driver}, авто: ${card.vehicle}, литраж: ${card.liters}л`); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ success: true })); return; } if (pathname === "/api/log" && method === "GET") { const data = load(); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(data.log.slice(-50).reverse())); return; } if (pathname === "/api/stats" && method === "GET") { const data = load(); const cards = data.fuelCards; const totalLiters = cards.reduce((sum, c) => sum + (parseFloat(c.liters) || 0), 0); const totalAmount = cards.reduce((sum, c) => sum + (parseFloat(c.amount) || 0), 0); const totalCards = cards.length; const byDriver = {}; cards.forEach(c => { if (!byDriver[c.driver]) { byDriver[c.driver] = { liters: 0, amount: 0, count: 0 }; } byDriver[c.driver].liters += parseFloat(c.liters) || 0; byDriver[c.driver].amount += parseFloat(c.amount) || 0; byDriver[c.driver].count += 1; }); const byVehicle = {}; cards.forEach(c => { if (!byVehicle[c.vehicle]) { byVehicle[c.vehicle] = { liters: 0, amount: 0, count: 0 }; } byVehicle[c.vehicle].liters += parseFloat(c.liters) || 0; byVehicle[c.vehicle].amount += parseFloat(c.amount) || 0; byVehicle[c.vehicle].count += 1; }); const byGasStation = {}; cards.forEach(c => { if (!byGasStation[c.gasStation]) { byGasStation[c.gasStation] = { liters: 0, amount: 0, count: 0 }; } byGasStation[c.gasStation].liters += parseFloat(c.liters) || 0; byGasStation[c.gasStation].amount += parseFloat(c.amount) || 0; byGasStation[c.gasStation].count += 1; }); const stats = { totalLiters, totalAmount, totalCards, byDriver, byVehicle, byGasStation, avgPricePerLiter: totalLiters > 0 ? (totalAmount / totalLiters).toFixed(2) : 0 }; res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(stats)); return; } const filePath = path.join(__dirname, parsedUrl.pathname === "/" ? "index.html" : parsedUrl.pathname); const ext = path.extname(filePath).toLowerCase(); const mimeTypes = { ".html": "text/html", ".css": "text/css", ".js": "application/javascript", ".json": "application/json", ".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml" }; fs.readFile(filePath, (err, content) => { if (err) { if (err.code === "ENOENT") { res.writeHead(404); res.end("Not found"); } else { res.writeHead(500); res.end("Server error"); } return; } res.writeHead(200, { "Content-Type": mimeTypes[ext] || "text/plain" }); res.end(content); }); }); server.listen(PORT, () => { console.log(`Сервер запущен на порту ${PORT}`); });