require('dotenv').config(); const express = require('express'); const AWS = require('aws-sdk'); const cors = require('cors'); const path = require('path'); const app = express(); const port = 3001; app.use(cors()); app.use(express.json()); // Serve static files from the React app app.use(express.static(path.join(__dirname, 'public'))); // Configure AWS S3 const s3 = new AWS.S3({ endpoint: 'https://storage.yandexcloud.net', region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, } }); const BUCKET_NAME = process.env.S3_BUCKET_NAME; const FILE_KEY = 'bgp_data/domains.txt'; // Get domains from S3 app.get('/api/domains', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: FILE_KEY, }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); const domains = fileContent.split('\n').filter(line => line).map(line => { // Trim the line to remove any leading/trailing whitespace, including \r // And split by any whitespace to be more robust const parts = line.trim().split(/\s+/); const domain = parts[0] || ''; const type = parts[1] || ''; return { domain, type }; }); res.json(domains); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update domains in S3 app.post('/api/domains', async (req, res) => { const { domains } = req.body; const fileContent = domains.map(d => `${d.domain} ${d.type}`).join('\n'); const params = { Bucket: BUCKET_NAME, Key: FILE_KEY, Body: fileContent, ContentType: 'text/plain', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // --- ASNs Routes --- // Get ASNs from S3 app.get('/api/asns', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/asns.txt', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); const asns = fileContent.split('\n').filter(line => line).map(line => { const parts = line.trim().split(/\s+/); const domain = parts[0] || ''; // Keep name 'domain' for consistency in component const type = parts[1] || ''; return { domain, type }; }); res.json(asns); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update ASNs in S3 app.post('/api/asns', async (req, res) => { const { domains: asns } = req.body; // Keep name 'domains' for consistency const fileContent = asns.map(a => `${a.domain} ${a.type}`).join('\n'); const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/asns.txt', Body: fileContent, ContentType: 'text/plain', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // --- Domains New Routes --- // Get domains-new from S3 app.get('/api/domains-new', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/domains_community.txt', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); const domains = fileContent.split('\n').filter(line => line).map(line => { const parts = line.trim().split(/\s+/); const domain = parts[0] || ''; const community = parts[1] || ''; return { domain, community }; }); res.json(domains); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update domains-new in S3 app.post('/api/domains-new', async (req, res) => { const { domains } = req.body; const fileContent = domains.map(d => `${d.domain} ${d.community}`).join('\n'); const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/domains_community.txt', Body: fileContent, ContentType: 'text/plain', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // --- IP Ranges Routes --- // Get IP ranges from S3 app.get('/api/ip-ranges', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/ips.txt', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); const ipRanges = fileContent.split('\n').filter(line => line).map(line => { const parts = line.trim().split(/\s+/); const ipRange = parts[0] || ''; const community = parts[1] || ''; return { ipRange, community }; }); res.json(ipRanges); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update IP ranges in S3 app.post('/api/ip-ranges', async (req, res) => { const { ipRanges } = req.body; const fileContent = ipRanges.map(ip => `${ip.ipRange} ${ip.community}`).join('\n'); const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/ips.txt', Body: fileContent, ContentType: 'text/plain', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // --- Servers Routes (JSON format) --- // Get servers from S3 app.get('/api/servers', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'servers.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let servers = []; try { servers = JSON.parse(fileContent); // Ensure it's an array if (!Array.isArray(servers)) { servers = []; } } catch (parseError) { console.error('Error parsing servers.json:', parseError); servers = []; } res.json(servers); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update servers in S3 app.post('/api/servers', async (req, res) => { const { domains: servers } = req.body; // Keep name 'domains' for consistency // Validate servers structure if (!Array.isArray(servers)) { return res.status(400).send('Servers must be an array'); } // Validate each server has required fields for (let i = 0; i < servers.length; i++) { const server = servers[i]; if (!server.ip || !server.dns || !server.country || !server.provider || !server.tunnel) { return res.status(400).send(`Server at index ${i} is missing required fields`); } } const params = { Bucket: BUCKET_NAME, Key: 'servers.json', Body: JSON.stringify(servers, null, 2), // Pretty print JSON ContentType: 'application/json', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // --- Billing Routes --- // Get billing data from S3 app.get('/api/billing', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'servers-billing.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let billingData = []; try { billingData = JSON.parse(fileContent); // Ensure it's an array if (!Array.isArray(billingData)) { billingData = []; } } catch (parseError) { console.error('Error parsing servers-billing.json:', parseError); billingData = []; } res.json(billingData); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update billing data in S3 app.post('/api/billing', async (req, res) => { const { domains: billingData } = req.body; // Keep name 'domains' for consistency // Validate billing data structure if (!Array.isArray(billingData)) { return res.status(400).send('Billing data must be an array'); } // Validate each billing item has required fields for (let i = 0; i < billingData.length; i++) { const item = billingData[i]; if (!item.hostName || !item.country || !item.provider) { return res.status(400).send(`Billing item at index ${i} is missing required fields: hostName, country, provider`); } } const params = { Bucket: BUCKET_NAME, Key: 'servers-billing.json', Body: JSON.stringify(billingData, null, 2), // Pretty print JSON ContentType: 'application/json', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // --- Filters Routes (JSON format) --- // Get filters from S3 app.get('/api/filters', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'filters.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let filters = []; try { filters = JSON.parse(fileContent); // Ensure it's an array if (!Array.isArray(filters)) { filters = []; } } catch (parseError) { console.error('Error parsing filters.json:', parseError); filters = []; } res.json(filters); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading from S3'); } } }); // Update filters in S3 app.post('/api/filters', async (req, res) => { const { domains: filters } = req.body; // Keep name 'domains' for consistency // Validate filters structure if (!Array.isArray(filters)) { return res.status(400).send('Filters must be an array'); } // Validate each filter has required fields for (let i = 0; i < filters.length; i++) { const filter = filters[i]; if (!filter.community || !filter.gateway) { return res.status(400).send(`Filter at index ${i} is missing required fields`); } } const params = { Bucket: BUCKET_NAME, Key: 'filters.json', Body: JSON.stringify(filters, null, 2), // Pretty print JSON ContentType: 'application/json', }; try { await s3.putObject(params).promise(); res.send('File updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing to S3'); } }); // Новый эндпоинт для получения дат последнего изменения файлов S3 app.get('/api/s3/last-modified', async (req, res) => { try { const results = await Promise.allSettled([ s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/domains_community.txt' }).promise(), s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/domains_community.txt' }).promise(), s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/asns.txt' }).promise(), s3.headObject({ Bucket: BUCKET_NAME, Key: 'servers.json' }).promise(), s3.headObject({ Bucket: BUCKET_NAME, Key: 'filters.json' }).promise() ]); const response = { domainsLastModified: null, domainsNewLastModified: null, asnsLastModified: null, serversLastModified: null, filtersLastModified: null }; // Обрабатываем результаты if (results[0].status === 'fulfilled') { response.domainsLastModified = results[0].value.LastModified ? results[0].value.LastModified.toISOString() : null; } if (results[1].status === 'fulfilled') { response.domainsNewLastModified = results[1].value.LastModified ? results[1].value.LastModified.toISOString() : null; } if (results[2].status === 'fulfilled') { response.asnsLastModified = results[2].value.LastModified ? results[2].value.LastModified.toISOString() : null; } if (results[3].status === 'fulfilled') { response.serversLastModified = results[3].value.LastModified ? results[3].value.LastModified.toISOString() : null; } if (results[4].status === 'fulfilled') { response.filtersLastModified = results[4].value.LastModified ? results[4].value.LastModified.toISOString() : null; } res.json(response); } catch (error) { console.error('Error fetching last modified dates from S3:', error); res.status(500).send('Error fetching last modified dates from S3'); } }); // Generate MikroTik configuration from filters app.get('/api/filters/generate-config', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'filters.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let filters = []; try { filters = JSON.parse(fileContent); if (!Array.isArray(filters)) { filters = []; } } catch (parseError) { console.error('Error parsing filters.json:', parseError); filters = []; } if (filters.length === 0) { return res.json({ config: '// No filters to generate configuration' }); } // Group filters by gateway const gatewayGroups = {}; filters.forEach(filter => { if (!gatewayGroups[filter.gateway]) { gatewayGroups[filter.gateway] = []; } gatewayGroups[filter.gateway].push(filter.community); }); let config = '// Frouting filter configuration for MikroTik 7.14+\n'; config += '// Generated automatically\n'; config += `// Date: ${new Date().toISOString()}\n\n`; config += '/routing filter bgp-in-tmp {\n'; // Если есть только один gateway, создаем простую конфигурацию if (Object.keys(gatewayGroups).length === 1) { const [gateway, communities] = Object.entries(gatewayGroups)[0]; config += ` if (\n`; communities.forEach((community, index) => { config += ` (bgp-communities includes ${community})`; if (index < communities.length - 1) { config += ` or \n`; } }); config += `\n )\n {\n set gw ${gateway}; accept;\n }\n else\n {\n reject;\n }\n`; } else { // Если несколько gateway, создаем каскадную структуру const gatewayEntries = Object.entries(gatewayGroups); gatewayEntries.forEach(([gateway, communities], gatewayIndex) => { if (gatewayIndex === 0) { config += ` if (\n`; } else { config += ` else if (\n`; } communities.forEach((community, index) => { config += ` (bgp-communities includes ${community})`; if (index < communities.length - 1) { config += ` or \n`; } }); config += `\n )\n {\n set gw ${gateway}; accept;\n }`; if (gatewayIndex === gatewayEntries.length - 1) { config += `\n else\n {\n reject;\n }\n`; } else { config += `\n`; } }); } config += '}\n'; res.json({ config }); } catch (error) { if (error.code === 'NoSuchKey') { res.json({ config: '// filters.json file not found' }); } else { console.error(error); res.status(500).send('Error generating configuration'); } } }); // Export MikroTik configuration to S3 app.post('/api/filters/export-config', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'filters.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let filters = []; try { filters = JSON.parse(fileContent); if (!Array.isArray(filters)) { filters = []; } } catch (parseError) { console.error('Error parsing filters.json:', parseError); filters = []; } if (filters.length === 0) { return res.json({ success: false, message: 'Нет фильтров для экспорта' }); } // Group filters by gateway const gatewayGroups = {}; filters.forEach(filter => { if (!gatewayGroups[filter.gateway]) { gatewayGroups[filter.gateway] = []; } gatewayGroups[filter.gateway].push(filter.community); }); let config = '// Frouting filter configuration for MikroTik 7.14+\n'; config += '// Generated automatically\n'; config += `// Date: ${new Date().toISOString()}\n\n`; config += '/routing filter bgp-in-tmp {\n'; // Если есть только один gateway, создаем простую конфигурацию if (Object.keys(gatewayGroups).length === 1) { const [gateway, communities] = Object.entries(gatewayGroups)[0]; config += ` if (\n`; communities.forEach((community, index) => { config += ` (bgp-communities includes ${community})`; if (index < communities.length - 1) { config += ` or \n`; } }); config += `\n )\n {\n set gw ${gateway}; accept;\n }\n else\n {\n reject;\n }\n`; } else { // Если несколько gateway, создаем каскадную структуру const gatewayEntries = Object.entries(gatewayGroups); gatewayEntries.forEach(([gateway, communities], gatewayIndex) => { if (gatewayIndex === 0) { config += ` if (\n`; } else { config += ` else if (\n`; } communities.forEach((community, index) => { config += ` (bgp-communities includes ${community})`; if (index < communities.length - 1) { config += ` or \n`; } }); config += `\n )\n {\n set gw ${gateway}; accept;\n }`; if (gatewayIndex === gatewayEntries.length - 1) { config += `\n else\n {\n reject;\n }\n`; } else { config += `\n`; } }); } config += '}\n'; // Save configuration to S3 const exportParams = { Bucket: BUCKET_NAME, Key: 'mikrotik-frouting-config.txt', Body: config, ContentType: 'text/plain', }; await s3.putObject(exportParams).promise(); res.json({ success: true, message: 'Конфигурация экспортирована в S3' }); } catch (error) { console.error(error); res.status(500).send('Error exporting configuration'); } }); // --- Server Configs Routes --- // Get server configs list app.get('/api/server-configs', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'server-configs.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let servers = []; try { servers = JSON.parse(fileContent); if (!Array.isArray(servers)) { servers = []; } } catch (parseError) { console.error('Error parsing server-configs.json:', parseError); servers = []; } res.json(servers); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading server configs from S3'); } } }); // Update server configs list app.post('/api/server-configs', async (req, res) => { const { servers } = req.body; // Validate servers structure if (!Array.isArray(servers)) { return res.status(400).send('Servers must be an array'); } // Validate each server has required fields for (let i = 0; i < servers.length; i++) { const server = servers[i]; if (!server.id || !server.name) { return res.status(400).send(`Server at index ${i} is missing required fields`); } } const params = { Bucket: BUCKET_NAME, Key: 'server-configs.json', Body: JSON.stringify(servers, null, 2), // Pretty print JSON ContentType: 'application/json', }; try { await s3.putObject(params).promise(); res.send('Server configs updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing server configs to S3'); } }); // Get specific server config app.get('/api/server-configs/:serverId', async (req, res) => { const { serverId } = req.params; const params = { Bucket: BUCKET_NAME, Key: `filter-manager/config-${serverId}.txt`, }; try { const data = await s3.getObject(params).promise(); const config = data.Body.toString('utf-8'); res.json({ config }); } catch (error) { if (error.code === 'NoSuchKey') { res.json({ config: '// Конфигурация не найдена' }); } else { console.error(error); res.status(500).send('Error reading server config from S3'); } } }); // Save specific server config app.post('/api/server-configs/:serverId', async (req, res) => { const { serverId } = req.params; const { config } = req.body; if (!config) { return res.status(400).send('Config is required'); } const params = { Bucket: BUCKET_NAME, Key: `filter-manager/config-${serverId}.txt`, Body: config, ContentType: 'text/plain', }; try { await s3.putObject(params).promise(); res.send('Server config saved successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing server config to S3'); } }); // Delete specific server config app.delete('/api/server-configs/:serverId', async (req, res) => { const { serverId } = req.params; const params = { Bucket: BUCKET_NAME, Key: `filter-manager/config-${serverId}.txt`, }; try { await s3.deleteObject(params).promise(); res.send('Server config deleted successfully'); } catch (error) { console.error(error); res.status(500).send('Error deleting server config from S3'); } }); // Delete server completely (config + filters) app.delete('/api/server-configs/:serverId/complete', async (req, res) => { const { serverId } = req.params; try { // Удаляем конфигурацию сервера const configParams = { Bucket: BUCKET_NAME, Key: `filter-manager/config-${serverId}.txt`, }; // Удаляем фильтры сервера const filtersParams = { Bucket: BUCKET_NAME, Key: `filter-manager/server-filters-${serverId}.json`, }; // Удаляем оба файла параллельно await Promise.allSettled([ s3.deleteObject(configParams).promise(), s3.deleteObject(filtersParams).promise() ]); res.send('Server and all associated files deleted successfully'); } catch (error) { console.error(error); res.status(500).send('Error deleting server files from S3'); } }); // --- Server Filters Routes --- // Get server filters app.get('/api/server-filters/:serverId', async (req, res) => { const { serverId } = req.params; const params = { Bucket: BUCKET_NAME, Key: `filter-manager/server-filters-${serverId}.json`, }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let filters = []; try { filters = JSON.parse(fileContent); if (!Array.isArray(filters)) { filters = []; } } catch (parseError) { console.error('Error parsing server filters:', parseError); filters = []; } res.json(filters); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading server filters from S3'); } } }); // Generate MikroTik configuration from server filters app.post('/api/server-filters/generate-config', async (req, res) => { console.log('Received generate-config request with filters:', req.body); const { filters } = req.body; if (!Array.isArray(filters) || filters.length === 0) { console.log('No filters provided, returning empty config'); return res.json({ config: '// No filters to generate configuration' }); } // Group filters by gateway const gatewayGroups = {}; filters.forEach(filter => { if (!gatewayGroups[filter.gateway]) { gatewayGroups[filter.gateway] = []; } gatewayGroups[filter.gateway].push(filter.community); }); console.log('Grouped filters by gateway:', gatewayGroups); let config = '// Frouting filter configuration for MikroTik 7.14+\n'; config += '// Generated automatically\n'; config += `// Date: ${new Date().toISOString()}\n\n`; config += '/routing filter bgp-in-tmp {\n'; // Если есть только один gateway, создаем простую конфигурацию if (Object.keys(gatewayGroups).length === 1) { const [gateway, communities] = Object.entries(gatewayGroups)[0]; config += ` if (\n`; communities.forEach((community, index) => { config += ` (bgp-communities includes ${community})`; if (index < communities.length - 1) { config += ` or \n`; } }); config += `\n )\n {\n set gw ${gateway}; accept;\n }\n else\n {\n reject;\n }\n`; } else { // Если несколько gateway, создаем каскадную структуру const gatewayEntries = Object.entries(gatewayGroups); gatewayEntries.forEach(([gateway, communities], gatewayIndex) => { if (gatewayIndex === 0) { config += ` if (\n`; } else { config += ` else if (\n`; } communities.forEach((community, index) => { config += ` (bgp-communities includes ${community})`; if (index < communities.length - 1) { config += ` or \n`; } }); config += `\n )\n {\n set gw ${gateway}; accept;\n }`; if (gatewayIndex === gatewayEntries.length - 1) { config += `\n else\n {\n reject;\n }\n`; } else { config += `\n`; } }); } config += '}\n'; console.log('Generated config:', config); console.log('Sending response:', { config }); res.json({ config }); }); // Update server filters app.post('/api/server-filters/:serverId', async (req, res) => { const { serverId } = req.params; const { filters } = req.body; // Validate filters structure if (!Array.isArray(filters)) { return res.status(400).send('Filters must be an array'); } // Validate each filter has required fields for (let i = 0; i < filters.length; i++) { const filter = filters[i]; if (!filter.community || !filter.gateway) { return res.status(400).send(`Filter at index ${i} is missing required fields`); } } const params = { Bucket: BUCKET_NAME, Key: `filter-manager/server-filters-${serverId}.json`, Body: JSON.stringify(filters, null, 2), // Pretty print JSON ContentType: 'application/json', }; try { await s3.putObject(params).promise(); res.send('Server filters updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing server filters to S3'); } }); // --- Simple Filters Routes --- // Get simple filters app.get('/api/simple-filters', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'filter-manager/simple-filters.json', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); let filters = []; try { filters = JSON.parse(fileContent); if (!Array.isArray(filters)) { filters = []; } } catch (parseError) { console.error('Error parsing simple filters:', parseError); filters = []; } res.json(filters); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading simple filters from S3'); } } }); // Update simple filters app.post('/api/simple-filters', async (req, res) => { const { filters } = req.body; // Validate filters structure if (!Array.isArray(filters)) { return res.status(400).send('Filters must be an array'); } // Validate each filter has required fields for (let i = 0; i < filters.length; i++) { const filter = filters[i]; if (!filter.community || !filter.gateway) { return res.status(400).send(`Filter at index ${i} is missing required fields`); } } const params = { Bucket: BUCKET_NAME, Key: 'filter-manager/simple-filters.json', Body: JSON.stringify(filters, null, 2), // Pretty print JSON ContentType: 'application/json', }; try { await s3.putObject(params).promise(); res.send('Simple filters updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing simple filters to S3'); } }); // --- Auto URL Routes --- // Get auto URLs from S3 app.get('/api/auto-urls', async (req, res) => { const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/auto_url/urls.txt', }; try { const data = await s3.getObject(params).promise(); const fileContent = data.Body.toString('utf-8'); const urls = fileContent.split('\n').filter(line => line).map(line => { const parts = line.trim().split(/\s+/); const url = parts[0] || ''; const community = parts[1] || ''; return { url, community }; }); res.json(urls); } catch (error) { if (error.code === 'NoSuchKey') { res.json([]); // Return empty array if file does not exist } else { console.error(error); res.status(500).send('Error reading auto URLs from S3'); } } }); // Update auto URLs in S3 app.post('/api/auto-urls', async (req, res) => { const { urls } = req.body; const fileContent = urls.map(u => `${u.url} ${u.community}`).join('\n'); const params = { Bucket: BUCKET_NAME, Key: 'bgp_data/auto_url/urls.txt', Body: fileContent, ContentType: 'text/plain', }; try { await s3.putObject(params).promise(); res.send('Auto URLs updated successfully'); } catch (error) { console.error(error); res.status(500).send('Error writing auto URLs to S3'); } }); // Process auto URLs and update IPs app.post('/api/auto-urls/process', async (req, res) => { const https = require('https'); const http = require('http'); try { // Get current auto URLs const urlsParams = { Bucket: BUCKET_NAME, Key: 'bgp_data/auto_url/urls.txt', }; let urls = []; try { const urlsData = await s3.getObject(urlsParams).promise(); const urlsContent = urlsData.Body.toString('utf-8'); urls = urlsContent.split('\n').filter(line => line).map(line => { const parts = line.trim().split(/\s+/); return { url: parts[0] || '', community: parts[1] || '' }; }); } catch (error) { if (error.code !== 'NoSuchKey') { throw error; } } if (urls.length === 0) { return res.status(400).send('No URLs to process'); } // Get current IPs const ipsParams = { Bucket: BUCKET_NAME, Key: 'bgp_data/ips.txt', }; let currentIps = []; try { const ipsData = await s3.getObject(ipsParams).promise(); const ipsContent = ipsData.Body.toString('utf-8'); currentIps = ipsContent.split('\n').filter(line => line).map(line => { const parts = line.trim().split(/\s+/); return { ipRange: parts[0] || '', community: parts[1] || '' }; }); } catch (error) { if (error.code !== 'NoSuchKey') { throw error; } } // Process each URL const newIps = []; for (const urlData of urls) { try { const url = urlData.url.trim(); const community = urlData.community.trim(); if (!url || !community) continue; // Download content from URL const content = await new Promise((resolve, reject) => { const protocol = url.startsWith('https:') ? https : http; const req = protocol.get(url, (res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', () => resolve(data)); }); req.on('error', reject); req.setTimeout(10000, () => req.destroy()); }); // Parse IPs from content const lines = content.split('\n'); for (const line of lines) { const ip = line.trim(); if (ip && (ip.includes('.') || ip.includes(':'))) { newIps.push({ ipRange: ip, community }); } } } catch (error) { console.error(`Error processing URL ${urlData.url}:`, error); } } // Merge with existing IPs (avoid duplicates) const existingIpRanges = new Set(currentIps.map(ip => ip.ipRange)); const uniqueNewIps = newIps.filter(ip => !existingIpRanges.has(ip.ipRange)); const allIps = [...currentIps, ...uniqueNewIps]; // Save updated IPs const updatedIpsContent = allIps.map(ip => `${ip.ipRange} ${ip.community}`).join('\n'); const updateParams = { Bucket: BUCKET_NAME, Key: 'bgp_data/ips.txt', Body: updatedIpsContent, ContentType: 'text/plain', }; await s3.putObject(updateParams).promise(); res.json({ success: true, message: `Обработано ${urls.length} URL, добавлено ${uniqueNewIps.length} новых IP-адресов`, processedUrls: urls.length, newIpsCount: uniqueNewIps.length, totalIpsCount: allIps.length }); } catch (error) { console.error('Error processing auto URLs:', error); res.status(500).send('Error processing auto URLs'); } }); // The "catchall" handler: for any request that doesn't // match one above, send back React's index.html file. app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); });