diff --git a/config/add-balance.js b/config/add-balance.js index 3aa786c16..75b9b4cda 100644 --- a/config/add-balance.js +++ b/config/add-balance.js @@ -1,36 +1,11 @@ const path = require('path'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); -const { askQuestion, silentExit } = require('./helpers'); +const { askQuestion, silentExit, connectWithTimeout } = require('./helpers'); const Transaction = require('~/models/Transaction'); -const connectDb = require('~/lib/db/connectDb'); const User = require('~/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); - } + await connectWithTimeout(); /** * Show the welcome / help menu diff --git a/config/ban-user.js b/config/ban-user.js index 6db29e815..a2c01a1ce 100644 --- a/config/ban-user.js +++ b/config/ban-user.js @@ -1,36 +1,11 @@ const path = require('path'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); -const { askQuestion, silentExit } = require('./helpers'); +const { askQuestion, silentExit, connectWithTimeout } = require('./helpers'); const banViolation = require('~/cache/banViolation'); -const connectDb = require('~/lib/db/connectDb'); const User = require('~/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); - } + await connectWithTimeout(); console.purple('---------------------'); console.purple('Ban a user account!'); diff --git a/config/create-user.js b/config/create-user.js index 738460c8a..cd0c71325 100644 --- a/config/create-user.js +++ b/config/create-user.js @@ -1,36 +1,11 @@ const path = require('path'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { registerUser } = require('~/server/services/AuthService'); -const { askQuestion, silentExit } = require('./helpers'); -const connectDb = require('~/lib/db/connectDb'); +const { askQuestion, silentExit, connectWithTimeout } = require('./helpers'); const User = require('~/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); - } + await connectWithTimeout(); /** * Show the welcome / help menu diff --git a/config/delete-user.js b/config/delete-user.js new file mode 100644 index 000000000..fe7efe057 --- /dev/null +++ b/config/delete-user.js @@ -0,0 +1,48 @@ +const path = require('path'); +require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); +const { connectWithTimeout, askQuestion, silentExit } = require('./helpers'); +const User = require('~/models/User'); + +(async () => { + await connectWithTimeout(); + + /** + * Show the welcome / help menu + */ + console.purple('---------------'); + console.purple('Deleting a user'); + console.purple('---------------'); + + let email = ''; + if (process.argv.length >= 3) { + email = process.argv[2]; + } else { + email = await askQuestion('Email:'); + } + let user = await User.findOne({ email: email }); + if (user !== null) { + if ((await askQuestion(`Delete user ${user}?`)) === 'y') { + user = await User.findOneAndDelete({ _id: user._id }); + if (user !== null) { + console.yellow(`Deleted user ${user}`); + } else { + console.yellow(`Couldn't delete user with email ${email}`); + } + } + } else { + console.yellow(`Didn't find user with email ${email}`); + } + + silentExit(0); +})(); + +process.on('uncaughtException', (err) => { + if (!err.message.includes('fetch failed')) { + console.error('There was an uncaught error:'); + console.error(err); + } + + if (!err.message.includes('fetch failed')) { + process.exit(1); + } +}); diff --git a/config/helpers.js b/config/helpers.js index 6bc6ed0ef..a86d562eb 100644 --- a/config/helpers.js +++ b/config/helpers.js @@ -6,6 +6,7 @@ const fs = require('fs'); const path = require('path'); const readline = require('readline'); const { execSync } = require('child_process'); +const { connectDb } = require('@librechat/backend/lib/db'); const askQuestion = (query) => { const rl = readline.createInterface({ @@ -43,6 +44,33 @@ const silentExit = (code = 0) => { process.exit(code); }; +async function connectWithTimeout() { + /** + * Connect to the database + * - If it takes a while, we'll warn the user + */ + 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); + } +} + // Set the console colours console.orange = (msg) => console.log('\x1b[33m%s\x1b[0m', msg); console.green = (msg) => console.log('\x1b[32m%s\x1b[0m', msg); @@ -58,5 +86,6 @@ module.exports = { askQuestion, silentExit, isDockerRunning, + connectWithTimeout, deleteNodeModules, }; diff --git a/config/list-balances.js b/config/list-balances.js new file mode 100644 index 000000000..670aba6e5 --- /dev/null +++ b/config/list-balances.js @@ -0,0 +1,39 @@ +const path = require('path'); +require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); +const { connectWithTimeout, silentExit } = require('./helpers'); +const Balance = require('~/models/Balance'); +const User = require('~/models/User'); + +(async () => { + await connectWithTimeout(); + + /** + * Show the welcome / help menu + */ + console.purple('-----------------------------'); + console.purple('Show the balance of all users'); + console.purple('-----------------------------'); + + let users = await User.find({}); + for (const user of users) { + let balance = await Balance.findOne({ user: user._id }); + if (balance !== null) { + console.green(`User ${user.name} has a balance of ${balance.tokenCredits}`); + } else { + console.yellow(`User ${user.name} has no balance`); + } + } + + silentExit(0); +})(); + +process.on('uncaughtException', (err) => { + if (!err.message.includes('fetch failed')) { + console.error('There was an uncaught error:'); + console.error(err); + } + + if (!err.message.includes('fetch failed')) { + process.exit(1); + } +}); diff --git a/docs/features/token_usage.md b/docs/features/token_usage.md index 876b385f9..0dfc83530 100644 --- a/docs/features/token_usage.md +++ b/docs/features/token_usage.md @@ -30,6 +30,14 @@ npm run add-balance danny@librechat.ai 1000 This works well to track your own usage for personal use; 1000 credits = $0.001 (1 mill USD) +## Listing of balances + +To see the balances of your users, you can run: + +```bash +npm run list-balances +``` + ## Notes - With summarization enabled, you will be blocked from making an API request if the cost of the content that you need to summarize + your messages payload exceeds the current balance diff --git a/docs/install/configuration/dotenv.md b/docs/install/configuration/dotenv.md index ea3cd097d..5eda7ba3f 100644 --- a/docs/install/configuration/dotenv.md +++ b/docs/install/configuration/dotenv.md @@ -581,6 +581,7 @@ see: **[Token Usage](../../features/token_usage.md)** - To manually add balances, run the following command:`npm run add-balance` - You can also specify the email and token credit amount to add, e.g.:`npm run add-balance example@example.com 1000` + - To list the balance of every user: `npm run list-balances` > **Note:** 1000 credits = $0.001 (1 mill USD) @@ -602,6 +603,7 @@ see: **[User/Auth System](../configuration/user_auth_system.md)** - `ALLOW_SOCIAL_REGISTRATION`: Enable or disable registration of new user using various social network. Set to `true` or `false` to enable or disable. > **Quick Tip:** Even with registration disabled, add users directly to the database using `npm run create-user`. +> **Quick Tip:** With registration disabled, you can delete a user with `npm run delete-user email@domain.com`. ```bash ALLOW_EMAIL_LOGIN=true diff --git a/docs/install/configuration/user_auth_system.md b/docs/install/configuration/user_auth_system.md index 7020d37ad..17016845d 100644 --- a/docs/install/configuration/user_auth_system.md +++ b/docs/install/configuration/user_auth_system.md @@ -30,6 +30,7 @@ Here's an overview of the general configuration, located in the `.env` file at t > **Note:** OpenID does not support the ability to disable only registration. >> **Quick Tip:** Even with registration disabled, add users directly to the database using `npm run create-user`. If you can't get npm to work, try `sudo docker exec -ti LibreChat sh` first to "ssh" into the container. +>> **Quick Tip:** To delete a user, you can run `docker-compose exec api npm run delete-user email@domain.com` ![image](https://github.com/danny-avila/LibreChat/assets/81851188/52a37d1d-7392-4a9a-a79f-90ed2da7f841) diff --git a/package.json b/package.json index bcf7c2474..77ddd8226 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "scripts": { "update": "node config/update.js", "add-balance": "node config/add-balance.js", + "list-balances": "node config/list-balances.js", "rebuild:package-lock": "node config/packages", "reinstall": "node config/update.js -l -g", "b:reinstall": "bun config/update.js -b -l -g", @@ -25,6 +26,7 @@ "upgrade": "node config/upgrade.js", "create-user": "node config/create-user.js", "ban-user": "node config/ban-user.js", + "delete-user": "node config/delete-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", @@ -53,7 +55,8 @@ "b:client:dev": "cd client && bun run b:dev", "b:test:client": "cd client && bun run b:test", "b:test:api": "cd api && bun run b:test", - "b:balance": "bun config/add-balance.js" + "b:balance": "bun config/add-balance.js", + "b:list-balances": "bun config/list-balances.js" }, "repository": { "type": "git",