diff --git a/api/cache/banViolation.js b/api/cache/banViolation.js index f00296d3b..f683f82af 100644 --- a/api/cache/banViolation.js +++ b/api/cache/banViolation.js @@ -47,7 +47,7 @@ const banViolation = async (req, res, errorMessage) => { res.clearCookie('refreshToken'); const banLogs = getLogStores('ban'); - const duration = banLogs.opts.ttl; + const duration = errorMessage.duration || banLogs.opts.ttl; if (duration <= 0) { return; @@ -55,6 +55,7 @@ const banViolation = async (req, res, errorMessage) => { req.ip = removePorts(req); console.log(`[BAN] Banning user ${user_id} @ ${req.ip} for ${duration / 1000 / 60} minutes`); + const expiresAt = Date.now() + duration; await banLogs.set(user_id, { type, violation_count, duration, expiresAt }); await banLogs.set(req.ip, { type, user_id, violation_count, duration, expiresAt }); diff --git a/config/ban-user.js b/config/ban-user.js new file mode 100644 index 000000000..c73d662cf --- /dev/null +++ b/config/ban-user.js @@ -0,0 +1,99 @@ +const connectDb = require('@librechat/backend/lib/db/connectDb'); +const { askQuestion, silentExit } = require('./helpers'); +const banViolation = require('../api/cache/banViolation'); +const User = require('@librechat/backend/models/User'); + +(async () => { + /** + * Connect to the database + * - If it takes a while, we'll warn the user + */ + // Warn the user if this is taking a while + let timeout = setTimeout(() => { + console.orange( + 'This is taking a while... You may need to check your connection if this fails.', + ); + timeout = setTimeout(() => { + console.orange('Still going... Might as well assume the connection failed...'); + timeout = setTimeout(() => { + console.orange('Error incoming in 3... 2... 1...'); + }, 13000); + }, 10000); + }, 5000); + // Attempt to connect to the database + try { + console.orange('Warming up the engines...'); + await connectDb(); + clearTimeout(timeout); + } catch (e) { + console.error(e); + silentExit(1); + } + + console.purple('---------------------'); + console.purple('Ban a user account!'); + console.purple('---------------------'); + + let email = ''; + let duration = ''; + + if (process.argv.length >= 4) { + // Check if there are enough command-line arguments. + email = process.argv[2]; + duration = parseInt(process.argv[3]); // Parse the duration as an integer. + } else { + console.orange('Usage: npm run ban-user '); + console.orange('Note: if you do not pass in the arguments, you will be prompted for them.'); + console.purple('--------------------------'); + } + + if (!email) { + email = await askQuestion('Email:'); + } + + if (!duration) { + const durationInMinutes = await askQuestion('Duration (in minutes):'); + duration = parseInt(durationInMinutes) * 60000; + } + + if (isNaN(duration) || duration <= 0) { + console.red('Error: Invalid duration!'); + silentExit(1); + } + + if (!email.includes('@')) { + console.red('Error: Invalid email address!'); + silentExit(1); + } + + const user = await User.findOne({ email }).lean(); + if (!user) { + console.red('Error: No user with that email was found!'); + silentExit(1); + } else { + console.purple(`Found user: ${user.email}`); + } + + const req = {}; + const res = { + clearCookie: () => {}, + status: function () { + return this; + }, + json: function () { + return this; + }, + }; + + const errorMessage = { + type: 'concurrent', + violation_count: 20, + user_id: user._id, + prev_count: 0, + duration: duration, + }; + + await banViolation(req, res, errorMessage); + + silentExit(0); +})(); diff --git a/package.json b/package.json index 87afd1911..8cd7a6b3a 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "stop:deployed": "docker-compose -f ./deploy-compose.yml down", "upgrade": "node config/upgrade.js", "create-user": "node config/create-user.js", + "ban-user": "node config/ban-user.js", "backend": "cross-env NODE_ENV=production node api/server/index.js", "backend:dev": "cross-env NODE_ENV=development npx nodemon api/server/index.js", "backend:stop": "node config/stop-backend.js",