diff --git a/index.js b/index.js index 56cf5d0..828ffa4 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,12 @@ // Copyright 2024-2026 © by Sefinek. All Rights Reserved. // https://sefinek.net -const fs = require('node:fs'); -const chokidar = require('chokidar'); const { parseUfwLog } = require('ufw-log-parser'); +const config = require('./config.js'); +require('./scripts/validations/index.js')(config.MAIN); const banner = require('./scripts/banners/ufw.js'); const { axiosService } = require('./scripts/services/axios.js'); +const tailFile = require('./scripts/services/tailFile.js'); const { saveBufferToFile, loadBufferFromFile, sendBulkReport, BULK_REPORT_BUFFER } = require('./scripts/services/bulk.js'); const { reportedIPs, loadReportedIPs, saveReportedIPs, isIPReportedRecently, markIPAsReported } = require('./scripts/services/cache.js'); const ABUSE_STATE = require('./scripts/services/state.js'); @@ -13,7 +14,6 @@ const { refreshServerIPs, getServerIPs } = require('./scripts/services/ipFetcher const { repoSlug, repoUrl } = require('./scripts/repo.js'); const isSpecialPurposeIP = require('./scripts/isSpecialPurposeIP.js'); const logger = require('./scripts/logger.js'); -const config = require('./config.js'); const { UFW_LOG_FILE, SERVER_ID, EXTENDED_LOGS, AUTO_UPDATE_ENABLED, AUTO_UPDATE_SCHEDULE, DISCORD_WEBHOOK_ENABLED, DISCORD_WEBHOOK_URL } = config.MAIN; const RATE_LIMIT_LOG_INTERVAL = 10 * 60 * 1000; @@ -25,7 +25,7 @@ const nextRateLimitReset = () => { return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 1)); }; -let LAST_RATELIMIT_LOG = 0, LAST_STATS_LOG = 0, RATELIMIT_RESET = nextRateLimitReset(), fileOffset = 0; +let LAST_RATELIMIT_LOG = 0, LAST_STATS_LOG = 0, RATELIMIT_RESET = nextRateLimitReset(); const checkRateLimit = async () => { const now = Date.now(); @@ -100,9 +100,15 @@ const reportIp = async ({ srcIp, dpt = 'N/A', proto = 'N/A', timestamp }, catego }; const processLogLine = async (line, test = false) => { - if (!line.includes('[UFW BLOCK]')) return logger.warn(`Ignoring invalid line: ${line}`); + if (!line || !line.includes('[UFW BLOCK]')) return; + + let data; + try { + data = parseUfwLog(line); + } catch (err) { + return logger.error(`Failed to parse UFW log line: ${err.message}`); + } - const data = parseUfwLog(line); const { srcIp, proto, dpt } = data; if (!srcIp) return logger.error(`Missing SRC in the log line: ${line}`, { ping: true }); @@ -178,57 +184,8 @@ const processLogLine = async (line, test = false) => { await sendBulkReport(); } - // Check UFW_LOG_FILE - if (!fs.existsSync(UFW_LOG_FILE)) { - logger.error(`Log file ${UFW_LOG_FILE} does not exist`, { ping: true }); - return; - } - - // Watch - let incompleteLine = ''; - let processing = Promise.resolve(); - - fileOffset = fs.statSync(UFW_LOG_FILE).size; - chokidar.watch(UFW_LOG_FILE, { persistent: true, ignoreInitial: true }) - .on('change', filePath => { - const stats = fs.statSync(filePath); - if (stats.size < fileOffset) { - incompleteLine = ''; - fileOffset = 0; - logger.info('The file has been truncated, and the offset has been reset'); - } - - const start = fileOffset; - fileOffset = stats.size; - - processing = processing.then(async () => { - let data = ''; - try { - await new Promise((resolve, reject) => { - fs.createReadStream(filePath, { start, encoding: 'utf8' }) - .on('data', chunk => { data += chunk; }) - .on('end', resolve) - .on('error', reject); - }); - } catch (err) { - logger.error(`Failed to read log chunk: ${err.message}`); - return; - } - - const text = incompleteLine + data; - const lines = text.split('\n'); - incompleteLine = lines.pop(); - - for (const line of lines) { - if (!line.trim()) continue; - try { - await processLogLine(line); - } catch (err) { - logger.error(`Failed to process log line: ${err.message}`); - } - } - }); - }); + // Tail file + await tailFile(UFW_LOG_FILE, processLogLine); // Summaries if (DISCORD_WEBHOOK_ENABLED && DISCORD_WEBHOOK_URL) await require('./scripts/services/summaries.js')(); diff --git a/package-lock.json b/package-lock.json index 4ff497f..ffd54ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,8 @@ "version": "0.4.7", "license": "GPL-3.0", "dependencies": { + "@logdna/tail-file": "^4.0.2", "axios": "^1.13.6", - "axios-retry": "^4.5.0", - "chokidar": "^5.0.0", "cron": "^4.4.0", "csv-parse": "^6.1.0", "csv-stringify": "^6.6.0", @@ -19,11 +18,15 @@ "ipaddr.js": "^2.3.0", "semver": "^7.7.4", "simple-git": "^3.32.3", + "split2": "^4.2.0", "ufw-log-parser": "^0.1.3" }, "devDependencies": { "@eslint/js": "^10.0.1", "globals": "^17.3.0" + }, + "engines": { + "node": ">=20" } }, "node_modules/@eslint/js": { @@ -62,6 +65,15 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT" }, + "node_modules/@logdna/tail-file": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@logdna/tail-file/-/tail-file-4.0.2.tgz", + "integrity": "sha512-aEZF3LanJoewsQ9J1CRk5wvOqvQvw4lhxitSMXz3sQPgfezamzwW+pGvKXxvht7EvJEYSlYPxfSjL6T2bDSEkg==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, "node_modules/@types/luxon": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", @@ -85,18 +97,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/axios-retry": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", - "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", - "license": "Apache-2.0", - "dependencies": { - "is-retry-allowed": "^2.2.0" - }, - "peerDependencies": { - "axios": "0.x || 1.x" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -110,21 +110,6 @@ "node": ">= 0.4" } }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -406,18 +391,6 @@ "node": ">= 10" } }, - "node_modules/is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/luxon": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", @@ -469,19 +442,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -509,6 +469,15 @@ "url": "https://github.com/steveukx/git-js?sponsor=1" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/ufw-log-parser": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ufw-log-parser/-/ufw-log-parser-0.1.3.tgz", diff --git a/package.json b/package.json index 6f9fe15..42a8615 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ufw-abuseipdb-reporter", - "version": "0.4.7", + "version": "0.5.0", "homepage": "https://github.com/sefinek/UFW-AbuseIPDB-Reporter#readme", "bugs": { "url": "https://github.com/sefinek/UFW-AbuseIPDB-Reporter/issues" @@ -20,9 +20,8 @@ "update": "npm run pull && npm ci --omit=dev && pm2 restart ufw-abuseipdb" }, "dependencies": { + "@logdna/tail-file": "^4.0.2", "axios": "^1.13.6", - "axios-retry": "^4.5.0", - "chokidar": "^5.0.0", "cron": "^4.4.0", "csv-parse": "^6.1.0", "csv-stringify": "^6.6.0", @@ -30,10 +29,14 @@ "ipaddr.js": "^2.3.0", "semver": "^7.7.4", "simple-git": "^3.32.3", + "split2": "^4.2.0", "ufw-log-parser": "^0.1.3" }, "devDependencies": { "@eslint/js": "^10.0.1", "globals": "^17.3.0" + }, + "engines": { + "node": ">=20" } }