161 lines
10 KiB
JavaScript
161 lines
10 KiB
JavaScript
const http = require("http");
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
const formidable = require("formidable").default || require("formidable");
|
||
|
||
const PORT = process.env.PORT || 3000;
|
||
const ROOT = __dirname;
|
||
const DATA = path.join(ROOT, "data.json");
|
||
const UPLOADS = path.join(ROOT, "uploads");
|
||
const QUESTIONS_TOTAL = 10;
|
||
|
||
if (!fs.existsSync(UPLOADS)) fs.mkdirSync(UPLOADS);
|
||
|
||
const EMPTY = { laws: [], structure: [], videos: [], complaints: [], results: [] };
|
||
|
||
const SEED = {
|
||
laws: [
|
||
{ id: 1, title: "Қазақстан Республикасының «Сыбайлас жемқорлыққа қарсы іс-қимыл туралы» Заңы", cat: "Заң", desc: "Сыбайлас жемқорлыққа қарсы іс-қимылдың негізгі қағидаттары, тыйымдар мен жауапкершілік." },
|
||
{ id: 2, title: "ҚР «Мемлекеттік қызмет туралы» Заңы", cat: "Заң", desc: "Мемлекеттік қызметшілердің этикасы мен шектеулері, мүдделер қақтығысын реттеу." },
|
||
{ id: 3, title: "«Жеке деректер және оларды қорғау туралы» Заң", cat: "Заң", desc: "Жеке деректерді жинау, өңдеу және сақтау талаптары." },
|
||
{ id: 4, title: "Қазақстан Республикасының Азаматтық кодексі (құпия ақпарат бөлімі)", cat: "Құқық", desc: "Коммерциялық құпия және құпия ақпарат режимі." },
|
||
{ id: 5, title: "«Ақпаратқа қол жеткізу туралы» Заң", cat: "Заң", desc: "Ақпаратқа қол жеткізу тәртібі және қызметтік ақпараттың құпиялылығы." },
|
||
{ id: 6, title: "Телеканалдың Ішкі еңбек тәртібі", cat: "Ішкі регламент", desc: "Еңбек тәртібі, қауіпсіздік және жұмыс орнындағы әдеп нормалары." },
|
||
{ id: 7, title: "Қаржылық бақылау жөніндегі нұсқаулық", cat: "Ішкі регламент", desc: "Қаржылық операцияларды бақылау, сыйлықтар мен төлемдерді декларациялау тәртібі." },
|
||
{ id: 8, title: "Ақпараттық қауіпсіздік саясаты", cat: "Ішкі регламент", desc: "Ақпараттық ресурстарды пайдалану, пароль және құпия сөздермен жұмыс ережелері." },
|
||
{ id: 9, title: "«Ақшаны жылыстатуға қарсы іс-қимыл туралы» Заң", cat: "Заң", desc: "Күдікті операциялар туралы хабарлау және қаржылық мониторинг талаптары." },
|
||
{ id: 10, title: "Комплаенс-бақылау жөніндегі ереже", cat: "Ішкі регламент", desc: "Комплаенс-тексерулер, өзін-өзі тексеру және есептілік тәртібі." }
|
||
],
|
||
structure: [
|
||
{ id: 1, name: "Айгүл Сейтова", role: "Бас директор", dept: "Әкімшілік", head: true, emoji: "👩💼" },
|
||
{ id: 2, name: "Ерлан Қасымов", role: "Директордың орынбасары", dept: "Әкімшілік", head: false, emoji: "👨💼" },
|
||
{ id: 3, name: "Асет Бисенов", role: "Комплаенс офицер", dept: "Комплаенс қызметі", head: false, emoji: "🛡️" },
|
||
{ id: 4, name: "Гүлназ Омарова", role: "Заңгер", dept: "Құқықтық департамент", head: false, emoji: "⚖️" },
|
||
{ id: 5, name: "Дәурен Нұрлан", role: "Бас бухгалтер", dept: "Қаржы департаменті", head: false, emoji: "💼" },
|
||
{ id: 6, name: "Майра Жұмабек", role: "HR директоры", dept: "Кадр департаменті", head: false, emoji: "🧑🏫" },
|
||
{ id: 7, name: "Бауыржан Серік", role: "IT директоры", dept: "IT департаменті", head: false, emoji: "💻" },
|
||
{ id: 8, name: "Ляззат Төлеген", role: "Бас редактор", dept: "Редакция", head: false, emoji: "📰" }
|
||
],
|
||
videos: [
|
||
{ id: 1, title: "Комплаенс деген не?", category: "Кіріспе", duration: "4:32", desc: "Комплаенс ұғымы және телеканалдағы рөлі" },
|
||
{ id: 2, title: "Мүдделер қақтығысы: қалай анықтауға?", category: "Этика", duration: "3:45", desc: "Мүдделер қақтығысының белгілері мен шешімдері" },
|
||
{ id: 3, title: "Сыбайлас жемқорлыққа қарсы саясат", category: "Сыбайлас жемқорлық", duration: "5:18", desc: "Негізгі тыйымдар мен жауапкершілік" },
|
||
{ id: 4, title: "Сыйлықтар мен төлемдер: шегі қандай?", category: "Сыбайлас жемқорлық", duration: "3:20", desc: "Сыйлық қабылдау ережелері және декларациялау" },
|
||
{ id: 5, title: "Құпия ақпаратпен жұмыс", category: "Ақпарат", duration: "4:55", desc: "Құпия ақпаратты қорғау ережелері" },
|
||
{ id: 6, title: "Жеке деректерді қорғау", category: "Ақпарат", duration: "3:55", desc: "Жеке деректермен жұмыс істеу талаптары" },
|
||
{ id: 7, title: "Қысым мен кемсітушілік", category: "Этика", duration: "4:28", desc: "Жұмыс орнындағы әдеп және теңдік" },
|
||
{ id: 8, title: "Әлеуметтік желілердегі мінез-құлық", category: "Этика", duration: "3:38", desc: "Қызметкердің әлеуметтік желідегі әдебі" }
|
||
],
|
||
complaints: [],
|
||
results: []
|
||
};
|
||
|
||
function load() {
|
||
let d;
|
||
try { d = JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { d = null; }
|
||
if (!d) {
|
||
save(JSON.parse(JSON.stringify(SEED)));
|
||
return JSON.parse(JSON.stringify(SEED));
|
||
}
|
||
["laws", "structure", "videos", "complaints", "results"].forEach(function (k) {
|
||
if (!Array.isArray(d[k])) d[k] = [];
|
||
});
|
||
return d;
|
||
}
|
||
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));
|
||
}
|
||
|
||
http.createServer(async (req, res) => {
|
||
const url = req.url.split("?")[0];
|
||
|
||
// ===== Законы и материалы =====
|
||
if (req.method === "GET" && url === "/api/laws") return json(res, 200, load().laws);
|
||
if (req.method === "POST" && url === "/api/laws") {
|
||
const d = load(); const b = await body(req);
|
||
d.laws.push({ id: Date.now(), cat: "Ішкі регламент", ...b });
|
||
save(d); return json(res, 200, { ok: true });
|
||
}
|
||
|
||
// ===== Структура =====
|
||
if (req.method === "GET" && url === "/api/structure") return json(res, 200, load().structure);
|
||
|
||
// ===== Видео =====
|
||
if (req.method === "GET" && url === "/api/videos") return json(res, 200, load().videos);
|
||
if (req.method === "POST" && url === "/api/upload") {
|
||
const form = formidable({ uploadDir: UPLOADS, keepExtensions: true, maxFileSize: 200 * 1024 * 1024 });
|
||
form.parse(req, (err, fields, files) => {
|
||
const f = files.file && files.file[0];
|
||
if (err || !f) return json(res, 400, { error: "file" });
|
||
const rel = "uploads/" + path.basename(f.filepath);
|
||
const d = load();
|
||
d.videos.push({
|
||
id: Date.now(),
|
||
title: (fields.title && fields.title[0]) || "Видео сабақ",
|
||
category: "Жаңа сабақ",
|
||
duration: "—",
|
||
desc: "Жаңадан қосылған видео сабақ",
|
||
src: "/" + rel
|
||
});
|
||
save(d);
|
||
return json(res, 200, { ok: true, src: "/" + rel });
|
||
});
|
||
return;
|
||
}
|
||
|
||
// ===== Жалобы =====
|
||
if (req.method === "GET" && url === "/api/complaints") return json(res, 200, load().complaints);
|
||
if (req.method === "POST" && url === "/api/complaints") {
|
||
const d = load(); const b = await body(req);
|
||
d.complaints.push({
|
||
id: Date.now(),
|
||
category: b.category || "Басқа",
|
||
text: b.text || "",
|
||
date: new Date().toISOString().slice(0, 10),
|
||
status: "жаңа"
|
||
});
|
||
save(d); return json(res, 200, { ok: true });
|
||
}
|
||
|
||
// ===== Результаты тестов =====
|
||
if (req.method === "GET" && url === "/api/results") return json(res, 200, load().results);
|
||
if (req.method === "POST" && url === "/api/results") {
|
||
const d = load(); const b = await body(req);
|
||
d.results.push({
|
||
name: b.name || "Аноним",
|
||
dept: b.dept || "—",
|
||
score: Number(b.score) || 0,
|
||
total: Number(b.total) || QUESTIONS_TOTAL,
|
||
date: new Date().toISOString().slice(0, 10)
|
||
});
|
||
save(d); return json(res, 200, { ok: true });
|
||
}
|
||
|
||
// ===== Статика =====
|
||
let file = url === "/" ? "/index.html" : url;
|
||
const full = path.join(ROOT, file);
|
||
if (full.startsWith(ROOT) && fs.existsSync(full) && fs.statSync(full).isFile()) {
|
||
const ext = path.extname(full);
|
||
const type =
|
||
ext === ".css" ? "text/css" :
|
||
ext === ".js" ? "application/javascript" :
|
||
ext === ".mp4" ? "video/mp4" :
|
||
ext === ".webm" ? "video/webm" :
|
||
ext === ".mov" ? "video/quicktime" : "text/html";
|
||
res.writeHead(200, { "Content-Type": type + "; charset=utf-8" });
|
||
return res.end(fs.readFileSync(full));
|
||
}
|
||
json(res, 404, { error: "Not found" });
|
||
}).listen(PORT, () => console.log("Compliance TV сервер: порт " + PORT));
|