Files

130 lines
3.3 KiB
JavaScript

/**
* 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);
});