/** * API сохранения инцидентов ServiceDesk: upsert по ключу «Название» (number_key). */ import express from "express"; import { createPool, migrate } from "./db.js"; const PORT = Number(process.env.PORT || 3910); const app = express(); app.use(express.json({ limit: "50mb" })); const FIELDS_RU = [ "Название", "Статус", "Ответственный (команда)", "Ответственный (сотрудник)", "Инициатор заявки", "Услуга", "Дата создания", "Регламентное время решения запроса", "Дата решения", "Кем решен (сотрудник)", "Уникальный идентификатор" ]; function trimKeys(row) { const o = {}; if (!row || typeof row !== "object") return o; for (const [k, v] of Object.entries(row)) { const key = String(k).trim(); o[key] = v; } return o; } function extractNumber(row) { const r = trimKeys(row); const raw = r["Название"]; if (raw === undefined || raw === null || raw === "") return NaN; const n = parseInt(String(raw).replace(/\s/g, ""), 10); return n; } function rowToJsonb(row) { const r = trimKeys(row); const out = {}; for (const key of FIELDS_RU) { if (Object.prototype.hasOwnProperty.call(r, key)) { let v = r[key]; if (v === undefined || v === null) v = ""; out[key] = typeof v === "string" ? v.trim() : v; } else { out[key] = ""; } } return out; } let pool; app.get("/api/health", (_req, res) => { res.json({ ok: true, service: "omc-servicedesk-monitor" }); }); app.get("/api/incidents", async (_req, res) => { try { const r = await pool.query( "SELECT data FROM incidents ORDER BY number_key ASC" ); const rows = r.rows.map((x) => x.data); res.json({ rows }); } catch (e) { console.error(e); res.status(500).json({ error: String(e.message || e) }); } }); app.post("/api/incidents/sync", async (req, res) => { const body = req.body; const rowsIn = body && body.rows; if (!Array.isArray(rowsIn)) { return res.status(400).json({ error: "Ожидается body: { rows: [...] }" }); } const client = await pool.connect(); let applied = 0; let skipped = 0; try { await client.query("BEGIN"); for (const raw of rowsIn) { const num = extractNumber(raw); if (Number.isNaN(num) || num <= 0) { skipped++; continue; } const data = rowToJsonb(raw); data["Название"] = num; await client.query( `INSERT INTO incidents (number_key, data, updated_at) VALUES ($1, $2::jsonb, NOW()) ON CONFLICT (number_key) DO UPDATE SET data = EXCLUDED.data, updated_at = NOW()`, [num, JSON.stringify(data)] ); applied++; } await client.query("COMMIT"); res.json({ ok: true, applied, skipped, total: rowsIn.length }); } catch (e) { await client.query("ROLLBACK"); console.error(e); res.status(500).json({ error: String(e.message || e) }); } finally { client.release(); } }); async function main() { pool = createPool(); await migrate(pool); app.listen(PORT, "0.0.0.0", () => { console.log(`omc-sd API слушает :${PORT}`); }); } main().catch((e) => { console.error(e); process.exit(1); });