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