Unlink scripts submodule and commit contents as normal directory
This commit is contained in:
1 parent
4f7dea111f
commit
6488b3ea85
48 files changed
+2876
-1
No files matched your search
@@ -0,0 +1,144 @@
|
||||
const fs = require('node:fs/promises');
|
||||
const path = require('node:path');
|
||||
const { existsSync } = require('node:fs');
|
||||
const { parse } = require('csv-parse/sync');
|
||||
const { stringify } = require('csv-stringify/sync');
|
||||
const logger = require('../../logger.js');
|
||||
|
||||
const CSV_FILE = path.join(__dirname, '..', '..', '..', 'tmp', 'reported_ips.csv');
|
||||
const CSV_COLUMNS = ['Timestamp', 'CF RayID', 'IP', 'Country', 'Hostname', 'Endpoint', 'User-Agent', 'Action taken', 'Status', 'Sefinek API'];
|
||||
const MAX_CSV_SIZE = 1024 * 1024;
|
||||
|
||||
const CSV_STRINGIFY_OPTS = {
|
||||
header: true,
|
||||
columns: CSV_COLUMNS,
|
||||
record_delimiter: '\n',
|
||||
quoted: true,
|
||||
quoted_empty: true,
|
||||
quoted_string: true,
|
||||
};
|
||||
|
||||
const CSV_PARSE_OPTS = {
|
||||
columns: true,
|
||||
skip_empty_lines: true,
|
||||
trim: false,
|
||||
skip_records_with_empty_values: false,
|
||||
};
|
||||
|
||||
const ensureCSVExists = async () => {
|
||||
try {
|
||||
if (existsSync(CSV_FILE)) return;
|
||||
await fs.mkdir(path.dirname(CSV_FILE), { recursive: true });
|
||||
await fs.writeFile(CSV_FILE, stringify([], CSV_STRINGIFY_OPTS));
|
||||
logger.success(`Created missing CSV file: ${CSV_FILE}`);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to ensure CSV exists: ${err.stack}`);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const checkCSVSize = async () => {
|
||||
try {
|
||||
if (!existsSync(CSV_FILE)) return;
|
||||
const stats = await fs.stat(CSV_FILE);
|
||||
|
||||
if (stats.size > MAX_CSV_SIZE) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const backupPath = `${CSV_FILE}.${timestamp}.bkp`;
|
||||
|
||||
await fs.copyFile(CSV_FILE, backupPath);
|
||||
await fs.writeFile(CSV_FILE, stringify([], CSV_STRINGIFY_OPTS));
|
||||
logger.success(`CSV size limit exceeded (${(MAX_CSV_SIZE / (1024 * 1024)).toFixed(2)} MB). Backup saved as "${path.basename(backupPath)}", original file cleared.`);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Failed to check or clear CSV size: ${err.stack}`);
|
||||
}
|
||||
};
|
||||
|
||||
const logToCSV = async (event, status = 'N/A', sefinekAPI = false) => {
|
||||
try {
|
||||
await ensureCSVExists();
|
||||
await checkCSVSize();
|
||||
|
||||
const {
|
||||
rayName, clientIP, clientCountryName,
|
||||
clientRequestHTTPHost, clientRequestPath,
|
||||
userAgent, action,
|
||||
} = event;
|
||||
|
||||
const row = {
|
||||
'Timestamp': new Date().toISOString(),
|
||||
'CF RayID': rayName,
|
||||
'IP': clientIP,
|
||||
'Country': clientCountryName,
|
||||
'Hostname': clientRequestHTTPHost,
|
||||
'Endpoint': clientRequestPath,
|
||||
'User-Agent': userAgent,
|
||||
'Action taken': action.toUpperCase(),
|
||||
'Status': status,
|
||||
'Sefinek API': String(sefinekAPI),
|
||||
};
|
||||
|
||||
const line = stringify([row], { ...CSV_STRINGIFY_OPTS, header: false });
|
||||
await fs.appendFile(CSV_FILE, line);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to log event to CSV: ${err.stack}`);
|
||||
}
|
||||
};
|
||||
|
||||
const readReportedIPs = async () => {
|
||||
if (!existsSync(CSV_FILE)) return [];
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(CSV_FILE, 'utf-8');
|
||||
const records = parse(content, CSV_PARSE_OPTS);
|
||||
|
||||
return records.map(row => ({
|
||||
timestamp: Date.parse(row['Timestamp']),
|
||||
rayId: row['CF RayID'],
|
||||
ip: row['IP'],
|
||||
country: row['Country'],
|
||||
hostname: row['Hostname'],
|
||||
endpoint: row['Endpoint'],
|
||||
userAgent: row['User-Agent'],
|
||||
action: row['Action taken'],
|
||||
status: row['Status'],
|
||||
sefinekAPI: row['Sefinek API'] === 'true',
|
||||
}));
|
||||
} catch (err) {
|
||||
logger.error(`Failed to parse or map CSV content: ${err.stack}`);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const batchUpdateSefinekAPIInCSV = async (rayIds = []) => {
|
||||
if (!existsSync(CSV_FILE) || !rayIds.length) return;
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(CSV_FILE, 'utf-8');
|
||||
const records = parse(content, CSV_PARSE_OPTS);
|
||||
|
||||
const raySet = new Set(rayIds);
|
||||
let updated = false;
|
||||
|
||||
for (const row of records) {
|
||||
if (raySet.has(row['CF RayID']) && row['Sefinek API'] !== 'true') {
|
||||
row['Sefinek API'] = 'true';
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
const output = stringify(records, CSV_STRINGIFY_OPTS);
|
||||
await fs.writeFile(CSV_FILE, output);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Batch CSV update failed: ${err.stack}`);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Object.freeze({
|
||||
logToCSV,
|
||||
readReportedIPs,
|
||||
batchUpdateSefinekAPIInCSV,
|
||||
});
|
||||
Reference in new issue
Block a user