Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 94 additions & 28 deletions config/delete-user.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,116 @@
#!/usr/bin/env node
const path = require('path');
const mongoose = require(path.resolve(__dirname, '..', 'api', 'node_modules', 'mongoose'));
const { User } = require('@librechat/data-schemas').createModels(mongoose);
const {
User,
Agent,
Assistant,
Balance,
Transaction,
ConversationTag,
Conversation,
Message,
File,
Key,
MemoryEntry,
PluginAuth,
Prompt,
PromptGroup,
Preset,
Session,
SharedLink,
ToolCall,
Token,
} = require('@librechat/data-schemas').createModels(mongoose);
require('module-alias')({ base: path.resolve(__dirname, '..', 'api') });
const { askQuestion, silentExit } = require('./helpers');
const connect = require('./connect');

async function gracefulExit(code = 0) {
try {
await mongoose.disconnect();
} catch (err) {
console.error('Error disconnecting from MongoDB:', err);
}
silentExit(code);
}

(async () => {
await connect();

/**
* Show the welcome / help menu
*/
console.purple('---------------');
console.purple('Deleting a user');
console.purple('Deleting a user and all related data');
console.purple('---------------');

let email = '';
if (process.argv.length >= 3) {
email = process.argv[2];
} else {
email = await askQuestion('Email:');
// 1) Get email
let email = process.argv[2]?.trim();
if (!email) {
email = (await askQuestion('Email:')).trim();
}

// 2) Find user
const user = await User.findOne({ email: email.toLowerCase() });
if (!user) {
console.yellow(`No user found with email "${email}"`);
return gracefulExit(0);
}
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}`);

// 3) Confirm full deletion
const confirmAll = await askQuestion(
`Really delete user ${user.email} (${user._id}) and ALL their data? (y/N)`,
);
if (confirmAll.toLowerCase() !== 'y') {
console.yellow('Aborted.');
return gracefulExit(0);
}

silentExit(0);
})();
// 4) Ask specifically about transactions
const confirmTx = await askQuestion('Also delete all transaction history for this user? (y/N)');
const deleteTx = confirmTx.toLowerCase() === 'y';

process.on('uncaughtException', (err) => {
if (!err.message.includes('fetch failed')) {
console.error('There was an uncaught error:');
console.error(err);
const uid = user._id.toString();

// 5) Build and run deletion tasks
const tasks = [
Agent.deleteMany({ author: uid }),
Assistant.deleteMany({ user: uid }),
Balance.deleteMany({ user: uid }),
ConversationTag.deleteMany({ user: uid }),
Conversation.deleteMany({ user: uid }),
Message.deleteMany({ user: uid }),
File.deleteMany({ user: uid }),
Key.deleteMany({ userId: uid }),
MemoryEntry.deleteMany({ userId: uid }),
PluginAuth.deleteMany({ userId: uid }),
Prompt.deleteMany({ author: uid }),
PromptGroup.deleteMany({ author: uid }),
Preset.deleteMany({ user: uid }),
Session.deleteMany({ user: uid }),
SharedLink.deleteMany({ user: uid }),
ToolCall.deleteMany({ user: uid }),
Token.deleteMany({ userId: uid }),
];

if (deleteTx) {
tasks.push(Transaction.deleteMany({ user: uid }));
}

await Promise.all(tasks);

// 6) Finally delete the user document itself
await User.deleteOne({ _id: uid });

console.green(`✔ Successfully deleted user ${email} and all associated data.`);
if (!deleteTx) {
console.yellow('⚠️ Transaction history was retained.');
}

return gracefulExit(0);
})().catch(async (err) => {
if (!err.message.includes('fetch failed')) {
console.error('There was an uncaught error:');
console.error(err);
await mongoose.disconnect();
process.exit(1);
}
});