feat: Оптимизация структуры server.js с модульным подходом, добавление новых маршрутов и улучшение обработки ошибок. Обновление валидаторов для MikroTik с использованием общих функций. Улучшение кода компонентов приложения для повышения читаемости и производительности.

This commit is contained in:
denozord committed 2025-10-03 01:49:51 +07:00
1 parent cdf692a2aa
commit 6231b645b6
17 files changed
+5149 -2295

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
/**
* Централизованный обработчик ошибок
*/
const promClient = require('prom-client');
// Метрики
const http304 = new promClient.Counter({ name: 'http_304_total', help: 'HTTP 304 responses' });
const http412 = new promClient.Counter({ name: 'http_412_total', help: 'HTTP 412 responses' });
const http423 = new promClient.Counter({ name: 'http_423_total', help: 'HTTP 423 responses' });
/**
* Отправить успешный ответ с метаданными
*/
function sendOk(res, meta) {
if (meta?.etag) res.set('ETag', String(meta.etag));
if (meta?.lastModified) res.set('Last-Modified', new Date(meta.lastModified).toUTCString());
if (typeof meta?.contentLength === 'number') res.set('Content-Length-Source', String(meta.contentLength));
return res.json({
ok: true,
etag: meta?.etag || null,
lastModified: meta?.lastModified || null,
contentLength: meta?.contentLength ?? null
});
}
/**
* Отправить ошибку
*/
function sendError(res, status, message, code, details) {
const requestId = res.req?.id;
try {
if (status === 304) http304.inc();
if (status === 412) http412.inc();
if (status === 423) http423.inc();
} catch {}
return res.status(status).json({ code, message, details, requestId });
}
/**
* Проверить If-None-Match заголовок
*/
function checkIfNoneMatch(req, res, etag) {
const inm = req.headers && (req.headers['if-none-match'] || req.headers['If-None-Match']);
if (inm && etag && String(inm) === String(etag)) {
try { http304.inc(); } catch {}
res.status(304).end();
return true;
}
return false;
}
/**
* Централизованный error handler middleware
*/
function errorHandler(err, req, res, next) {
const status = typeof err?.status === 'number' ? err.status : 500;
const code = err?.code || 'E_INTERNAL';
const message = status === 500 && process.env.NODE_ENV === 'production'
? 'Internal Server Error'
: (err?.message || 'Error');
const details = err?.details;
const requestId = req?.id;
try {
req.log?.error({ err, code, requestId }, 'request error');
} catch {}
res.status(status).json({ code, message, details, requestId });
}
module.exports = {
sendOk,
sendError,
checkIfNoneMatch,
errorHandler,
http304,
http412,
http423,
};
+70
View File
@@ -0,0 +1,70 @@
/**
* Менеджер soft-locks для ресурсов
* Предотвращает одновременное редактирование одного ресурса
*/
// In-memory locks: key -> { owner, expiresAt }
const locks = new Map();
/**
* Очистить истекшие блокировки
*/
function cleanupExpiredLocks() {
const now = Date.now();
for (const [k, v] of locks.entries()) {
if (!v || typeof v.expiresAt !== 'number' || v.expiresAt <= now) {
locks.delete(k);
}
}
}
// Автоматическая очистка каждые 30 секунд
setInterval(cleanupExpiredLocks, 30_000);
/**
* Получить статус блокировки ресурса
*/
function getLockStatus(resource) {
cleanupExpiredLocks();
const info = locks.get(resource);
if (!info) return { locked: false };
return { locked: true, owner: info.owner, expiresAt: info.expiresAt };
}
/**
* Получить или обновить блокировку ресурса
*/
function acquireLock(resource, owner = 'anonymous', ttlSeconds = 120) {
cleanupExpiredLocks();
const now = Date.now();
const existing = locks.get(resource);
if (existing && existing.expiresAt > now && existing.owner !== owner) {
return {
success: false,
locked: true,
owner: existing.owner,
expiresAt: existing.expiresAt
};
}
const expiresAt = now + Math.max(30, Math.min(600, Number(ttlSeconds) || 120)) * 1000;
locks.set(resource, { owner, expiresAt });
return { success: true, locked: true, owner, expiresAt };
}
/**
* Освободить блокировку ресурса
*/
function releaseLock(resource) {
locks.delete(resource);
return { released: true };
}
module.exports = {
getLockStatus,
acquireLock,
releaseLock,
cleanupExpiredLocks,
};