Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m27s
134 lines
5.0 KiB
JavaScript
134 lines
5.0 KiB
JavaScript
/**
|
|
* Общие роуты для JSON данных (servers, filters, billing и т.д.)
|
|
*/
|
|
|
|
const { s3, BUCKET_NAME, writeS3JsonObject } = require('../services/s3Service');
|
|
const { sendError, sendOk, checkIfNoneMatch } = require('../middleware/errorHandler');
|
|
const { GetObjectCommand, HeadObjectCommand } = require('@aws-sdk/client-s3');
|
|
const { streamToString } = require('../services/s3Service');
|
|
|
|
/**
|
|
* Создать 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 s3.send(new HeadObjectCommand({ Bucket: BUCKET_NAME, Key: s3Key })).catch(() => null);
|
|
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;
|
|
|
|
const data = await s3.send(new GetObjectCommand({ Bucket: BUCKET_NAME, Key: s3Key }));
|
|
const fileContent = await streamToString(data.Body);
|
|
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) {
|
|
if (error?.code === 'NoSuchKey' || error?.$metadata?.httpStatusCode === 404) {
|
|
res.json(fallback);
|
|
} else {
|
|
console.error(error);
|
|
return sendError(res, 500, 'Error reading from S3', 'E_S3');
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Создать POST эндпоинт для JSON данных
|
|
* @param {string} s3Key - Ключ в S3
|
|
* @param {Function} validateItem - Функция валидации (для массива - валидирует каждый элемент, для объекта - весь объект)
|
|
* @param {Object} options - Опции
|
|
* @param {boolean} options.singleObject - Если true, ожидает объект вместо массива
|
|
*/
|
|
function createJsonDataPOST(s3Key, validateItem = null, options = {}) {
|
|
const { singleObject = false } = options;
|
|
|
|
return async (req, res) => {
|
|
const { domains: items } = req.body; // Используем 'domains' для обратной совместимости
|
|
|
|
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 to S3', 'E_S3');
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Создать роуты для JSON данных (GET + POST)
|
|
* @param {string} s3Key - Ключ в S3
|
|
* @param {Function} validateItem - Функция валидации
|
|
* @param {Object} options - Опции
|
|
* @param {boolean} options.singleObject - Если true, работает с объектом вместо массива
|
|
* @param {any} options.defaultValue - Значение по умолчанию
|
|
*/
|
|
function createJsonDataRoutes(s3Key, validateItem = null, options = {}) {
|
|
return {
|
|
get: createJsonDataGET(s3Key, options),
|
|
post: createJsonDataPOST(s3Key, validateItem, options)
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
createJsonDataRoutes,
|
|
createJsonDataGET,
|
|
createJsonDataPOST,
|
|
};
|
|
|