nuzhno-sozdat-resurs-sayt-dl/server.js

173 lines
7.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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));