2025-05-30 22:18:13 -04:00
|
|
|
import { connectDb } from '@librechat/backend/db/connect';
|
2024-12-23 11:12:07 +01:00
|
|
|
import {
|
2025-05-30 22:18:13 -04:00
|
|
|
findUser,
|
2024-12-23 11:12:07 +01:00
|
|
|
deleteConvos,
|
2025-05-30 22:18:13 -04:00
|
|
|
deleteMessages,
|
2024-12-23 11:12:07 +01:00
|
|
|
deleteAllUserSessions,
|
|
|
|
|
} from '@librechat/backend/models';
|
2025-11-21 18:03:26 +01:00
|
|
|
import { User, Balance, Transaction, AclEntry, Token, Group } from '@librechat/backend/db/models';
|
2025-05-30 22:18:13 -04:00
|
|
|
|
2023-09-18 15:19:50 -04:00
|
|
|
type TUser = { email: string; password: string };
|
|
|
|
|
|
|
|
|
|
export default async function cleanupUser(user: TUser) {
|
|
|
|
|
const { email } = user;
|
|
|
|
|
try {
|
|
|
|
|
console.log('🤖: global teardown has been started');
|
|
|
|
|
const db = await connectDb();
|
|
|
|
|
console.log('🤖: ✅ Connected to Database');
|
|
|
|
|
|
2025-05-30 22:18:13 -04:00
|
|
|
const foundUser = await findUser({ email });
|
|
|
|
|
if (!foundUser) {
|
|
|
|
|
console.log('🤖: ⚠️ User not found in Database');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const userId = foundUser._id;
|
2023-09-18 15:19:50 -04:00
|
|
|
console.log('🤖: ✅ Found user in Database');
|
|
|
|
|
|
|
|
|
|
// Delete all conversations & associated messages
|
2025-05-30 22:18:13 -04:00
|
|
|
const { deletedCount, messages } = await deleteConvos(userId, {});
|
2023-09-18 15:19:50 -04:00
|
|
|
|
|
|
|
|
if (messages.deletedCount > 0 || deletedCount > 0) {
|
|
|
|
|
console.log(`🤖: ✅ Deleted ${deletedCount} convos & ${messages.deletedCount} messages`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure all user messages are deleted
|
2025-05-30 22:18:13 -04:00
|
|
|
const { deletedCount: deletedMessages } = await deleteMessages({ user: userId });
|
2023-09-18 15:19:50 -04:00
|
|
|
if (deletedMessages > 0) {
|
|
|
|
|
console.log(`🤖: ✅ Deleted ${deletedMessages} remaining message(s)`);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-30 22:18:13 -04:00
|
|
|
// Delete all user sessions
|
|
|
|
|
await deleteAllUserSessions(userId.toString());
|
|
|
|
|
|
2025-11-21 18:03:26 +01:00
|
|
|
// Delete user, balance, transactions, tokens, ACL entries, and remove from groups
|
2025-05-30 22:18:13 -04:00
|
|
|
await Balance.deleteMany({ user: userId });
|
|
|
|
|
await Transaction.deleteMany({ user: userId });
|
2025-11-21 18:03:26 +01:00
|
|
|
await Token.deleteMany({ userId: userId });
|
|
|
|
|
await AclEntry.deleteMany({ principalId: userId });
|
🐘 feat: FerretDB Compatibility (#11769)
* feat: replace unsupported MongoDB aggregation operators for FerretDB compatibility
Replace $lookup, $unwind, $sample, $replaceRoot, and $addFields aggregation
stages which are unsupported on FerretDB v2.x (postgres-documentdb backend).
- Prompt.js: Replace $lookup/$unwind/$project pipelines with find().select().lean()
+ attachProductionPrompts() batch helper. Replace $group/$replaceRoot/$sample
in getRandomPromptGroups with distinct() + Fisher-Yates shuffle.
- Agent/Prompt migration scripts: Replace $lookup anti-join pattern with
distinct() + $nin two-step queries for finding un-migrated resources.
All replacement patterns verified against FerretDB v2.7.0.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: use $pullAll for simple array removals, fix memberIds type mismatches
Replace $pull with $pullAll for exact-value scalar array removals. Both
operators work on MongoDB and FerretDB, but $pullAll is more explicit for
exact matching (no condition expressions).
Fix critical type mismatch bugs where ObjectId values were used against
String[] memberIds arrays in Group queries:
- config/delete-user.js: use string uid instead of ObjectId user._id
- e2e/setup/cleanupUser.ts: convert userId.toString() before query
Harden PermissionService.bulkUpdateResourcePermissions abort handling to
prevent crash when abortTransaction is called after commitTransaction.
All changes verified against FerretDB v2.7.0 and MongoDB Memory Server.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: harden transaction support probe for FerretDB compatibility
Commit the transaction before aborting in supportsTransactions probe, and
wrap abortTransaction in try-catch to prevent crashes when abort is called
after a successful commit (observed behavior on FerretDB).
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: add FerretDB compatibility test suite, retry utilities, and CI config
Add comprehensive FerretDB integration test suite covering:
- $pullAll scalar array operations
- $pull with subdocument conditions
- $lookup replacement (find + manual join)
- $sample replacement (distinct + Fisher-Yates)
- $bit and $bitsAllSet operations
- Migration anti-join pattern
- Multi-tenancy (useDb, scaling, write amplification)
- Sharding proof-of-concept
- Production operations (backup/restore, schema migration, deadlock retry)
Add production retryWithBackoff utility for deadlock recovery during
concurrent index creation on FerretDB/DocumentDB backends.
Add UserController.spec.js tests for deleteUserController (runs in CI).
Configure jest and eslint to isolate FerretDB tests from CI pipelines:
- packages/data-schemas/jest.config.mjs: ignore misc/ directory
- eslint.config.mjs: ignore packages/data-schemas/misc/
Include Docker Compose config for local FerretDB v2.7 + postgres-documentdb,
dedicated jest/tsconfig for the test files, and multi-tenancy findings doc.
Co-authored-by: Cursor <cursoragent@cursor.com>
* style: brace formatting in aclEntry.ts modifyPermissionBits
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor: reorganize retry utilities and update imports
- Moved retryWithBackoff utility to a new file `retry.ts` for better structure.
- Updated imports in `orgOperations.ferretdb.spec.ts` to reflect the new location of retry utilities.
- Removed old import statement for retryWithBackoff from index.ts to streamline exports.
* test: add $pullAll coverage for ConversationTag and PermissionService
Add integration tests for deleteConversationTag verifying $pullAll
removes tags from conversations correctly, and for
syncUserEntraGroupMemberships verifying $pullAll removes user from
non-matching Entra groups while preserving local group membership.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 02:14:34 -05:00
|
|
|
const userIdStr = userId.toString();
|
|
|
|
|
await Group.updateMany({ memberIds: userIdStr }, { $pullAll: { memberIds: [userIdStr] } });
|
2025-11-21 18:03:26 +01:00
|
|
|
await User.deleteMany({ _id: userId });
|
2023-09-18 15:19:50 -04:00
|
|
|
|
|
|
|
|
console.log('🤖: ✅ Deleted user from Database');
|
|
|
|
|
|
|
|
|
|
await db.connection.close();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
process.on('uncaughtException', (err) => console.error('Uncaught Exception:', err));
|