Files
router-lists-ui/backend/routes/jsonDataRoutes.js
T
2026-04-20 01:17:00 +07:00

118 lines
3.7 KiB
JavaScript

/**
* Общие роуты для JSON данных (servers, filters, billing и т.д.)
*/
const { writeS3JsonObject, headMeta, readS3TextObject } = require('../services/s3Service');
const { sendError, sendOk, checkIfNoneMatch } = require('../middleware/errorHandler');
/**
* Создать GET эндпоинт для JSON данных
* @param {string} s3Key - Ключ объекта (логический, как раньше в S3)
* @param {Object} options - Опции
* @param {boolean} options.singleObject - Если true, возвращает объект вместо массива
* @param {any} options.defaultValue - Значение по умолчанию ([] для массива, {} для объекта)
*/
function createJsonDataGET(s3Key, options = {}) {
const { singleObject = false, defaultValue } = options;
const fallback = defaultValue !== undefined ? defaultValue : (singleObject ? {} : []);
return async (req, res) => {
try {
const head = await headMeta(s3Key);
const etag = head?.etag || null;
if (etag) res.set('ETag', String(etag));
if (head?.lastModified) res.set('Last-Modified', new Date(head.lastModified).toUTCString());
if (typeof head?.contentLength === 'number') res.set('Content-Length-Source', String(head.contentLength));
if (checkIfNoneMatch(req, res, etag)) return;
let fileContent;
try {
const data = await readS3TextObject(s3Key);
fileContent = data.body;
} catch (e) {
if (e?.code === 'NoSuchKey') {
return res.json(fallback);
}
throw e;
}
let items = fallback;
try {
const parsed = JSON.parse(fileContent);
if (singleObject) {
items = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? parsed : fallback;
} else {
items = Array.isArray(parsed) ? parsed : fallback;
}
} catch (parseError) {
console.error(`Error parsing ${s3Key}:`, parseError);
items = fallback;
}
res.json(items);
} catch (error) {
console.error(error);
return sendError(res, 500, 'Error reading storage', 'E_STORAGE');
}
};
}
/**
* Создать POST эндпоинт для JSON данных
*/
function createJsonDataPOST(s3Key, validateItem = null, options = {}) {
const { singleObject = false } = options;
return async (req, res) => {
const { domains: items } = req.body;
if (singleObject) {
if (typeof items !== 'object' || items === null || Array.isArray(items)) {
return sendError(res, 400, 'Data must be an object', 'E_BAD_REQUEST');
}
if (validateItem) {
const error = validateItem(items);
if (error) {
return sendError(res, 400, error, 'E_SCHEMA');
}
}
} else {
if (!Array.isArray(items)) {
return sendError(res, 400, 'Data must be an array', 'E_BAD_REQUEST');
}
if (validateItem) {
for (let i = 0; i < items.length; i++) {
const error = validateItem(items[i], i);
if (error) {
return sendError(res, 400, error, 'E_SCHEMA');
}
}
}
}
try {
const meta = await writeS3JsonObject(s3Key, items);
return sendOk(res, meta);
} catch (error) {
console.error(error);
return sendError(res, 500, 'Error writing storage', 'E_STORAGE');
}
};
}
function createJsonDataRoutes(s3Key, validateItem = null, options = {}) {
return {
get: createJsonDataGET(s3Key, options),
post: createJsonDataPOST(s3Key, validateItem, options),
};
}
module.exports = {
createJsonDataRoutes,
createJsonDataGET,
createJsonDataPOST,
};