Backend: SQLite storage, EvoBGP integration, filters in SQL
Made-with: Cursor
This commit is contained in:
+100
-95
@@ -15,9 +15,16 @@ const crypto = require('crypto');
|
||||
// Импорт модулей
|
||||
const { sendError, sendOk, errorHandler } = require('./middleware/errorHandler');
|
||||
const { getLockStatus, acquireLock, releaseLock } = require('./middleware/lockManager');
|
||||
const { createTextDataRoutes } = require('./routes/textDataRoutes');
|
||||
const { createJsonDataRoutes } = require('./routes/jsonDataRoutes');
|
||||
const { splitWhitespace } = require('./utils/helpers');
|
||||
const { createFilterRulesRoutes } = require('./routes/filterRulesRoutes');
|
||||
const evobgpClient = require('./services/evobgpClient');
|
||||
const {
|
||||
createEvobgpListGET,
|
||||
createEvobgpDomainsNewPOST,
|
||||
createEvobgpAsnsPOST,
|
||||
createEvobgpIpRangesPOST,
|
||||
createEvobgpLegacyDomainsGET,
|
||||
} = require('./routes/evobgpReferenceRoutes');
|
||||
const validators = require('./lib/validators');
|
||||
const mikrotikValidator = require('./lib/mikrotik-validator');
|
||||
|
||||
@@ -231,99 +238,100 @@ const validateDomainsNew = ajv.compile(schemaDomainsNew);
|
||||
const validateAsns = ajv.compile(schemaAsns);
|
||||
const validateIpRanges = ajv.compile(schemaIpRanges);
|
||||
|
||||
// === Domains (старая версия) ===
|
||||
const domainsOldRoutes = createTextDataRoutes({
|
||||
s3Key: 'bgp_data/domains.txt',
|
||||
mapLine: (line) => {
|
||||
const parts = splitWhitespace(line);
|
||||
return { domain: parts[0] || '', type: parts[1] || '' };
|
||||
},
|
||||
formatLine: (d) => `${String(d.domain || '').trim()} ${String(d.type || '').trim()}`.trim(),
|
||||
validate: validateAsns,
|
||||
cachePrefix: 'domains'
|
||||
});
|
||||
// === Domains (старая версия, поля domain+type — тот же источник, что ASNs) ===
|
||||
const asnValidateItem = (asn, i) => {
|
||||
const errors = [];
|
||||
if (!validators.isValidASN(asn.domain)) {
|
||||
errors.push(`Элемент ${i}: неверный ASN "${asn.domain}"`);
|
||||
}
|
||||
if (!validators.isValidCommunity(asn.type)) {
|
||||
errors.push(`Элемент ${i}: неверный community "${asn.type}"`);
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
const domainsOldRoutes = {
|
||||
get: createEvobgpLegacyDomainsGET({ validate: validateAsns }),
|
||||
post: createEvobgpAsnsPOST({ validate: validateAsns, validateItem: asnValidateItem }),
|
||||
};
|
||||
|
||||
app.get('/api/domains', domainsOldRoutes.get);
|
||||
app.post('/api/domains', domainsOldRoutes.post);
|
||||
app.post('/api/domains', writeLimiter, domainsOldRoutes.post);
|
||||
|
||||
// === ASNs ===
|
||||
const asnsRoutes = createTextDataRoutes({
|
||||
s3Key: 'bgp_data/asns.txt',
|
||||
mapLine: (line) => {
|
||||
const parts = splitWhitespace(line);
|
||||
return { domain: parts[0] || '', type: parts[1] || '' };
|
||||
},
|
||||
formatLine: (a) => `${String(a.domain || '').trim().toUpperCase()} ${String(a.type || '').trim()}`.trim(),
|
||||
validate: validateAsns,
|
||||
validateItem: (asn, i) => {
|
||||
const errors = [];
|
||||
if (!validators.isValidASN(asn.domain)) {
|
||||
errors.push(`Элемент ${i}: неверный ASN "${asn.domain}"`);
|
||||
}
|
||||
if (!validators.isValidCommunity(asn.type)) {
|
||||
errors.push(`Элемент ${i}: неверный community "${asn.type}"`);
|
||||
}
|
||||
return errors;
|
||||
},
|
||||
cachePrefix: 'asns'
|
||||
});
|
||||
// === ASNs (EvoBGP) ===
|
||||
const asnsRoutes = {
|
||||
get: createEvobgpListGET({
|
||||
cachePrefix: 'asns',
|
||||
validate: validateAsns,
|
||||
fetchItems: async () => {
|
||||
const rows = await evobgpClient.fetchAsnsData();
|
||||
return rows.map((r) => ({ domain: r.asn, type: r.community }));
|
||||
},
|
||||
}),
|
||||
post: createEvobgpAsnsPOST({ validate: validateAsns, validateItem: asnValidateItem }),
|
||||
};
|
||||
|
||||
app.get('/api/asns', asnsRoutes.get);
|
||||
app.post('/api/asns', writeLimiter, asnsRoutes.post);
|
||||
|
||||
// === Domains New ===
|
||||
const domainsNewRoutes = createTextDataRoutes({
|
||||
s3Key: 'bgp_data/domains_community.txt',
|
||||
mapLine: (line) => {
|
||||
const parts = splitWhitespace(line);
|
||||
return { domain: parts[0] || '', community: parts[1] || '' };
|
||||
},
|
||||
formatLine: (d) => `${String(d.domain || '').trim().toLowerCase()} ${String(d.community || '').trim()}`.trim(),
|
||||
validate: validateDomainsNew,
|
||||
validateItem: (d, i) => {
|
||||
const errors = [];
|
||||
if (!validators.isValidDomain(d.domain) && !validators.isValidWildcardDomain(d.domain)) {
|
||||
errors.push(`Элемент ${i}: неверный домен "${d.domain}"`);
|
||||
}
|
||||
if (!validators.isValidCommunity(d.community)) {
|
||||
errors.push(`Элемент ${i}: неверный community "${d.community}"`);
|
||||
}
|
||||
if (!validators.isSafeXSSString(d.domain)) {
|
||||
errors.push(`Элемент ${i}: домен содержит потенциально опасные символы`);
|
||||
}
|
||||
return errors;
|
||||
},
|
||||
cachePrefix: 'domains-new'
|
||||
});
|
||||
// === Domains New (EvoBGP) ===
|
||||
const domainsNewRoutes = {
|
||||
get: createEvobgpListGET({
|
||||
cachePrefix: 'domains-new',
|
||||
validate: validateDomainsNew,
|
||||
fetchItems: async () => {
|
||||
const rows = await evobgpClient.fetchDomainsData();
|
||||
return rows.map(({ domain, community }) => ({ domain, community }));
|
||||
},
|
||||
}),
|
||||
post: createEvobgpDomainsNewPOST({
|
||||
validate: validateDomainsNew,
|
||||
validateItem: (d, i) => {
|
||||
const errors = [];
|
||||
if (!validators.isValidDomain(d.domain) && !validators.isValidWildcardDomain(d.domain)) {
|
||||
errors.push(`Элемент ${i}: неверный домен "${d.domain}"`);
|
||||
}
|
||||
if (!validators.isValidCommunity(d.community)) {
|
||||
errors.push(`Элемент ${i}: неверный community "${d.community}"`);
|
||||
}
|
||||
if (!validators.isSafeXSSString(d.domain)) {
|
||||
errors.push(`Элемент ${i}: домен содержит потенциально опасные символы`);
|
||||
}
|
||||
return errors;
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
app.get('/api/domains-new', domainsNewRoutes.get);
|
||||
app.post('/api/domains-new', writeLimiter, domainsNewRoutes.post);
|
||||
|
||||
// === IP Ranges ===
|
||||
const ipRangesRoutes = createTextDataRoutes({
|
||||
s3Key: 'bgp_data/ips.txt',
|
||||
mapLine: (line) => {
|
||||
const parts = splitWhitespace(line);
|
||||
return { ipRange: parts[0] || '', community: parts[1] || '' };
|
||||
},
|
||||
formatLine: (ip) => `${String(ip.ipRange || '').trim()} ${String(ip.community || '').trim()}`.trim(),
|
||||
validate: validateIpRanges,
|
||||
validateItem: (ip, i) => {
|
||||
const errors = [];
|
||||
const ipStr = String(ip.ipRange || '').trim();
|
||||
const isValidCIDR = validators.isValidCIDRv4(ipStr) || validators.isValidCIDRv6(ipStr);
|
||||
const isValidIP = validators.isValidIPv4(ipStr) || validators.isValidIPv6(ipStr);
|
||||
|
||||
if (!isValidCIDR && !isValidIP) {
|
||||
errors.push(`Элемент ${i}: неверный IP/CIDR "${ipStr}"`);
|
||||
}
|
||||
if (!validators.isValidCommunity(ip.community)) {
|
||||
errors.push(`Элемент ${i}: неверный community "${ip.community}"`);
|
||||
}
|
||||
return errors;
|
||||
},
|
||||
cachePrefix: 'ip-ranges'
|
||||
});
|
||||
// === IP Ranges (EvoBGP) ===
|
||||
const ipRangesRoutes = {
|
||||
get: createEvobgpListGET({
|
||||
cachePrefix: 'ip-ranges',
|
||||
validate: validateIpRanges,
|
||||
fetchItems: async () => {
|
||||
const rows = await evobgpClient.fetchIpRangesData();
|
||||
return rows.map(({ ipRange, community }) => ({ ipRange, community }));
|
||||
},
|
||||
}),
|
||||
post: createEvobgpIpRangesPOST({
|
||||
validate: validateIpRanges,
|
||||
validateItem: (ip, i) => {
|
||||
const errors = [];
|
||||
const ipStr = String(ip.ipRange || '').trim();
|
||||
const isValidCIDR = validators.isValidCIDRv4(ipStr) || validators.isValidCIDRv6(ipStr);
|
||||
const isValidIP = validators.isValidIPv4(ipStr) || validators.isValidIPv6(ipStr);
|
||||
if (!isValidCIDR && !isValidIP) {
|
||||
errors.push(`Элемент ${i}: неверный IP/CIDR "${ipStr}"`);
|
||||
}
|
||||
if (!validators.isValidCommunity(ip.community)) {
|
||||
errors.push(`Элемент ${i}: неверный community "${ip.community}"`);
|
||||
}
|
||||
return errors;
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
app.get('/api/ip-ranges', ipRangesRoutes.get);
|
||||
app.post('/api/ip-ranges', writeLimiter, ipRangesRoutes.post);
|
||||
@@ -360,24 +368,21 @@ const billingRoutes = createJsonDataRoutes('servers-billing.json', (item, i) =>
|
||||
app.get('/api/billing', billingRoutes.get);
|
||||
app.post('/api/billing', billingRoutes.post);
|
||||
|
||||
// Filters
|
||||
const filtersRoutes2 = createJsonDataRoutes('filters.json', (filter, i) => {
|
||||
// Filters (SQL app_filter_rules, scope global)
|
||||
const filterItemValidator = (filter, i) => {
|
||||
if (!filter.community || !filter.gateway) {
|
||||
return `Filter at index ${i} is missing required fields`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
const filtersRoutes2 = createFilterRulesRoutes('global', filterItemValidator);
|
||||
|
||||
app.get('/api/filters', filtersRoutes2.get);
|
||||
app.post('/api/filters', filtersRoutes2.post);
|
||||
|
||||
// Simple Filters
|
||||
const simpleFiltersRoutes = createJsonDataRoutes('filter-manager/simple-filters.json', (filter, i) => {
|
||||
if (!filter.community || !filter.gateway) {
|
||||
return `Filter at index ${i} is missing required fields`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// Simple Filters (app_filter_rules, scope simple)
|
||||
const simpleFiltersRoutes = createFilterRulesRoutes('simple', filterItemValidator);
|
||||
|
||||
app.get('/api/simple-filters', simpleFiltersRoutes.get);
|
||||
app.post('/api/simple-filters', simpleFiltersRoutes.post);
|
||||
@@ -508,7 +513,7 @@ app.get('/api/resources/stats', resourceStatsRoutes.getResourceStats);
|
||||
// === ALERTS (агрегация проблем: серверы, ресурсы роутеров) ===
|
||||
app.get('/api/alerts', alertsRoutes.getAlerts);
|
||||
|
||||
// === MIKROTIK BACKUPS (S3) ===
|
||||
// === MIKROTIK BACKUPS (локальная ФС) ===
|
||||
app.post('/api/mikrotik/backups', writeLimiter, mikrotikBackupRoutes.createBackup);
|
||||
app.get('/api/mikrotik/backups', mikrotikBackupRoutes.listBackups);
|
||||
app.get('/api/mikrotik/backups/item', mikrotikBackupRoutes.getBackup);
|
||||
|
||||
Reference in New Issue
Block a user