113 lines
4.4 KiB
JavaScript
113 lines
4.4 KiB
JavaScript
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: [], conversations: [] }; }
|
|
}
|
|
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({}); } });
|
|
});
|
|
}
|
|
|
|
http.createServer(async (req, res) => {
|
|
const url = req.url.split("?")[0];
|
|
|
|
// API: получить настройки агента
|
|
if (req.method === "GET" && url === "/api/agent") {
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify({ role: "Помощник", behavior: "Вежливый и точный", tools: [] }));
|
|
}
|
|
|
|
// API: сохранить настройки агента
|
|
if (req.method === "POST" && url === "/api/agent") {
|
|
const settings = await body(req);
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify({ ok: true }));
|
|
}
|
|
|
|
// API: отправить сообщение и получить ответ ИИ
|
|
if (req.method === "POST" && url === "/api/chat") {
|
|
const { message } = await body(req);
|
|
let answer = "Заглушка: ИИ пока не подключён. Настройка в Фазе 2.";
|
|
try {
|
|
if (process.env.AI_BASE_URL && process.env.AI_API_KEY) {
|
|
const r = await fetch(process.env.AI_BASE_URL + "/chat/completions", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json", Authorization: "Bearer " + process.env.AI_API_KEY },
|
|
body: JSON.stringify({ model: process.env.AI_MODEL || "gpt-4o-mini", messages: [{ role: "user", content: message }] })
|
|
});
|
|
if (r.ok) {
|
|
const j = await r.json();
|
|
answer = j.choices?.[0]?.message?.content || "ИИ вернул пустой ответ";
|
|
} else {
|
|
answer = "ИИ недоступен (код " + r.status + ")";
|
|
}
|
|
}
|
|
} catch (e) {
|
|
answer = "Ошибка ИИ: " + e.message;
|
|
}
|
|
const data = readData();
|
|
data.conversations = data.conversations || [];
|
|
data.conversations.push({ role: "user", content: message, ts: Date.now() });
|
|
data.conversations.push({ role: "assistant", content: answer, ts: Date.now() });
|
|
writeData(data);
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify({ ok: true, answer }));
|
|
}
|
|
|
|
// API: получить историю диалогов
|
|
if (req.method === "GET" && url === "/api/history") {
|
|
const data = readData();
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify({ conversations: data.conversations || [] }));
|
|
}
|
|
|
|
// API: очистить историю
|
|
if (req.method === "POST" && url === "/api/history/clear") {
|
|
const data = readData();
|
|
data.conversations = [];
|
|
writeData(data);
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
return res.end(JSON.stringify({ ok: true }));
|
|
}
|
|
|
|
// API: экспортировать результаты
|
|
if (req.method === "POST" && url === "/api/export") {
|
|
const { format } = await body(req);
|
|
const data = readData();
|
|
let content = "";
|
|
let type = "text/plain";
|
|
let ext = "txt";
|
|
if (format === "json") {
|
|
content = JSON.stringify(data.conversations, null, 2);
|
|
type = "application/json";
|
|
ext = "json";
|
|
} else {
|
|
content = (data.conversations || []).map(m => `[${m.role}] ${m.content}`).join("\n\n");
|
|
}
|
|
res.writeHead(200, { "Content-Type": type, "Content-Disposition": "attachment; filename=export." + ext });
|
|
return res.end(content);
|
|
}
|
|
|
|
// Статика
|
|
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" : "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));
|