Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 14m34s
225 lines
6.8 KiB
JavaScript
225 lines
6.8 KiB
JavaScript
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 = '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: '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: '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');
|
|
}
|
|
});
|
|
|
|
// --- 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');
|
|
}
|
|
});
|
|
|
|
// Новый эндпоинт для получения дат последнего изменения файлов S3
|
|
app.get('/api/s3/last-modified', async (req, res) => {
|
|
try {
|
|
const [domainsHead, asnsHead, serversHead] = await Promise.all([
|
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'domains.txt' }).promise(),
|
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'asns.txt' }).promise(),
|
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'servers.json' }).promise()
|
|
]);
|
|
res.json({
|
|
domainsLastModified: domainsHead.LastModified ? domainsHead.LastModified.toISOString() : null,
|
|
asnsLastModified: asnsHead.LastModified ? asnsHead.LastModified.toISOString() : null,
|
|
serversLastModified: serversHead.LastModified ? serversHead.LastModified.toISOString() : null
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).send('Error fetching last modified dates from S3');
|
|
}
|
|
});
|
|
|
|
// 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}`);
|
|
});
|