const http = require("http"); const fs = require("fs"); const path = require("path"); const PORT = process.env.PORT || 3000; const DATA = path.join(__dirname, "data.json"); function readData() { try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { return { items: [], log: [], users: [] }; } } function writeData(data) { fs.writeFileSync(DATA, JSON.stringify(data, null, 2)); } function parseBody(req) { return new Promise((resolve) => { let body = ""; req.on("data", chunk => body += chunk); req.on("end", () => { try { resolve(JSON.parse(body || "{}")); } catch { resolve({}); } }); }); } http.createServer(async (req, res) => { const url = req.url.split("?")[0]; const method = req.method; // ==================== API ПОЛЬЗОВАТЕЛЕЙ ==================== if (url === "/api/users" && method === "GET") { const data = readData(); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify(data.users.map(u => ({ login: u.login, role: u.role, createdAt: u.createdAt })))); } if (url === "/api/users" && method === "POST") { const body = await parseBody(req); const data = readData(); if (data.users.find(u => u.login === body.login)) { res.writeHead(400, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ error: "Пользователь существует" })); } data.users.push({ login: body.login, password: body.password, role: body.role || "user", createdAt: new Date().toISOString() }); writeData(data); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ ok: true })); } if (url.startsWith("/api/users/") && method === "DELETE") { const login = url.split("/")[3]; const data = readData(); const idx = data.users.findIndex(u => u.login === login); if (idx === -1 || (data.users[idx].role === "admin" && data.users.filter(u => u.role === "admin").length <= 1)) { res.writeHead(400, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ error: "Нельзя удалить" })); } data.users.splice(idx, 1); writeData(data); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ ok: true })); } // ==================== API ЗАПИСЕЙ ==================== if (url === "/api/items" && method === "GET") { const data = readData(); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify(data.items)); } if (url === "/api/items" && method === "POST") { const body = await parseBody(req); const data = readData(); const newItem = { id: Date.now(), ...body, createdAt: new Date().toISOString().slice(0, 10), updatedAt: new Date().toISOString().slice(0, 10) }; delete newItem.id; data.items.push(newItem); data.log.push({ id: Date.now(), timestamp: new Date().toISOString(), action: "создана", itemId: newItem.id, itemName: newItem.name, user: 'system', changes: {} }); writeData(data); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ ok: true })); } if (url.startsWith("/api/items/") && method === "PUT") { const id = parseInt(url.split("/")[3]); const body = await parseBody(req); const data = readData(); const idx = data.items.findIndex(i => i.id === id); if (idx === -1) { res.writeHead(404, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ error: "Не найдено" })); } const changes = {}; Object.keys(body).forEach(k => { if (k !== 'id' && data.items[idx][k] !== body[k]) changes[k] = `${data.items[idx][k]} → ${body[k]}`; }); data.items[idx] = { ...data.items[idx], ...body, updatedAt: new Date().toISOString().slice(0, 10) }; data.log.push({ id: Date.now(), timestamp: new Date().toISOString(), action: "изменена", itemId: id, itemName: data.items[idx].name, user: 'system', changes }); writeData(data); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ ok: true })); } if (url.startsWith("/api/items/") && method === "DELETE") { const id = parseInt(url.split("/")[3]); const data = readData(); const idx = data.items.findIndex(i => i.id === id); if (idx === -1) { res.writeHead(404, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ error: "Не найдено" })); } const removed = data.items.splice(idx, 1)[0]; data.log.push({ id: Date.now(), timestamp: new Date().toISOString(), action: "удалена", itemId: id, itemName: removed.name, user: 'system', changes: {} }); writeData(data); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ ok: true })); } if (url === "/api/log" && method === "GET") { const data = readData(); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify(data.log.slice(-100).reverse())); } if (url === "/api/stats" && method === "GET") { const data = readData(); const today = new Date().toISOString().slice(0, 10); const in30Days = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10); const stats = { total: data.items.length, byStatus: { "в эксплуатации": 0, "на складе": 0, "в ремонте": 0, "списано": 0 }, requireCheck: 0, overdue: 0 }; data.items.forEach(i => { if (stats.byStatus[i.status] !== undefined) stats.byStatus[i.status]++; if (i.nextCheckDate && i.nextCheckDate < today) stats.overdue++; else if (i.nextCheckDate && i.nextCheckDate <= in30Days) stats.requireCheck++; }); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify(stats)); } // ==================== СТАТИКА ==================== if (url === "/login.html" || url === "/style.css" || url.startsWith("/design-system/")) { const file = path.join(__dirname, url.split("?")[0]); if (fs.existsSync(file)) { const ext = path.extname(file); const type = ext === ".css" ? "text/css" : "text/html"; res.writeHead(200, { "Content-Type": type + "; charset=utf-8" }); return res.end(fs.readFileSync(file)); } } if (url === "/" || url === "/index.html") { const file = path.join(__dirname, user ? "/index.html" : "/login.html"); if (fs.existsSync(file)) { res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); return res.end(fs.readFileSync(file)); } } if (url === "/script.js") { const file = path.join(__dirname, "/script.js"); if (fs.existsSync(file)) { res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8" }); return res.end(fs.readFileSync(file)); } } res.writeHead(404); res.end("Not found"); }).listen(PORT, () => console.log("Сервер на порту " + PORT));