hr_system/server.js

136 lines
5.6 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");
const OWNER_PIN = process.env.OWNER_PIN || "drakon2027";
function load() {
try { return JSON.parse(fs.readFileSync(DATA, "utf8")); }
catch (_) { return { regs: [], wishes: [], log: [] }; }
}
function save(d) {
fs.writeFileSync(DATA, JSON.stringify(d, null, 2));
}
function body(req) {
return new Promise((resolve) => {
let s = "";
req.on("data", (c) => (s += c));
req.on("end", () => { try { resolve(JSON.parse(s || "{}")); } catch (_) { resolve({}); } });
});
}
function json(res, code, obj) {
res.writeHead(code, { "Content-Type": "application/json" });
res.end(JSON.stringify(obj));
}
function totalGuests(d) {
return d.regs.reduce((s, r) => s + (parseInt(r.guests) || 1), 0);
}
function pinOk(b) { return b && b.pin === OWNER_PIN; }
http.createServer(async (req, res) => {
const url = req.url.split("?")[0];
// Публичный: зарегистрироваться на праздник
if (req.method === "POST" && url === "/api/regs") {
const b = await body(req);
const d = load();
if (!b.name || !b.phone) return json(res, 400, { ok: false, error: "Укажи имя и телефон" });
const rec = {
id: Date.now(),
name: String(b.name).slice(0, 100),
phone: String(b.phone).slice(0, 30),
guests: Math.max(1, parseInt(b.guests) || 1),
note: String(b.note || "").slice(0, 300),
date: new Date().toISOString().slice(0, 10)
};
d.regs.push(rec);
d.log.push({ when: new Date().toISOString(), what: "Регистрация: " + rec.name });
save(d);
return json(res, 200, { ok: true, total: totalGuests(d) });
}
// Публичный: список подарков
if (req.method === "GET" && url === "/api/wish") {
return json(res, 200, { ok: true, data: load().wishes });
}
// Публичный: забронировать подарок
if (req.method === "POST" && url === "/api/wish/reserve") {
const b = await body(req);
const d = load();
const w = d.wishes.find((x) => x.id === b.id);
if (!w) return json(res, 404, { ok: false, error: "Подарок не найден" });
if (w.reservedBy) return json(res, 400, { ok: false, error: "Подарок уже забронирован" });
if (!b.guest) return json(res, 400, { ok: false, error: "Напиши, кто дарит" });
w.reservedBy = String(b.guest).slice(0, 100);
d.log.push({ when: new Date().toISOString(), what: "Подарок «" + w.title + "» забронирован: " + w.reservedBy });
save(d);
return json(res, 200, { ok: true, data: d.wishes });
}
// Публичный: снять бронь (на случай ошибки гостя)
if (req.method === "POST" && url === "/api/wish/unreserve") {
const b = await body(req);
const d = load();
const w = d.wishes.find((x) => x.id === b.id);
if (!w) return json(res, 404, { ok: false, error: "Подарок не найден" });
if (!w.reservedBy) return json(res, 400, { ok: false, error: "Брони нет" });
const who = w.reservedBy;
w.reservedBy = "";
d.log.push({ when: new Date().toISOString(), what: "Бронь снята с «" + w.title + "» (" + who + ")" });
save(d);
return json(res, 200, { ok: true, data: d.wishes });
}
// Админ: список гостей
if (req.method === "POST" && url === "/api/admin/regs") {
const b = await body(req);
if (!pinOk(b)) return json(res, 403, { ok: false, error: "Неверный код" });
const d = load();
return json(res, 200, { ok: true, data: d.regs, total: totalGuests(d) });
}
// Админ: добавить подарок
if (req.method === "POST" && url === "/api/admin/wish") {
const b = await body(req);
if (!pinOk(b)) return json(res, 403, { ok: false, error: "Неверный код" });
const d = load();
if (!b.title) return json(res, 400, { ok: false, error: "Название обязательно" });
d.wishes.push({
id: Date.now(),
title: String(b.title).slice(0, 100),
emoji: String(b.emoji || "🎁").slice(0, 4),
reservedBy: ""
});
d.log.push({ when: new Date().toISOString(), what: "Добавлен подарок: " + b.title });
save(d);
return json(res, 200, { ok: true, data: d.wishes });
}
// Админ: удалить подарок
if (req.method === "POST" && url === "/api/admin/wish/del") {
const b = await body(req);
if (!pinOk(b)) return json(res, 403, { ok: false, error: "Неверный код" });
const d = load();
const w = d.wishes.find((x) => x.id === b.id);
d.wishes = d.wishes.filter((x) => x.id !== b.id);
if (w) d.log.push({ when: new Date().toISOString(), what: "Удалён подарок: " + w.title });
save(d);
return json(res, 200, { ok: true, data: d.wishes });
}
// Статика: index.html / style.css / script.js / data.json
let file = url === "/" ? "/index.html" : url;
const full = path.join(__dirname, file);
if (full.startsWith(__dirname) && fs.existsSync(full) && fs.statSync(full).isFile()) {
const ext = path.extname(full);
const type = ext === ".css" ? "text/css" : ext === ".js" ? "application/javascript" : ext === ".json" ? "application/json" : "text/html";
res.writeHead(200, { "Content-Type": type + "; charset=utf-8" });
return res.end(fs.readFileSync(full));
}
json(res, 404, { ok: false, error: "Not found" });
}).listen(PORT, () => console.log("Сервер на порту " + PORT));