130 lines
3.3 KiB
JavaScript
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);
|
|
});
|