diff --git a/README.md b/README.md index 17e79dd..8f748b7 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Also, check this out: [sefinek/Cloudflare-WAF-To-AbuseIPDB](https://github.com/s ## ✅ Features -1. A `config.js` file enabling easy configuration. +1. A [`config.js`](default.config.js) file enabling easy configuration. 2. A simple installer allowing quick integration deployment. 3. Integration with Discord Webhooks (coming soon): - Alerts in case of script errors - Daily summaries of reported IP addresses - +4. Automatic updates. ## 📥 Installation diff --git a/default.config.js b/default.config.js index 05e483b..7f8e433 100644 --- a/default.config.js +++ b/default.config.js @@ -9,6 +9,10 @@ exports.MAIN = { ABUSEIPDB_API_KEY: '', // Secret API key for AbuseIPDB. IP_REPORT_COOLDOWN: 12 * 60 * 60 * 1000, // The minimum time (12 hours) that must pass before reporting the same IP address again. + // Automatic updates + AUTO_UPDATE_SCHEDULE: '0 18 * * *', // Schedule for automatic script updates (CRON format). Default: every day at 18:00 + AUTO_UPDATE_ENABLED: true, // Do you want the script to automatically update to the latest version using 'git pull'? (true = enabled, false = disabled) + // Project GITHUB_REPO: 'https://github.com/sefinek/UFW-AbuseIPDB-Reporter', // If you are using a fork, provide the link to the forked repository here. }; diff --git a/index.js b/index.js index efe2cb3..1e001ab 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,9 @@ const config = require('./config.js'); const { version } = require('./package.json'); const { UFW_LOG_FILE, ABUSEIPDB_API_KEY, SERVER_ID, GITHUB_REPO } = config.MAIN; +// Updates +require('./update.js'); + let fileOffset = 0; const reportToAbuseIPDb = async (logData, categories, comment) => { diff --git a/package-lock.json b/package-lock.json index a0b417d..e87a728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "dependencies": { "axios": "^1.7.9", "chokidar": "^4.0.3", - "ipaddr.js": "^2.2.0" + "cron": "^3.5.0", + "ipaddr.js": "^2.2.0", + "simple-git": "^3.27.0" }, "devDependencies": { "@eslint/js": "^9.18.0", @@ -28,6 +30,27 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "license": "MIT" + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -72,6 +95,33 @@ "node": ">= 0.8" } }, + "node_modules/cron": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.5.0.tgz", + "integrity": "sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.5.0" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -137,6 +187,15 @@ "node": ">= 10" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -158,6 +217,12 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -176,6 +241,21 @@ "type": "individual", "url": "https://paulmillr.com/funding/" } + }, + "node_modules/simple-git": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.27.0.tgz", + "integrity": "sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==", + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } } } } diff --git a/package.json b/package.json index 00fda1b..bde4ad2 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ "dependencies": { "axios": "^1.7.9", "chokidar": "^4.0.3", - "ipaddr.js": "^2.2.0" + "cron": "^3.5.0", + "ipaddr.js": "^2.2.0", + "simple-git": "^3.27.0" }, "devDependencies": { "@eslint/js": "^9.18.0", diff --git a/services/reloadApp.js b/services/reloadApp.js new file mode 100644 index 0000000..67f5167 --- /dev/null +++ b/services/reloadApp.js @@ -0,0 +1,19 @@ +const { exec } = require('node:child_process'); +const ecosystem = require('../ecosystem.config.js'); + +const executeCommand = cmd => + new Promise((resolve, reject) => { + exec(cmd, (err, stdout, stderr) => { + if (err || stderr) reject(err || stderr); + else resolve(stdout); + }); + }); + +module.exports = async () => { + try { + console.log(await executeCommand('npm install --omit=dev')); + console.log(await executeCommand(`pm2 restart ${ecosystem.apps[0].name}`)); + } catch (err) { + console.error(err); + } +}; \ No newline at end of file diff --git a/services/serverAddress.js b/services/serverAddress.js index 2b462ce..cae1c01 100644 --- a/services/serverAddress.js +++ b/services/serverAddress.js @@ -1,7 +1,7 @@ const os = require('node:os'); const axios = require('./axios.js'); const isLocalIP = require('../utils/isLocalIP.js'); -const { MAIN } = require('../config.js'); +const { REFRESHING_IP_ADDRESS } = require('../config.js').MAIN; const ipAddressList = new Set(); @@ -20,9 +20,7 @@ const fetchIPv6Address = () => { Object.values(networkInterfaces).forEach(interfaces => { interfaces.forEach(details => { const ip = details.address; - if (!details.internal && ip && !isLocalIP(ip)) { - ipAddressList.add(ip); - } + if (!details.internal && ip && !isLocalIP(ip)) ipAddressList.add(ip); }); }); }; @@ -35,7 +33,7 @@ const fetchIPAddress = async () => { (async () => { await fetchIPAddress(); - setInterval(fetchIPAddress, MAIN.REFRESHING_IP_ADDRESS); + setInterval(fetchIPAddress, REFRESHING_IP_ADDRESS); // console.debug(ipAddressList); })(); diff --git a/update.js b/update.js new file mode 100644 index 0000000..997f9c1 --- /dev/null +++ b/update.js @@ -0,0 +1,28 @@ +const { SERVER_ID, AUTO_UPDATE_SCHEDULE, AUTO_UPDATE_ENABLED } = require('./config.js').MAIN; + +if (AUTO_UPDATE_ENABLED) { + const simpleGit = require('simple-git'); + const { CronJob } = require('cron'); + const restartApp = require('./services/reloadApp.js'); + const log = require('./utils/log.js'); + + const git = simpleGit(); + + const updateScript = async () => { + log(0, 'The script is being updated...'); + + try { + const { summary } = await git.pull(); + log(0, `Changes: ${summary.changes}; Deletions: ${summary.insertions}; Insertions: ${summary.insertions};`); + + if (SERVER_ID !== 'development') await restartApp(); + } catch (err) { + log(2, err.message); + } + }; + + // https://crontab.guru + new CronJob(AUTO_UPDATE_SCHEDULE || '0 18 * * *', updateScript, null, true, 'UTC'); // At 18:00 + + if (SERVER_ID === 'development') (async () => updateScript())(); +} \ No newline at end of file