kak-ya-mogu-skopirovat-chuzh/server.js
2026-09-10 10:43:35 +00:00

246 lines
10 KiB
JavaScript
Raw 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 USERS_FILE = path.join(__dirname, "users.json");
// Простая авторизация по токенам
function loadUsers() {
try { return JSON.parse(fs.readFileSync(USERS_FILE, "utf8")); } catch (_) {
return { users: [{ id: "admin", name: "Админ", token: "tok_" + Date.now() }] };
}
}
function saveUsers(users) {
fs.writeFileSync(USERS_FILE, JSON.stringify(users, null, 2));
}
function readData() {
try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { return { events: [] }; }
}
function writeData(data) {
fs.writeFileSync(DATA, JSON.stringify(data, 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 checkAuth(req) {
const token = req.headers.authorization?.replace("Bearer ", "");
if (!token) return null;
const users = loadUsers();
return users.users.find(u => u.token === token) || null;
}
http.createServer(async (req, res) => {
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, Authorization");
if (req.method === "OPTIONS") {
res.writeHead(200);
return res.end();
}
const urlPath = req.url.split("?")[0];
const queryString = req.url.split("?")[1] || "";
const query = new URLSearchParams(queryString);
// API: Авторизация (GET и POST для работы через proxy)
if ((req.method === "POST" || req.method === "GET") && urlPath === "/service/login") {
const username = query.get("username") || (await body(req)).username;
const password = query.get("password") || (await body(req)).password;
const users = loadUsers();
const user = users.users.find(u => u.id === username && u.password === password);
if (user) {
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true, token: user.token, name: user.name }));
}
res.writeHead(401, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Неверный логин или пароль" }));
}
// API: Получить текущего пользователя
if (req.method === "GET" && urlPath === "/service/me") {
const user = checkAuth(req);
if (user) {
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ id: user.id, name: user.name, isAdmin: user.id === 'admin' }));
}
res.writeHead(401, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Не авторизован" }));
}
// API: Получить всех пользователей (только admin)
if (req.method === "GET" && urlPath === "/service/users") {
const user = checkAuth(req);
if (!user || user.id !== 'admin') {
res.writeHead(403, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Только для администратора" }));
}
const users = loadUsers();
// Не отдаём пароли и токены
const safeUsers = users.users.map(u => ({ id: u.id, name: u.name }));
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify(safeUsers));
}
// API: Добавить пользователя (только admin)
if (req.method === "POST" && urlPath === "/service/users") {
const user = checkAuth(req);
if (!user || user.id !== 'admin') {
res.writeHead(403, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Только для администратора" }));
}
const { id, name, password } = await body(req);
if (!id || !name || !password) {
res.writeHead(400, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Нужны id, name и password" }));
}
const users = loadUsers();
if (users.users.find(u => u.id === id)) {
res.writeHead(400, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Пользователь уже существует" }));
}
const token = "tok_" + id + "_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
users.users.push({ id, name, password, token });
saveUsers(users);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true, user: { id, name, password, token } }));
}
// API: Удалить пользователя (только admin, нельзя удалить admin и себя)
if (req.method === "DELETE" && urlPath.startsWith("/service/users/")) {
const user = checkAuth(req);
if (!user || user.id !== 'admin') {
res.writeHead(403, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Только для администратора" }));
}
const idToDelete = urlPath.split("/")[3];
if (idToDelete === 'admin') {
res.writeHead(400, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Нельзя удалить admin" }));
}
const users = loadUsers();
users.users = users.users.filter(u => u.id !== idToDelete);
saveUsers(users);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true }));
}
// API: Изменить пароль пользователя (только admin)
if (req.method === "PUT" && urlPath.startsWith("/service/users/")) {
const user = checkAuth(req);
if (!user || user.id !== 'admin') {
res.writeHead(403, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Только для администратора" }));
}
const userId = urlPath.split("/")[3];
const { password } = await body(req);
if (!password) {
res.writeHead(400, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Нужен password" }));
}
const users = loadUsers();
const u = users.users.find(u => u.id === userId);
if (!u) {
res.writeHead(404, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Пользователь не найден" }));
}
u.password = password;
saveUsers(users);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true }));
}
// API: GET /service/events
if (req.method === "GET" && urlPath === "/service/events") {
const tab = query.get("tab") || "biot";
const data = readData();
const events = data[tab + "Events"] || [];
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify(events));
}
// API: POST /service/events (требуется авторизация)
if (req.method === "POST" && urlPath === "/service/events") {
const user = checkAuth(req);
if (!user) {
res.writeHead(401, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Требуется авторизация" }));
}
const tab = query.get("tab") || "biot";
console.log("POST /service/events tab:", tab, "user:", user.name);
const data = readData();
const event = await body(req);
console.log("Event data:", event);
event.id = Date.now();
event.tab = tab;
event.createdBy = user.id;
const arrayName = tab + "Events";
if (!data[arrayName]) data[arrayName] = [];
data[arrayName].push(event);
console.log("Saving to:", arrayName, "Total:", data[arrayName].length);
writeData(data);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true, event }));
}
// API: PUT /service/events/:id (требуется авторизация)
if (req.method === "PUT" && urlPath.startsWith("/service/events/")) {
const user = checkAuth(req);
if (!user) {
res.writeHead(401, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Требуется авторизация" }));
}
const tab = query.get("tab") || "biot";
const id = parseInt(urlPath.split("/")[3]);
const data = readData();
const events = data[tab + "Events"] || [];
const idx = events.findIndex(e => e.id === id);
if (idx !== -1) {
events[idx] = { ...events[idx], ...await body(req), updatedBy: user.id };
data[tab + "Events"] = events;
writeData(data);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true }));
}
res.writeHead(404);
return res.end(JSON.stringify({ error: "Не найдено" }));
}
// API: DELETE /service/events/:id (требуется авторизация)
if (req.method === "DELETE" && urlPath.startsWith("/service/events/")) {
const user = checkAuth(req);
if (!user) {
res.writeHead(401, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Требуется авторизация" }));
}
const tab = query.get("tab") || "biot";
const id = parseInt(urlPath.split("/")[3]);
const data = readData();
data[tab + "Events"] = (data[tab + "Events"] || []).filter(e => e.id !== id);
writeData(data);
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ ok: true }));
}
// Статика
let file = urlPath === "/" ? "/index.html" : urlPath;
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" : "text/html";
res.writeHead(200, { "Content-Type": type + "; charset=utf-8" });
return res.end(fs.readFileSync(full));
}
res.writeHead(404);
res.end("Not found");
}).listen(PORT, () => console.log("Сервер запущен на порту " + PORT));