Unlink scripts submodule and commit contents as normal directory

This commit is contained in:
Kyle Speight committed 2026-06-14 03:42:18 -07:00
1 parent 4f7dea111f
commit 6488b3ea85
48 files changed
+2876 -1

No files matched your search

+144
View File
@@ -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,
});