mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 02:10:15 +01:00
fix(auth): replace mongoose model references with new function imports
- Updated AuthController, checkBan middleware, localStrategy, and openidStrategy to use new function imports for user operations. - Removed unused mongoose imports to streamline the codebase. - Enhanced consistency across user-related operations by utilizing the centralized methods for user management.
This commit is contained in:
parent
90ac2b51cd
commit
6e278f6932
5 changed files with 12 additions and 241 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
const cookies = require('cookie');
|
const cookies = require('cookie');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const openIdClient = require('openid-client');
|
const openIdClient = require('openid-client');
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const {
|
const {
|
||||||
|
|
@ -10,11 +9,11 @@ const {
|
||||||
requestPasswordReset,
|
requestPasswordReset,
|
||||||
setOpenIDAuthTokens,
|
setOpenIDAuthTokens,
|
||||||
} = require('~/server/services/AuthService');
|
} = require('~/server/services/AuthService');
|
||||||
|
const { findUser, getUserById } = require('~/models');
|
||||||
const { getOpenIdConfig } = require('~/strategies');
|
const { getOpenIdConfig } = require('~/strategies');
|
||||||
const { isEnabled } = require('~/server/utils');
|
const { isEnabled } = require('~/server/utils');
|
||||||
|
|
||||||
const Session = require('~/db/models').Session;
|
const Session = require('~/db/models').Session;
|
||||||
const User = require('~/db/models').User;
|
|
||||||
|
|
||||||
const registrationController = async (req, res) => {
|
const registrationController = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -73,7 +72,7 @@ const refreshController = async (req, res) => {
|
||||||
const openIdConfig = getOpenIdConfig();
|
const openIdConfig = getOpenIdConfig();
|
||||||
const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken);
|
const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken);
|
||||||
const claims = tokenset.claims();
|
const claims = tokenset.claims();
|
||||||
const user = await User.findUser({ email: claims.email });
|
const user = await findUser({ email: claims.email });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(401).redirect('/login');
|
return res.status(401).redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +85,7 @@ const refreshController = async (req, res) => {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
|
const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
|
||||||
const user = await User.getUserById(payload.id, '-password -__v -totpSecret');
|
const user = await getUserById(payload.id, '-password -__v -totpSecret');
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(401).redirect('/login');
|
return res.status(401).redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
const { Keyv } = require('keyv');
|
const { Keyv } = require('keyv');
|
||||||
const uap = require('ua-parser-js');
|
const uap = require('ua-parser-js');
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const { ViolationTypes } = require('librechat-data-provider');
|
const { ViolationTypes } = require('librechat-data-provider');
|
||||||
const { isEnabled, removePorts } = require('~/server/utils');
|
const { isEnabled, removePorts } = require('~/server/utils');
|
||||||
const keyvMongo = require('~/cache/keyvMongo');
|
const keyvMongo = require('~/cache/keyvMongo');
|
||||||
const denyRequest = require('./denyRequest');
|
const denyRequest = require('./denyRequest');
|
||||||
const { getLogStores } = require('~/cache');
|
const { getLogStores } = require('~/cache');
|
||||||
|
const { findUser } = require('~/models');
|
||||||
const User = require('~/db/models').User;
|
|
||||||
|
|
||||||
const banCache = new Keyv({ store: keyvMongo, namespace: ViolationTypes.BAN, ttl: 0 });
|
const banCache = new Keyv({ store: keyvMongo, namespace: ViolationTypes.BAN, ttl: 0 });
|
||||||
const message = 'Your account has been temporarily banned due to violations of our service.';
|
const message = 'Your account has been temporarily banned due to violations of our service.';
|
||||||
|
|
@ -59,7 +57,7 @@ const checkBan = async (req, res, next = () => {}) => {
|
||||||
let userId = req.user?.id ?? req.user?._id ?? null;
|
let userId = req.user?.id ?? req.user?._id ?? null;
|
||||||
|
|
||||||
if (!userId && req?.body?.email) {
|
if (!userId && req?.body?.email) {
|
||||||
const user = await User.findUser({ email: req.body.email }, '_id');
|
const user = await findUser({ email: req.body.email }, '_id');
|
||||||
userId = user?._id ? user._id.toString() : userId;
|
userId = user?._id ? user._id.toString() : userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const { errorsToString } = require('librechat-data-provider');
|
const { errorsToString } = require('librechat-data-provider');
|
||||||
const { Strategy: PassportLocalStrategy } = require('passport-local');
|
const { Strategy: PassportLocalStrategy } = require('passport-local');
|
||||||
const { isEnabled, checkEmailConfig } = require('~/server/utils');
|
const { isEnabled, checkEmailConfig } = require('~/server/utils');
|
||||||
const { comparePassword } = require('~/models');
|
const { findUser, comparePassword } = require('~/models');
|
||||||
const { loginSchema } = require('./validators');
|
const { loginSchema } = require('./validators');
|
||||||
|
|
||||||
const User = require('~/db/models').User;
|
const User = require('~/db/models').User;
|
||||||
|
|
@ -25,7 +24,7 @@ async function passportLogin(req, email, password, done) {
|
||||||
return done(null, false, { message: validationError });
|
return done(null, false, { message: validationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.findUser({ email: email.trim() });
|
const user = await findUser({ email: email.trim() });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logError('Passport Local Strategy - User Not Found', { email });
|
logError('Passport Local Strategy - User Not Found', { email });
|
||||||
logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`);
|
logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch');
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const passport = require('passport');
|
const passport = require('passport');
|
||||||
const client = require('openid-client');
|
const client = require('openid-client');
|
||||||
const jwtDecode = require('jsonwebtoken/decode');
|
const jwtDecode = require('jsonwebtoken/decode');
|
||||||
|
|
@ -8,11 +7,10 @@ const { CacheKeys } = require('librechat-data-provider');
|
||||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||||
const { Strategy: OpenIDStrategy } = require('openid-client/passport');
|
const { Strategy: OpenIDStrategy } = require('openid-client/passport');
|
||||||
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
||||||
|
const { findUser, createUser, updateUser } = require('~/models');
|
||||||
const getLogStores = require('~/cache/getLogStores');
|
const getLogStores = require('~/cache/getLogStores');
|
||||||
const { isEnabled } = require('~/server/utils');
|
const { isEnabled } = require('~/server/utils');
|
||||||
|
|
||||||
const User = require('~/db/models').User;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('openid-client').ClientMetadata} ClientMetadata
|
* @typedef {import('openid-client').ClientMetadata} ClientMetadata
|
||||||
* @typedef {import('openid-client').Configuration} Configuration
|
* @typedef {import('openid-client').Configuration} Configuration
|
||||||
|
|
@ -248,13 +246,13 @@ async function setupOpenId() {
|
||||||
async (tokenset, done) => {
|
async (tokenset, done) => {
|
||||||
try {
|
try {
|
||||||
const claims = tokenset.claims();
|
const claims = tokenset.claims();
|
||||||
let user = await User.findUser({ openidId: claims.sub });
|
let user = await findUser({ openidId: claims.sub });
|
||||||
logger.info(
|
logger.info(
|
||||||
`[openidStrategy] user ${user ? 'found' : 'not found'} with openidId: ${claims.sub}`,
|
`[openidStrategy] user ${user ? 'found' : 'not found'} with openidId: ${claims.sub}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = await User.findUser({ email: claims.email });
|
user = await findUser({ email: claims.email });
|
||||||
logger.info(
|
logger.info(
|
||||||
`[openidStrategy] user ${user ? 'found' : 'not found'} with email: ${
|
`[openidStrategy] user ${user ? 'found' : 'not found'} with email: ${
|
||||||
claims.email
|
claims.email
|
||||||
|
|
@ -318,7 +316,7 @@ async function setupOpenId() {
|
||||||
|
|
||||||
const balanceConfig = await getBalanceConfig();
|
const balanceConfig = await getBalanceConfig();
|
||||||
|
|
||||||
user = await User.createUser(user, balanceConfig, true, true);
|
user = await createUser(user, balanceConfig, true, true);
|
||||||
} else {
|
} else {
|
||||||
user.provider = 'openid';
|
user.provider = 'openid';
|
||||||
user.openidId = userinfo.sub;
|
user.openidId = userinfo.sub;
|
||||||
|
|
@ -354,7 +352,7 @@ async function setupOpenId() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user = await User.updateUser(user._id, user);
|
user = await updateUser(user._id, user);
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`[openidStrategy] login success openidId: ${user.openidId} | email: ${user.email} | username: ${user.username} `,
|
`[openidStrategy] login success openidId: ${user.openidId} | email: ${user.email} | username: ${user.username} `,
|
||||||
|
|
|
||||||
|
|
@ -1,223 +0,0 @@
|
||||||
# Data Schemas - Refactored Architecture
|
|
||||||
|
|
||||||
This package has been refactored to follow a clean, modular architecture with clear separation of concerns.
|
|
||||||
|
|
||||||
## 📁 Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
packages/data-schemas/src/
|
|
||||||
├── index.ts # Main exports
|
|
||||||
├── schema/ # 🗄️ Mongoose schema definitions
|
|
||||||
│ ├── user.ts
|
|
||||||
│ ├── session.ts
|
|
||||||
│ └── token.ts
|
|
||||||
├── models/ # 🏗️ Mongoose model instances
|
|
||||||
│ └── index.ts
|
|
||||||
├── methods/ # ⚙️ Business logic functions
|
|
||||||
│ ├── index.ts
|
|
||||||
│ ├── user.ts
|
|
||||||
│ ├── session.ts
|
|
||||||
│ └── token.ts
|
|
||||||
└── types/ # 📋 TypeScript interfaces & types
|
|
||||||
├── index.ts
|
|
||||||
├── user.ts
|
|
||||||
├── session.ts
|
|
||||||
└── token.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Key Benefits
|
|
||||||
|
|
||||||
### 1. **Separation of Concerns**
|
|
||||||
- **Schema**: Pure Mongoose schema definitions
|
|
||||||
- **Models**: Model instances created once
|
|
||||||
- **Methods**: Business logic as pure functions
|
|
||||||
- **Types**: Shared TypeScript interfaces
|
|
||||||
|
|
||||||
### 2. **No Dynamic Imports**
|
|
||||||
- Models are created once in the models directory
|
|
||||||
- Methods import model instances directly
|
|
||||||
- No more dynamic `import()` calls
|
|
||||||
|
|
||||||
### 3. **Better Type Safety**
|
|
||||||
- Shared types across all layers
|
|
||||||
- Proper TypeScript typing throughout
|
|
||||||
- Clear interfaces for all operations
|
|
||||||
|
|
||||||
### 4. **Pure Functions**
|
|
||||||
- Methods are now side-effect free
|
|
||||||
- Easy to test and reason about
|
|
||||||
- No magic or hidden dependencies
|
|
||||||
|
|
||||||
## 🚀 Migration Guide
|
|
||||||
|
|
||||||
### Before (Static Methods)
|
|
||||||
```typescript
|
|
||||||
import { User } from 'some-model-registry';
|
|
||||||
|
|
||||||
// Old way with static methods
|
|
||||||
const user = await User.findUser({ email: 'test@example.com' });
|
|
||||||
const result = await User.deleteUserById(userId);
|
|
||||||
```
|
|
||||||
|
|
||||||
### After (Pure Functions)
|
|
||||||
```typescript
|
|
||||||
import { findUser, deleteUserById } from '~/methods';
|
|
||||||
|
|
||||||
// New way with pure functions
|
|
||||||
const user = await findUser({ email: 'test@example.com' });
|
|
||||||
const result = await deleteUserById(userId);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 Usage Examples
|
|
||||||
|
|
||||||
### User Operations
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
findUser,
|
|
||||||
createUser,
|
|
||||||
updateUser,
|
|
||||||
deleteUserById,
|
|
||||||
generateToken
|
|
||||||
} from '~/methods';
|
|
||||||
|
|
||||||
// Find a user
|
|
||||||
const user = await findUser(
|
|
||||||
{ email: 'user@example.com' },
|
|
||||||
'name email role'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a user with balance config
|
|
||||||
const newUser = await createUser(
|
|
||||||
{ email: 'new@example.com', name: 'John' },
|
|
||||||
{ enabled: true, startBalance: 100 },
|
|
||||||
true, // disable TTL
|
|
||||||
true // return user object
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update user
|
|
||||||
const updated = await updateUser(userId, { name: 'Jane' });
|
|
||||||
|
|
||||||
// Delete user
|
|
||||||
const result = await deleteUserById(userId);
|
|
||||||
|
|
||||||
// Generate JWT token
|
|
||||||
const token = await generateToken(user);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Session Operations
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
createSession,
|
|
||||||
findSession,
|
|
||||||
deleteSession,
|
|
||||||
deleteAllUserSessions
|
|
||||||
} from '~/methods';
|
|
||||||
|
|
||||||
// Create session
|
|
||||||
const { session, refreshToken } = await createSession(userId);
|
|
||||||
|
|
||||||
// Find session by refresh token
|
|
||||||
const foundSession = await findSession({ refreshToken });
|
|
||||||
|
|
||||||
// Delete specific session
|
|
||||||
await deleteSession({ sessionId });
|
|
||||||
|
|
||||||
// Delete all user sessions
|
|
||||||
await deleteAllUserSessions(userId, {
|
|
||||||
excludeCurrentSession: true,
|
|
||||||
currentSessionId
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Token Operations
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
createToken,
|
|
||||||
findToken,
|
|
||||||
updateToken,
|
|
||||||
deleteTokens
|
|
||||||
} from '~/methods';
|
|
||||||
|
|
||||||
// Create token
|
|
||||||
const token = await createToken({
|
|
||||||
userId,
|
|
||||||
token: 'abc123',
|
|
||||||
type: 'verification',
|
|
||||||
expiresIn: 3600 // 1 hour
|
|
||||||
});
|
|
||||||
|
|
||||||
// Find token
|
|
||||||
const foundToken = await findToken({
|
|
||||||
token: 'abc123',
|
|
||||||
type: 'verification'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update token
|
|
||||||
const updated = await updateToken(
|
|
||||||
{ token: 'abc123' },
|
|
||||||
{ type: 'password-reset' }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Delete tokens
|
|
||||||
await deleteTokens({ userId });
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Path Aliases
|
|
||||||
|
|
||||||
The project uses `~/` as an alias for `./src/`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { IUser } from '~/types';
|
|
||||||
import { User } from '~/models';
|
|
||||||
import { findUser } from '~/methods';
|
|
||||||
import userSchema from '~/schema/user';
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⚠️ Breaking Changes
|
|
||||||
|
|
||||||
1. **Static Methods Removed**: All static methods have been removed from schema files
|
|
||||||
2. **Function Signatures**: Methods no longer take model as first parameter
|
|
||||||
3. **Import Paths**: Import from `~/methods` instead of calling static methods
|
|
||||||
4. **Type Definitions**: Types moved to dedicated `~/types` directory
|
|
||||||
|
|
||||||
## 🧪 Testing
|
|
||||||
|
|
||||||
The new pure function approach makes testing much easier:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { findUser } from '~/methods';
|
|
||||||
|
|
||||||
// Easy to mock and test
|
|
||||||
jest.mock('~/models', () => ({
|
|
||||||
User: {
|
|
||||||
findOne: jest.fn().mockReturnValue({
|
|
||||||
select: jest.fn().mockReturnValue({
|
|
||||||
lean: jest.fn().mockResolvedValue(mockUser)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
test('findUser should return user', async () => {
|
|
||||||
const result = await findUser({ email: 'test@example.com' });
|
|
||||||
expect(result).toEqual(mockUser);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔄 Error Handling
|
|
||||||
|
|
||||||
All methods include proper error handling with typed errors:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { SessionError } from '~/types';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const session = await createSession(userId);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof SessionError) {
|
|
||||||
console.log('Session error:', error.code, error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This refactoring provides a much cleaner, more maintainable, and type-safe architecture for data operations.
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue