mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00
chore: Remove Unused Dependencies 🧹 (#939)
* chore: cleanup client depend 🧹
* chore: replace joi with zod and remove unused user validator
* chore: move dep from root to api, cleanup other unused api deps
* chore: remove unused dev dep
* chore: update bun lockfile
* fix: bun scripts
* chore: add bun flag to update script
* chore: remove legacy webpack + babel dev deps
* chore: add back dev deps needed for frontend unit testing
* fix(validators): make schemas as expected and more robust with a full test suite of edge cases
* chore: remove axios from root package, remove path from api, update bun
This commit is contained in:
parent
7f5b0b5310
commit
b3afd562b9
12 changed files with 1935 additions and 4938 deletions
|
@ -1,17 +1,10 @@
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const Joi = require('joi');
|
|
||||||
const DebugControl = require('../utils/debug.js');
|
|
||||||
const userSchema = require('./schema/userSchema.js');
|
const userSchema = require('./schema/userSchema.js');
|
||||||
const { SESSION_EXPIRY } = process.env ?? {};
|
const { SESSION_EXPIRY } = process.env ?? {};
|
||||||
const expires = eval(SESSION_EXPIRY) ?? 1000 * 60 * 15;
|
const expires = eval(SESSION_EXPIRY) ?? 1000 * 60 * 15;
|
||||||
|
|
||||||
function log({ title, parameters }) {
|
|
||||||
DebugControl.log.functionName(title);
|
|
||||||
DebugControl.log.parameters(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
userSchema.methods.toJSON = function () {
|
userSchema.methods.toJSON = function () {
|
||||||
return {
|
return {
|
||||||
id: this._id,
|
id: this._id,
|
||||||
|
@ -65,26 +58,6 @@ module.exports.hashPassword = async (password) => {
|
||||||
return hashedPassword;
|
return hashedPassword;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.validateUser = (user) => {
|
|
||||||
log({
|
|
||||||
title: 'Validate User',
|
|
||||||
parameters: [{ name: 'Validate User', value: user }],
|
|
||||||
});
|
|
||||||
const schema = {
|
|
||||||
avatar: Joi.any(),
|
|
||||||
name: Joi.string().min(3).max(80).required(),
|
|
||||||
username: Joi.string()
|
|
||||||
.trim()
|
|
||||||
.allow('')
|
|
||||||
.min(2)
|
|
||||||
.max(80)
|
|
||||||
.regex(/^[a-zA-Z0-9_.-@#$%&*() ]+$/),
|
|
||||||
password: Joi.string().min(8).max(128).allow('').allow(null),
|
|
||||||
};
|
|
||||||
|
|
||||||
return schema.validate(user);
|
|
||||||
};
|
|
||||||
|
|
||||||
const User = mongoose.model('User', userSchema);
|
const User = mongoose.model('User', userSchema);
|
||||||
|
|
||||||
module.exports = User;
|
module.exports = User;
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
"@anthropic-ai/sdk": "^0.5.4",
|
"@anthropic-ai/sdk": "^0.5.4",
|
||||||
"@azure/search-documents": "^11.3.2",
|
"@azure/search-documents": "^11.3.2",
|
||||||
"@dqbd/tiktoken": "^1.0.7",
|
"@dqbd/tiktoken": "^1.0.7",
|
||||||
"@fortaine/fetch-event-source": "^3.0.6",
|
|
||||||
"@keyv/mongo": "^2.1.8",
|
"@keyv/mongo": "^2.1.8",
|
||||||
"@waylaidwanderer/chatgpt-api": "^1.37.2",
|
"@waylaidwanderer/chatgpt-api": "^1.37.2",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
|
@ -32,10 +31,8 @@
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"cohere-ai": "^5.0.2",
|
"cohere-ai": "^5.0.2",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"cookie-parser": "^1.4.6",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"eslint": "^8.41.0",
|
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-mongo-sanitize": "^2.2.0",
|
"express-mongo-sanitize": "^2.2.0",
|
||||||
"express-rate-limit": "^6.9.0",
|
"express-rate-limit": "^6.9.0",
|
||||||
|
@ -43,7 +40,6 @@
|
||||||
"googleapis": "^118.0.0",
|
"googleapis": "^118.0.0",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"html": "^1.0.0",
|
"html": "^1.0.0",
|
||||||
"joi": "^17.9.2",
|
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"keyv": "^4.5.3",
|
"keyv": "^4.5.3",
|
||||||
|
@ -63,7 +59,6 @@
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pino": "^8.12.1",
|
"pino": "^8.12.1",
|
||||||
"sanitize": "^2.1.2",
|
|
||||||
"sharp": "^0.32.5",
|
"sharp": "^0.32.5",
|
||||||
"ua-parser-js": "^1.0.36",
|
"ua-parser-js": "^1.0.36",
|
||||||
"zod": "^3.22.2"
|
"zod": "^3.22.2"
|
||||||
|
@ -71,7 +66,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"path": "^0.12.7",
|
|
||||||
"supertest": "^6.3.3"
|
"supertest": "^6.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ const bcrypt = require('bcryptjs');
|
||||||
const User = require('../../models/User');
|
const User = require('../../models/User');
|
||||||
const Session = require('../../models/Session');
|
const Session = require('../../models/Session');
|
||||||
const Token = require('../../models/schema/tokenSchema');
|
const Token = require('../../models/schema/tokenSchema');
|
||||||
const { registerSchema } = require('../../strategies/validators');
|
const { registerSchema, errorsToString } = require('../../strategies/validators');
|
||||||
const config = require('../../../config/loader');
|
const config = require('../../../config/loader');
|
||||||
const { sendEmail } = require('../utils');
|
const { sendEmail } = require('../utils');
|
||||||
const domains = config.domains;
|
const domains = config.domains;
|
||||||
|
@ -44,15 +44,16 @@ const logoutUser = async (userId, refreshToken) => {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const registerUser = async (user) => {
|
const registerUser = async (user) => {
|
||||||
const { error } = registerSchema.validate(user);
|
const { error } = registerSchema.safeParse(user);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
const errorMessage = errorsToString(error.errors);
|
||||||
console.info(
|
console.info(
|
||||||
'Route: register - Joi Validation Error',
|
'Route: register - Validation Error',
|
||||||
{ name: 'Request params:', value: user },
|
{ name: 'Request params:', value: user },
|
||||||
{ name: 'Validation error:', value: error.details },
|
{ name: 'Validation error:', value: errorMessage },
|
||||||
);
|
);
|
||||||
|
|
||||||
return { status: 422, message: error.details[0].message };
|
return { status: 422, message: errorMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
const { email, password, name, username } = user;
|
const { email, password, name, username } = user;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
const { Strategy: PassportLocalStrategy } = require('passport-local');
|
const { Strategy: PassportLocalStrategy } = require('passport-local');
|
||||||
const User = require('../models/User');
|
const User = require('../models/User');
|
||||||
const { loginSchema } = require('./validators');
|
const { loginSchema, errorsToString } = require('./validators');
|
||||||
const DebugControl = require('../utils/debug.js');
|
const DebugControl = require('../utils/debug.js');
|
||||||
|
|
||||||
async function validateLoginRequest(req) {
|
async function validateLoginRequest(req) {
|
||||||
const { error } = loginSchema.validate(req.body);
|
const { error } = loginSchema.safeParse(req.body);
|
||||||
return error ? error.details[0].message : null;
|
return error ? errorsToString(error.errors) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findUserByEmail(email) {
|
async function findUserByEmail(email) {
|
||||||
|
|
|
@ -1,24 +1,69 @@
|
||||||
const Joi = require('joi');
|
const { z } = require('zod');
|
||||||
|
|
||||||
const loginSchema = Joi.object().keys({
|
function errorsToString(errors) {
|
||||||
email: Joi.string().trim().email().required(),
|
return errors
|
||||||
password: Joi.string().trim().min(8).max(128).required(),
|
.map((error) => {
|
||||||
|
let field = error.path.join('.');
|
||||||
|
let message = error.message;
|
||||||
|
|
||||||
|
return `${field}: ${message}`;
|
||||||
|
})
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginSchema = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(8)
|
||||||
|
.max(128)
|
||||||
|
.refine((value) => value.trim().length > 0, {
|
||||||
|
message: 'Password cannot be only spaces',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const registerSchema = Joi.object().keys({
|
const registerSchema = z
|
||||||
name: Joi.string().trim().min(3).max(80).required(),
|
.object({
|
||||||
username: Joi.string()
|
name: z.string().min(3).max(80),
|
||||||
.trim()
|
username: z
|
||||||
.allow('')
|
.union([
|
||||||
.min(2)
|
z.literal(''),
|
||||||
.max(80)
|
z
|
||||||
.regex(/^[a-zA-Z0-9_.-@#$%&*() ]+$/),
|
.string()
|
||||||
email: Joi.string().trim().email().required(),
|
.min(2)
|
||||||
password: Joi.string().trim().min(8).max(128).required(),
|
.max(80)
|
||||||
confirm_password: Joi.string().trim().min(8).max(128).required(),
|
.regex(/^[a-zA-Z0-9_.-@#$%&*() ]+$/),
|
||||||
});
|
])
|
||||||
|
.transform((value) => (value === '' ? null : value))
|
||||||
|
.optional()
|
||||||
|
.nullable(),
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(8)
|
||||||
|
.max(128)
|
||||||
|
.refine((value) => value.trim().length > 0, {
|
||||||
|
message: 'Password cannot be only spaces',
|
||||||
|
}),
|
||||||
|
confirm_password: z
|
||||||
|
.string()
|
||||||
|
.min(8)
|
||||||
|
.max(128)
|
||||||
|
.refine((value) => value.trim().length > 0, {
|
||||||
|
message: 'Password cannot be only spaces',
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.superRefine(({ confirm_password, password }, ctx) => {
|
||||||
|
if (confirm_password !== password) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: 'custom',
|
||||||
|
message: 'The passwords did not match',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
loginSchema,
|
loginSchema,
|
||||||
registerSchema,
|
registerSchema,
|
||||||
|
errorsToString,
|
||||||
};
|
};
|
||||||
|
|
279
api/strategies/validators.spec.js
Normal file
279
api/strategies/validators.spec.js
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
const { loginSchema, registerSchema, errorsToString } = require('./validators');
|
||||||
|
|
||||||
|
describe('Zod Schemas', () => {
|
||||||
|
describe('loginSchema', () => {
|
||||||
|
it('should validate a correct login object', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: 'test@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate an incorrect email', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: 'testexample.com',
|
||||||
|
password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate a short password', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: 'test@example.com',
|
||||||
|
password: 'pass',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle email with unusual characters', () => {
|
||||||
|
const emails = ['test+alias@example.com', 'test@subdomain.example.co.uk'];
|
||||||
|
emails.forEach((email) => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email,
|
||||||
|
password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate email without a domain', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: 'test@.com',
|
||||||
|
password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate password with only spaces', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: 'test@example.com',
|
||||||
|
password: ' ',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate password that is too long', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: 'test@example.com',
|
||||||
|
password: 'a'.repeat(129),
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate empty email or password', () => {
|
||||||
|
const result = loginSchema.safeParse({
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('registerSchema', () => {
|
||||||
|
it('should validate a correct register object', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow the username to be omitted', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate a short name', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'Jo',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty username by transforming to null', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: '',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.data.username).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle name with special characters', () => {
|
||||||
|
const names = ['Jöhn Dœ', 'John <Doe>'];
|
||||||
|
names.forEach((name) => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name,
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle username with special characters', () => {
|
||||||
|
const usernames = ['john.doe@', 'john..doe'];
|
||||||
|
usernames.forEach((username) => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username,
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate mismatched password and confirm_password', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password124',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle email without a TLD', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@domain',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle email with multiple @ symbols', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@domain@com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle name that is too long', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'a'.repeat(81),
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle username that is too long', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'a'.repeat(81),
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle password or confirm_password that is too long', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'a'.repeat(129),
|
||||||
|
confirm_password: 'a'.repeat(129),
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle password or confirm_password that is just spaces', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: ' ',
|
||||||
|
confirm_password: ' ',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle null values for fields', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: null,
|
||||||
|
username: null,
|
||||||
|
email: null,
|
||||||
|
password: null,
|
||||||
|
confirm_password: null,
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle undefined values for fields', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: undefined,
|
||||||
|
username: undefined,
|
||||||
|
email: undefined,
|
||||||
|
password: undefined,
|
||||||
|
confirm_password: undefined,
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle extra fields not defined in the schema', () => {
|
||||||
|
const result = registerSchema.safeParse({
|
||||||
|
name: 'John Doe',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
extraField: 'I shouldn\'t be here',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('errorsToString', () => {
|
||||||
|
it('should convert errors to string', () => {
|
||||||
|
const { error } = registerSchema.safeParse({
|
||||||
|
name: 'Jo',
|
||||||
|
username: 'john_doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
confirm_password: 'password123',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = errorsToString(error.errors);
|
||||||
|
expect(result).toBe('name: String must contain at least 3 character(s)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
babel is used for frontend unit testing
|
||||||
|
|
||||||
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
['@babel/preset-env', { 'targets': { 'node': 'current' } }], //compiling ES2015+ syntax
|
['@babel/preset-env', { 'targets': { 'node': 'current' } }], //compiling ES2015+ syntax
|
||||||
|
|
|
@ -4,15 +4,15 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"data-provider": "cd .. && npm run build:data-provider",
|
"data-provider": "cd .. && npm run build:data-provider",
|
||||||
"build": "cross-env NODE_ENV=production dotenv -e ../.env -- vite build",
|
"build": "cross-env NODE_ENV=production vite build",
|
||||||
"build:ci": "cross-env NODE_ENV=development vite build --mode ci",
|
"build:ci": "cross-env NODE_ENV=development vite build --mode ci",
|
||||||
"dev": "cross-env NODE_ENV=development dotenv -e ../.env -- vite",
|
"dev": "cross-env NODE_ENV=development vite",
|
||||||
"preview-prod": "cross-env NODE_ENV=development dotenv -e ../.env -- vite preview",
|
"preview-prod": "cross-env NODE_ENV=development vite preview",
|
||||||
"test": "cross-env NODE_ENV=test jest --watch",
|
"test": "cross-env NODE_ENV=test jest --watch",
|
||||||
"b:test": "NODE_ENV=test bun jest --watch",
|
|
||||||
"test:ci": "cross-env NODE_ENV=test jest --ci",
|
"test:ci": "cross-env NODE_ENV=test jest --ci",
|
||||||
"b:build": "NODE_ENV=production bun vite build",
|
"b:test": "NODE_ENV=test bunx jest --watch",
|
||||||
"b:dev": "NODE_ENV=development bun vite"
|
"b:build": "NODE_ENV=production bun --bun vite build",
|
||||||
|
"b:dev": "NODE_ENV=development bunx vite"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -26,11 +26,6 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/danny-avila/LibreChat#readme",
|
"homepage": "https://github.com/danny-avila/LibreChat#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
||||||
"@headlessui/react": "^1.7.13",
|
"@headlessui/react": "^1.7.13",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
"@radix-ui/react-alert-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-checkbox": "^1.0.3",
|
"@radix-ui/react-checkbox": "^1.0.3",
|
||||||
|
@ -43,7 +38,6 @@
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tabs": "^1.0.3",
|
"@radix-ui/react-tabs": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.0.6",
|
"@radix-ui/react-tooltip": "^1.0.6",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
|
||||||
"@tanstack/react-query": "^4.28.0",
|
"@tanstack/react-query": "^4.28.0",
|
||||||
"@zattoo/use-double-click": "1.2.0",
|
"@zattoo/use-double-click": "1.2.0",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
|
@ -51,24 +45,19 @@
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"crypto-browserify": "^3.12.0",
|
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"esbuild": "0.17.19",
|
|
||||||
"export-from-json": "^1.7.2",
|
"export-from-json": "^1.7.2",
|
||||||
"filenamify": "^6.0.0",
|
"filenamify": "^6.0.0",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"librechat-data-provider": "*",
|
"librechat-data-provider": "*",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.220.0",
|
"lucide-react": "^0.220.0",
|
||||||
"pino": "^8.12.1",
|
|
||||||
"rc-input-number": "^7.4.2",
|
"rc-input-number": "^7.4.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-lazy-load": "^4.0.1",
|
|
||||||
"react-markdown": "^8.0.6",
|
"react-markdown": "^8.0.6",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-string-replace": "^1.1.0",
|
|
||||||
"react-textarea-autosize": "^8.4.0",
|
"react-textarea-autosize": "^8.4.0",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
|
@ -85,14 +74,10 @@
|
||||||
"zod": "^3.22.2"
|
"zod": "^3.22.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.20.7",
|
"@babel/plugin-transform-runtime": "^7.22.15",
|
||||||
"@babel/core": "^7.21.8",
|
"@babel/preset-env": "^7.22.15",
|
||||||
"@babel/eslint-parser": "^7.19.1",
|
"@babel/preset-react": "^7.22.15",
|
||||||
"@babel/plugin-transform-runtime": "^7.21.4",
|
"@babel/preset-typescript": "^7.22.15",
|
||||||
"@babel/preset-env": "^7.21.5",
|
|
||||||
"@babel/preset-react": "^7.18.6",
|
|
||||||
"@babel/preset-typescript": "^7.21.0",
|
|
||||||
"@babel/runtime": "^7.20.13",
|
|
||||||
"@tanstack/react-query-devtools": "^4.29.0",
|
"@tanstack/react-query-devtools": "^4.29.0",
|
||||||
"@testing-library/dom": "^9.3.0",
|
"@testing-library/dom": "^9.3.0",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
|
@ -104,15 +89,10 @@
|
||||||
"@types/react-dom": "^18.2.4",
|
"@types/react-dom": "^18.2.4",
|
||||||
"@vitejs/plugin-react": "^4.0.4",
|
"@vitejs/plugin-react": "^4.0.4",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"babel-jest": "^29.5.0",
|
|
||||||
"babel-loader": "^9.1.2",
|
|
||||||
"babel-plugin-replace-ts-export-assignment": "^0.0.2",
|
"babel-plugin-replace-ts-export-assignment": "^0.0.2",
|
||||||
"babel-plugin-root-import": "^6.6.0",
|
"babel-plugin-root-import": "^6.6.0",
|
||||||
"babel-plugin-transform-import-meta": "^2.2.0",
|
"babel-plugin-transform-import-meta": "^2.2.1",
|
||||||
"babel-plugin-transform-vite-meta-env": "^1.0.3",
|
"babel-plugin-transform-vite-meta-env": "^1.0.3",
|
||||||
"babel-preset-react": "^6.24.1",
|
|
||||||
"css-loader": "^6.7.3",
|
|
||||||
"dotenv-cli": "^7.2.1",
|
|
||||||
"eslint-plugin-jest": "^27.2.1",
|
"eslint-plugin-jest": "^27.2.1",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
|
@ -120,15 +100,11 @@
|
||||||
"jest-environment-jsdom": "^29.5.0",
|
"jest-environment-jsdom": "^29.5.0",
|
||||||
"jest-file-loader": "^1.0.3",
|
"jest-file-loader": "^1.0.3",
|
||||||
"jest-junit": "^16.0.0",
|
"jest-junit": "^16.0.0",
|
||||||
"path": "^0.12.7",
|
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"postcss-loader": "^7.1.0",
|
"postcss-loader": "^7.1.0",
|
||||||
"postcss-preset-env": "^8.2.0",
|
"postcss-preset-env": "^8.2.0",
|
||||||
"source-map-loader": "^4.0.1",
|
|
||||||
"style-loader": "^3.3.1",
|
|
||||||
"tailwindcss": "^3.2.6",
|
"tailwindcss": "^3.2.6",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"ts-loader": "^9.4.2",
|
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vite-plugin-html": "^3.2.0"
|
"vite-plugin-html": "^3.2.0"
|
||||||
|
|
|
@ -3,9 +3,10 @@ const path = require('path');
|
||||||
const { askQuestion, isDockerRunning, deleteNodeModules, silentExit } = require('./helpers');
|
const { askQuestion, isDockerRunning, deleteNodeModules, silentExit } = require('./helpers');
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
localUpdate: process.argv.includes('-l'),
|
bun: process.argv.includes('-b'),
|
||||||
dockerUpdate: process.argv.includes('-d'),
|
local: process.argv.includes('-l'),
|
||||||
useSingleComposeFile: process.argv.includes('-s'),
|
docker: process.argv.includes('-d'),
|
||||||
|
singleCompose: process.argv.includes('-s'),
|
||||||
useSudo: process.argv.includes('--sudo'),
|
useSudo: process.argv.includes('--sudo'),
|
||||||
skipGit: process.argv.includes('-g'),
|
skipGit: process.argv.includes('-g'),
|
||||||
};
|
};
|
||||||
|
@ -20,14 +21,14 @@ const directories = [
|
||||||
];
|
];
|
||||||
|
|
||||||
async function updateConfigWithWizard() {
|
async function updateConfigWithWizard() {
|
||||||
if (!config.dockerUpdate && !config.useSingleComposeFile) {
|
if (!config.docker && !config.singleCompose) {
|
||||||
config.dockerUpdate = (await askQuestion('Are you using Docker? (y/n): '))
|
config.docker = (await askQuestion('Are you using Docker? (y/n): '))
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.startsWith('y');
|
.startsWith('y');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.dockerUpdate && !config.useSingleComposeFile) {
|
if (config.docker && !config.singleCompose) {
|
||||||
config.useSingleComposeFile = !(
|
config.singleCompose = !(
|
||||||
await askQuestion('Are you using the default docker-compose file? (y/n): ')
|
await askQuestion('Are you using the default docker-compose file? (y/n): ')
|
||||||
)
|
)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
|
@ -36,11 +37,11 @@ async function updateConfigWithWizard() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateDockerRunning() {
|
async function validateDockerRunning() {
|
||||||
if (!config.dockerUpdate && config.useSingleComposeFile) {
|
if (!config.docker && config.singleCompose) {
|
||||||
config.dockerUpdate = true;
|
config.docker = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.dockerUpdate && !isDockerRunning()) {
|
if (config.docker && !isDockerRunning()) {
|
||||||
console.red(
|
console.red(
|
||||||
'Error: Docker is not running. You will need to start Docker Desktop or if using linux/mac, run `sudo systemctl start docker`',
|
'Error: Docker is not running. You will need to start Docker Desktop or if using linux/mac, run `sudo systemctl start docker`',
|
||||||
);
|
);
|
||||||
|
@ -49,7 +50,7 @@ async function validateDockerRunning() {
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const showWizard = !config.localUpdate && !config.dockerUpdate && !config.useSingleComposeFile;
|
const showWizard = !config.local && !config.docker && !config.singleCompose;
|
||||||
|
|
||||||
if (showWizard) {
|
if (showWizard) {
|
||||||
await updateConfigWithWizard();
|
await updateConfigWithWizard();
|
||||||
|
@ -60,7 +61,7 @@ async function validateDockerRunning() {
|
||||||
);
|
);
|
||||||
|
|
||||||
await validateDockerRunning();
|
await validateDockerRunning();
|
||||||
const { dockerUpdate, useSingleComposeFile: singleCompose, useSudo, skipGit } = config;
|
const { docker, singleCompose, useSudo, skipGit, bun } = config;
|
||||||
const sudo = useSudo ? 'sudo ' : '';
|
const sudo = useSudo ? 'sudo ' : '';
|
||||||
if (!skipGit) {
|
if (!skipGit) {
|
||||||
// Fetch latest repo
|
// Fetch latest repo
|
||||||
|
@ -76,7 +77,7 @@ async function validateDockerRunning() {
|
||||||
execSync('git pull origin main', { stdio: 'inherit' });
|
execSync('git pull origin main', { stdio: 'inherit' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dockerUpdate) {
|
if (docker) {
|
||||||
console.purple('Removing previously made Docker container...');
|
console.purple('Removing previously made Docker container...');
|
||||||
const downCommand = `${sudo}docker-compose ${
|
const downCommand = `${sudo}docker-compose ${
|
||||||
singleCompose ? '-f ./docs/dev/single-compose.yml ' : ''
|
singleCompose ? '-f ./docs/dev/single-compose.yml ' : ''
|
||||||
|
@ -113,11 +114,11 @@ async function validateDockerRunning() {
|
||||||
|
|
||||||
// Build client-side code
|
// Build client-side code
|
||||||
console.purple('Building frontend...');
|
console.purple('Building frontend...');
|
||||||
execSync('npm run frontend', { stdio: 'inherit' });
|
execSync(bun ? 'bun b:client' : 'npm run frontend', { stdio: 'inherit' });
|
||||||
}
|
}
|
||||||
|
|
||||||
let startCommand = 'npm run backend';
|
let startCommand = 'npm run backend';
|
||||||
if (dockerUpdate) {
|
if (docker) {
|
||||||
startCommand = `${sudo}docker-compose ${
|
startCommand = `${sudo}docker-compose ${
|
||||||
singleCompose ? '-f ./docs/dev/single-compose.yml ' : ''
|
singleCompose ? '-f ./docs/dev/single-compose.yml ' : ''
|
||||||
}up`;
|
}up`;
|
||||||
|
|
6379
package-lock.json
generated
6379
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@
|
||||||
"update": "node config/update.js",
|
"update": "node config/update.js",
|
||||||
"rebuild:package-lock": "node config/packages",
|
"rebuild:package-lock": "node config/packages",
|
||||||
"reinstall": "node config/update.js -l -g",
|
"reinstall": "node config/update.js -l -g",
|
||||||
|
"b:reinstall": "bun config/update.js -b -l -g",
|
||||||
"reinstall:docker": "node config/update.js -d -g",
|
"reinstall:docker": "node config/update.js -d -g",
|
||||||
"update:local": "node config/update.js -l",
|
"update:local": "node config/update.js -l",
|
||||||
"update:docker": "node config/update.js -d",
|
"update:docker": "node config/update.js -d",
|
||||||
|
@ -62,15 +63,10 @@
|
||||||
"url": "https://github.com/danny-avila/LibreChat/issues"
|
"url": "https://github.com/danny-avila/LibreChat/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/danny-avila/LibreChat#readme",
|
"homepage": "https://github.com/danny-avila/LibreChat#readme",
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.4.0",
|
|
||||||
"passport-facebook": "^3.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.32.1",
|
"@playwright/test": "^1.32.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
"@typescript-eslint/parser": "^5.62.0",
|
"@typescript-eslint/parser": "^5.62.0",
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue