Move usermethods and models to data-schema

This commit is contained in:
Cha 2025-05-29 16:37:31 +08:00 committed by Danny Avila
parent 4808c5be48
commit 4049b5572c
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
93 changed files with 2396 additions and 1267 deletions

View file

@ -17,9 +17,9 @@ const { logger, getFlowStateManager, sendEvent } = require('~/config');
const { encryptV2, decryptV2 } = require('~/server/utils/crypto');
const { getActions, deleteActions } = require('~/models/Action');
const { deleteAssistant } = require('~/models/Assistant');
const { findToken } = require('~/models/Token');
const { logAxiosError } = require('~/utils');
const { getLogStores } = require('~/cache');
const db = require('~/lib/db/connectDb');
const JWT_SECRET = process.env.JWT_SECRET;
const toolNameRegex = /^[a-zA-Z0-9_-]+$/;
@ -231,9 +231,10 @@ async function createActionTool({
};
const tokenPromises = [];
tokenPromises.push(findToken({ userId, type: 'oauth', identifier }));
const { Token } = db.models;
tokenPromises.push(Token.findToken({ userId, type: 'oauth', identifier }));
tokenPromises.push(
findToken({
Token.findToken({
userId,
type: 'oauth_refresh',
identifier: `${identifier}:refresh`,

View file

@ -1,28 +1,12 @@
const bcrypt = require('bcryptjs');
const { webcrypto } = require('node:crypto');
const { SystemRoles, errorsToString } = require('librechat-data-provider');
const {
findUser,
countUsers,
createUser,
updateUser,
getUserById,
generateToken,
deleteUserById,
} = require('~/models/userMethods');
const {
createToken,
findToken,
deleteTokens,
findSession,
deleteSession,
createSession,
generateRefreshToken,
} = require('~/models');
const { isEnabled, checkEmailConfig, sendEmail } = require('~/server/utils');
const { isEmailDomainAllowed } = require('~/server/services/domains');
const { registerSchema } = require('~/strategies/validators');
const { logger } = require('~/config');
const db = require('~/lib/db/connectDb');
const { getBalanceConfig } = require('~/server/services/Config');
const domains = {
client: process.env.DOMAIN_CLIENT,
@ -40,13 +24,14 @@ const genericVerificationMessage = 'Please check your email to verify your email
* @returns
*/
const logoutUser = async (req, refreshToken) => {
const { Session } = db.models;
try {
const userId = req.user._id;
const session = await findSession({ userId: userId, refreshToken });
const session = await Session.findSession({ userId: userId, refreshToken });
if (session) {
try {
await deleteSession({ sessionId: session._id });
await Session.deleteSession({ sessionId: session._id });
} catch (deleteErr) {
logger.error('[logoutUser] Failed to delete session.', deleteErr);
return { status: 500, message: 'Failed to delete session.' };
@ -98,7 +83,7 @@ const sendVerificationEmail = async (user) => {
template: 'verifyEmail.handlebars',
});
await createToken({
await db.models.Token.createToken({
userId: user._id,
email: user.email,
token: hash,
@ -117,7 +102,8 @@ const verifyEmail = async (req) => {
const { email, token } = req.body;
const decodedEmail = decodeURIComponent(email);
const user = await findUser({ email: decodedEmail }, 'email _id emailVerified');
const { User, Token } = db.models;
const user = await User.findUser({ email: decodedEmail }, 'email _id emailVerified');
if (!user) {
logger.warn(`[verifyEmail] [User not found] [Email: ${decodedEmail}]`);
@ -129,7 +115,7 @@ const verifyEmail = async (req) => {
return { message: 'Email already verified', status: 'success' };
}
let emailVerificationData = await findToken({ email: decodedEmail });
let emailVerificationData = await Token.findToken({ email: decodedEmail });
if (!emailVerificationData) {
logger.warn(`[verifyEmail] [No email verification data found] [Email: ${decodedEmail}]`);
@ -145,13 +131,13 @@ const verifyEmail = async (req) => {
return new Error('Invalid or expired email verification token');
}
const updatedUser = await updateUser(emailVerificationData.userId, { emailVerified: true });
const updatedUser = await User.updateUser(emailVerificationData.userId, { emailVerified: true });
if (!updatedUser) {
logger.warn(`[verifyEmail] [User update failed] [Email: ${decodedEmail}]`);
return new Error('Failed to update user verification status');
}
await deleteTokens({ token: emailVerificationData.token });
await Token.deleteTokens({ token: emailVerificationData.token });
logger.info(`[verifyEmail] Email verification successful [Email: ${decodedEmail}]`);
return { message: 'Email verification was successful', status: 'success' };
};
@ -162,6 +148,7 @@ const verifyEmail = async (req) => {
* @returns {Promise<{status: number, message: string, user?: MongoUser}>}
*/
const registerUser = async (user, additionalData = {}) => {
const { User } = db.models;
const { error } = registerSchema.safeParse(user);
if (error) {
const errorMessage = errorsToString(error.errors);
@ -178,7 +165,7 @@ const registerUser = async (user, additionalData = {}) => {
let newUserId;
try {
const existingUser = await findUser({ email }, 'email _id');
const existingUser = await User.findUser({ email }, 'email _id');
if (existingUser) {
logger.info(
@ -200,7 +187,7 @@ const registerUser = async (user, additionalData = {}) => {
}
//determine if this is the first registered user (not counting anonymous_user)
const isFirstRegisteredUser = (await countUsers()) === 0;
const isFirstRegisteredUser = (await User.countUsers()) === 0;
const salt = bcrypt.genSaltSync(10);
const newUserData = {
@ -216,7 +203,9 @@ const registerUser = async (user, additionalData = {}) => {
const emailEnabled = checkEmailConfig();
const disableTTL = isEnabled(process.env.ALLOW_UNVERIFIED_EMAIL_LOGIN);
const newUser = await createUser(newUserData, disableTTL, true);
const balanceConfig = await getBalanceConfig();
const newUser = await User.createUser(newUserData, balanceConfig, disableTTL, true);
newUserId = newUser._id;
if (emailEnabled && !newUser.emailVerified) {
await sendVerificationEmail({
@ -225,14 +214,14 @@ const registerUser = async (user, additionalData = {}) => {
name,
});
} else {
await updateUser(newUserId, { emailVerified: true });
await User.updateUser(newUserId, { emailVerified: true });
}
return { status: 200, message: genericVerificationMessage };
} catch (err) {
logger.error('[registerUser] Error in registering user:', err);
if (newUserId) {
const result = await deleteUserById(newUserId);
const result = await User.deleteUserById(newUserId);
logger.warn(
`[registerUser] [Email: ${email}] [Temporary User deleted: ${JSON.stringify(result)}]`,
);
@ -247,7 +236,8 @@ const registerUser = async (user, additionalData = {}) => {
*/
const requestPasswordReset = async (req) => {
const { email } = req.body;
const user = await findUser({ email }, 'email _id');
const { User, Token } = db.models;
const user = await User.findUser({ email }, 'email _id');
const emailEnabled = checkEmailConfig();
logger.warn(`[requestPasswordReset] [Password reset request initiated] [Email: ${email}]`);
@ -259,11 +249,11 @@ const requestPasswordReset = async (req) => {
};
}
await deleteTokens({ userId: user._id });
await Token.deleteTokens({ userId: user._id });
const [resetToken, hash] = createTokenHash();
await createToken({
await Token.createToken({
userId: user._id,
token: hash,
createdAt: Date.now(),
@ -308,7 +298,8 @@ const requestPasswordReset = async (req) => {
* @returns
*/
const resetPassword = async (userId, token, password) => {
let passwordResetToken = await findToken({
const { User, Token } = db.models;
let passwordResetToken = await Token.findToken({
userId,
});
@ -323,7 +314,7 @@ const resetPassword = async (userId, token, password) => {
}
const hash = bcrypt.hashSync(password, 10);
const user = await updateUser(userId, { password: hash });
const user = await User.updateUser(userId, { password: hash });
if (checkEmailConfig()) {
await sendEmail({
@ -338,7 +329,7 @@ const resetPassword = async (userId, token, password) => {
});
}
await deleteTokens({ token: passwordResetToken.token });
await Token.deleteTokens({ token: passwordResetToken.token });
logger.info(`[resetPassword] Password reset successful. [Email: ${user.email}]`);
return { message: 'Password reset was successful' };
};
@ -353,19 +344,20 @@ const resetPassword = async (userId, token, password) => {
*/
const setAuthTokens = async (userId, res, sessionId = null) => {
try {
const user = await getUserById(userId);
const token = await generateToken(user);
const { User, Session } = db.models;
const user = await User.getUserById(userId);
const token = await User.generateToken(user);
let session;
let refreshToken;
let refreshTokenExpires;
if (sessionId) {
session = await findSession({ sessionId: sessionId }, { lean: false });
session = await Session.findSession({ sessionId: sessionId }, { lean: false });
refreshTokenExpires = session.expiration.getTime();
refreshToken = await generateRefreshToken(session);
refreshToken = await Session.generateRefreshToken(session);
} else {
const result = await createSession(userId);
const result = await Session.createSession(userId);
session = result.session;
refreshToken = result.refreshToken;
refreshTokenExpires = session.expiration.getTime();
@ -444,8 +436,9 @@ const setOpenIDAuthTokens = (tokenset, res) => {
const resendVerificationEmail = async (req) => {
try {
const { email } = req.body;
await deleteTokens(email);
const user = await findUser({ email }, 'email _id name');
const { User, Token } = db.models;
await Token.deleteTokens(email);
const user = await User.findUser({ email }, 'email _id name');
if (!user) {
logger.warn(`[resendVerificationEmail] [No user found] [Email: ${email}]`);
@ -470,7 +463,7 @@ const resendVerificationEmail = async (req) => {
template: 'verifyEmail.handlebars',
});
await createToken({
await Token.createToken({
userId: user._id,
email: user.email,
token: hash,

View file

@ -2,11 +2,10 @@ const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { resizeImageBuffer } = require('../images/resize');
const { updateUser } = require('~/models/userMethods');
const { updateFile } = require('~/models/File');
const { logger } = require('~/config');
const { saveBufferToAzure } = require('./crud');
const db = require('~/lib/db/connectDb');
/**
* Uploads an image file to Azure Blob Storage.
* It resizes and converts the image similar to your Firebase implementation.
@ -108,7 +107,7 @@ async function processAzureAvatar({ buffer, userId, manual, basePath = 'images',
const isManual = manual === 'true';
const url = `${downloadURL}?manual=${isManual}`;
if (isManual) {
await updateUser(userId, { avatar: url });
await db.models?.User.updateUser(userId, { avatar: url });
}
return url;
} catch (error) {

View file

@ -2,10 +2,10 @@ const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { resizeImageBuffer } = require('../images/resize');
const { updateUser } = require('~/models/userMethods');
const { saveBufferToFirebase } = require('./crud');
const { updateFile } = require('~/models/File');
const { logger } = require('~/config');
const db = require('~/lib/db/connectDb');
/**
* Converts an image file to the target format. The function first resizes the image based on the specified
@ -99,7 +99,7 @@ async function processFirebaseAvatar({ buffer, userId, manual }) {
const url = `${downloadURL}?manual=${isManual}`;
if (isManual) {
await updateUser(userId, { avatar: url });
await db.models.User.updateUser(userId, { avatar: url });
}
return url;

View file

@ -2,8 +2,8 @@ const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { resizeImageBuffer } = require('../images/resize');
const { updateUser } = require('~/models/userMethods');
const { updateFile } = require('~/models/File');
const db = require('~/lib/db/connectDb');
/**
* Converts an image file to the target format. The function first resizes the image based on the specified
@ -141,7 +141,7 @@ async function processLocalAvatar({ buffer, userId, manual }) {
let url = `${urlRoute}?manual=${isManual}`;
if (isManual) {
await updateUser(userId, { avatar: url });
await db.models?.User.updateUser(userId, { avatar: url });
}
return url;

View file

@ -2,10 +2,10 @@ const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { resizeImageBuffer } = require('../images/resize');
const { updateUser } = require('~/models/userMethods');
const { saveBufferToS3 } = require('./crud');
const { updateFile } = require('~/models/File');
const { logger } = require('~/config');
const db = require('~/lib/db/connectDb');
const defaultBasePath = 'images';
@ -102,7 +102,7 @@ async function processS3Avatar({ buffer, userId, manual, basePath = defaultBaseP
try {
const downloadURL = await saveBufferToS3({ userId, buffer, fileName: 'avatar.png', basePath });
if (manual === 'true') {
await updateUser(userId, { avatar: downloadURL });
await db.models?.User.updateUser(userId, { avatar: downloadURL });
}
return downloadURL;
} catch (error) {

View file

@ -1,8 +1,7 @@
const { ErrorTypes } = require('librechat-data-provider');
const { encrypt, decrypt } = require('~/server/utils');
const { updateUser, Key } = require('~/models');
const { logger } = require('~/config');
const db = require('~/lib/db/connectDb');
/**
* Updates the plugins for a user based on the action specified (install/uninstall).
* @async
@ -17,10 +16,11 @@ const { logger } = require('~/config');
const updateUserPluginsService = async (user, pluginKey, action) => {
try {
const userPlugins = user.plugins || [];
const { User } = db.models;
if (action === 'install') {
return await updateUser(user._id, { plugins: [...userPlugins, pluginKey] });
return await User.updateUser(user._id, { plugins: [...userPlugins, pluginKey] });
} else if (action === 'uninstall') {
return await updateUser(user._id, {
return await User.updateUser(user._id, {
plugins: userPlugins.filter((plugin) => plugin !== pluginKey),
});
}
@ -42,7 +42,7 @@ const updateUserPluginsService = async (user, pluginKey, action) => {
* an error indicating that there is no user key available.
*/
const getUserKey = async ({ userId, name }) => {
const keyValue = await Key.findOne({ userId, name }).lean();
const keyValue = await db.models.Key.findOne({ userId, name }).lean();
if (!keyValue) {
throw new Error(
JSON.stringify({
@ -89,7 +89,7 @@ const getUserKeyValues = async ({ userId, name }) => {
* returns its expiry date. If the key is not found, it returns null for the expiry date.
*/
const getUserKeyExpiry = async ({ userId, name }) => {
const keyValue = await Key.findOne({ userId, name }).lean();
const keyValue = await db.models.Key.findOne({ userId, name }).lean();
if (!keyValue) {
return { expiresAt: null };
}
@ -123,7 +123,7 @@ const updateUserKey = async ({ userId, name, value, expiresAt = null }) => {
// make sure to remove if already present
updateQuery.$unset = { expiresAt };
}
return await Key.findOneAndUpdate({ userId, name }, updateQuery, {
return await db.models.Key.findOneAndUpdate({ userId, name }, updateQuery, {
upsert: true,
new: true,
}).lean();
@ -143,10 +143,10 @@ const updateUserKey = async ({ userId, name, value, expiresAt = null }) => {
*/
const deleteUserKey = async ({ userId, name, all = false }) => {
if (all) {
return await Key.deleteMany({ userId });
return await db.models.Key.deleteMany({ userId });
}
await Key.findOneAndDelete({ userId, name }).lean();
await db.models.Key.findOneAndDelete({ userId, name }).lean();
};
/**

View file

@ -1,6 +1,7 @@
const { webcrypto } = require('node:crypto');
const { decryptV3, decryptV2 } = require('../utils/crypto');
const { hashBackupCode } = require('~/server/utils/crypto');
const db = require('~/lib/db/connectDb');
// Base32 alphabet for TOTP secret encoding.
const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
@ -172,8 +173,7 @@ const verifyBackupCode = async ({ user, backupCode }) => {
: codeObj,
);
// Update the user record with the marked backup code.
const { updateUser } = require('~/models');
await updateUser(user._id, { backupCodes: updatedBackupCodes });
await db.models.User.updateUser(user._id, { backupCodes: updatedBackupCodes });
return true;
}
return false;