mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🧑💻 refactor: Secure Field Selection for 2FA & API Build Sourcemap (#9087)
* refactor: `packages/api` build scripts for better inline debugging * refactor: Explicitly select secure fields as no longer returned by default, exclude backupCodes from user data retrieval in authentication and 2FA processes * refactor: Backup Codes UI to not expect backup codes, only regeneration * refactor: Ensure secure fields are deleted from user data in getUserController
This commit is contained in:
parent
50b7bd6643
commit
3547873bc4
15 changed files with 82 additions and 31 deletions
|
|
@ -84,7 +84,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 getUserById(payload.id, '-password -__v -totpSecret');
|
const user = await getUserById(payload.id, '-password -__v -totpSecret -backupCodes');
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(401).redirect('/login');
|
return res.status(401).redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ const verify2FA = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
const { token, backupCode } = req.body;
|
const { token, backupCode } = req.body;
|
||||||
const user = await getUserById(userId);
|
const user = await getUserById(userId, '_id totpSecret backupCodes');
|
||||||
|
|
||||||
if (!user || !user.totpSecret) {
|
if (!user || !user.totpSecret) {
|
||||||
return res.status(400).json({ message: '2FA not initiated' });
|
return res.status(400).json({ message: '2FA not initiated' });
|
||||||
|
|
@ -79,7 +79,7 @@ const confirm2FA = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
const { token } = req.body;
|
const { token } = req.body;
|
||||||
const user = await getUserById(userId);
|
const user = await getUserById(userId, '_id totpSecret');
|
||||||
|
|
||||||
if (!user || !user.totpSecret) {
|
if (!user || !user.totpSecret) {
|
||||||
return res.status(400).json({ message: '2FA not initiated' });
|
return res.status(400).json({ message: '2FA not initiated' });
|
||||||
|
|
@ -105,7 +105,7 @@ const disable2FA = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
const { token, backupCode } = req.body;
|
const { token, backupCode } = req.body;
|
||||||
const user = await getUserById(userId);
|
const user = await getUserById(userId, '_id totpSecret backupCodes');
|
||||||
|
|
||||||
if (!user || !user.totpSecret) {
|
if (!user || !user.totpSecret) {
|
||||||
return res.status(400).json({ message: '2FA is not setup for this user' });
|
return res.status(400).json({ message: '2FA is not setup for this user' });
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,13 @@ const { getMCPManager } = require('~/config');
|
||||||
const getUserController = async (req, res) => {
|
const getUserController = async (req, res) => {
|
||||||
/** @type {MongoUser} */
|
/** @type {MongoUser} */
|
||||||
const userData = req.user.toObject != null ? req.user.toObject() : { ...req.user };
|
const userData = req.user.toObject != null ? req.user.toObject() : { ...req.user };
|
||||||
|
/**
|
||||||
|
* These fields should not exist due to secure field selection, but deletion
|
||||||
|
* is done in case of alternate database incompatibility with Mongo API
|
||||||
|
* */
|
||||||
|
delete userData.password;
|
||||||
delete userData.totpSecret;
|
delete userData.totpSecret;
|
||||||
|
delete userData.backupCodes;
|
||||||
if (req.app.locals.fileStrategy === FileSources.s3 && userData.avatar) {
|
if (req.app.locals.fileStrategy === FileSources.s3 && userData.avatar) {
|
||||||
const avatarNeedsRefresh = needsRefresh(userData.avatar, 3600);
|
const avatarNeedsRefresh = needsRefresh(userData.avatar, 3600);
|
||||||
if (!avatarNeedsRefresh) {
|
if (!avatarNeedsRefresh) {
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ const verify2FAWithTempToken = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
payload = jwt.verify(tempToken, process.env.JWT_SECRET);
|
payload = jwt.verify(tempToken, process.env.JWT_SECRET);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
logger.error('Failed to verify temporary token:', err);
|
||||||
return res.status(401).json({ message: 'Invalid or expired temporary token' });
|
return res.status(401).json({ message: 'Invalid or expired temporary token' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await getUserById(payload.userId);
|
const user = await getUserById(payload.userId, '+totpSecret +backupCodes');
|
||||||
if (!user || !user.twoFactorEnabled) {
|
if (!user || !user.twoFactorEnabled) {
|
||||||
return res.status(400).json({ message: '2FA is not enabled for this user' });
|
return res.status(400).json({ message: '2FA is not enabled for this user' });
|
||||||
}
|
}
|
||||||
|
|
@ -42,11 +43,11 @@ const verify2FAWithTempToken = async (req, res) => {
|
||||||
return res.status(401).json({ message: 'Invalid 2FA code or backup code' });
|
return res.status(401).json({ message: 'Invalid 2FA code or backup code' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare user data to return (omit sensitive fields).
|
|
||||||
const userData = user.toObject ? user.toObject() : { ...user };
|
const userData = user.toObject ? user.toObject() : { ...user };
|
||||||
delete userData.password;
|
|
||||||
delete userData.__v;
|
delete userData.__v;
|
||||||
|
delete userData.password;
|
||||||
delete userData.totpSecret;
|
delete userData.totpSecret;
|
||||||
|
delete userData.backupCodes;
|
||||||
userData.id = user._id.toString();
|
userData.id = user._id.toString();
|
||||||
|
|
||||||
const authToken = await setAuthTokens(user._id, res);
|
const authToken = await setAuthTokens(user._id, res);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const jwtLogin = () =>
|
||||||
},
|
},
|
||||||
async (payload, done) => {
|
async (payload, done) => {
|
||||||
try {
|
try {
|
||||||
const user = await getUserById(payload?.id, '-password -__v -totpSecret');
|
const user = await getUserById(payload?.id, '-password -__v -totpSecret -backupCodes');
|
||||||
if (user) {
|
if (user) {
|
||||||
user.id = user._id.toString();
|
user.id = user._id.toString();
|
||||||
if (!user.role) {
|
if (!user.role) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ async function passportLogin(req, email, password, done) {
|
||||||
return done(null, false, { message: validationError });
|
return done(null, false, { message: validationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await findUser({ email: email.trim() });
|
const user = await findUser({ email: email.trim() }, '+password');
|
||||||
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,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { RefreshCcw, ShieldX } from 'lucide-react';
|
import { RefreshCcw } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { TBackupCode, TRegenerateBackupCodesResponse, type TUser } from 'librechat-data-provider';
|
import { TBackupCode, TRegenerateBackupCodesResponse, type TUser } from 'librechat-data-provider';
|
||||||
import {
|
import {
|
||||||
|
|
@ -73,8 +73,8 @@ const BackupCodesItem: React.FC = () => {
|
||||||
<Label className="font-light">{localize('com_ui_backup_codes')}</Label>
|
<Label className="font-light">{localize('com_ui_backup_codes')}</Label>
|
||||||
</div>
|
</div>
|
||||||
<OGDialogTrigger asChild>
|
<OGDialogTrigger asChild>
|
||||||
<Button aria-label="Show Backup Codes" variant="outline">
|
<Button aria-label="Manage Backup Codes" variant="outline">
|
||||||
{localize('com_ui_show')}
|
{localize('com_ui_manage')}
|
||||||
</Button>
|
</Button>
|
||||||
</OGDialogTrigger>
|
</OGDialogTrigger>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -93,6 +93,16 @@ const BackupCodesItem: React.FC = () => {
|
||||||
>
|
>
|
||||||
{Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 ? (
|
{Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
<div className="border-warning-300 bg-warning-50 dark:border-warning-700 dark:bg-warning-900/20 mb-6 rounded-lg border p-4">
|
||||||
|
<p className="text-sm text-text-secondary">
|
||||||
|
{localize('com_ui_backup_codes_security_info')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="mb-4 text-lg font-medium">
|
||||||
|
{localize('com_ui_backup_codes_status')}
|
||||||
|
</h3>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
{user?.backupCodes.map((code, index) => {
|
{user?.backupCodes.map((code, index) => {
|
||||||
const isUsed = code.used;
|
const isUsed = code.used;
|
||||||
|
|
@ -125,7 +135,7 @@ const BackupCodesItem: React.FC = () => {
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between" aria-hidden="true">
|
<div className="flex items-center justify-between" aria-hidden="true">
|
||||||
<span className="text-sm font-medium text-text-secondary">
|
<span className="text-sm font-medium text-text-secondary">
|
||||||
#{index + 1}
|
{localize('com_ui_backup_code_number', { number: index + 1 })}
|
||||||
</span>
|
</span>
|
||||||
<TooltipAnchor
|
<TooltipAnchor
|
||||||
description={
|
description={
|
||||||
|
|
@ -171,8 +181,6 @@ const BackupCodesItem: React.FC = () => {
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center gap-4 p-6 text-center">
|
<div className="flex flex-col items-center gap-4 p-6 text-center">
|
||||||
<ShieldX className="h-12 w-12 text-text-primary" />
|
|
||||||
<p className="text-lg text-text-secondary">{localize('com_ui_no_backup_codes')}</p>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleRegenerate}
|
onClick={handleRegenerate}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
|
@ -180,7 +188,7 @@ const BackupCodesItem: React.FC = () => {
|
||||||
className="px-8 py-3 transition-all disabled:opacity-50"
|
className="px-8 py-3 transition-all disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{isLoading && <Spinner className="mr-2" />}
|
{isLoading && <Spinner className="mr-2" />}
|
||||||
{localize('com_ui_generate_backup')}
|
{localize('com_ui_regenerate_backup')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,6 @@ export const useDeleteUserMutation = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0
|
|
||||||
|
|
||||||
export const useEnableTwoFactorMutation = (): UseMutationResult<
|
export const useEnableTwoFactorMutation = (): UseMutationResult<
|
||||||
t.TEnable2FAResponse,
|
t.TEnable2FAResponse,
|
||||||
unknown,
|
unknown,
|
||||||
|
|
|
||||||
|
|
@ -633,8 +633,11 @@
|
||||||
"com_ui_back_to_chat": "Back to Chat",
|
"com_ui_back_to_chat": "Back to Chat",
|
||||||
"com_ui_back_to_prompts": "Back to Prompts",
|
"com_ui_back_to_prompts": "Back to Prompts",
|
||||||
"com_ui_backup_codes": "Backup Codes",
|
"com_ui_backup_codes": "Backup Codes",
|
||||||
|
"com_ui_backup_code_number": "Code #{{number}}",
|
||||||
"com_ui_backup_codes_regenerate_error": "There was an error regenerating backup codes",
|
"com_ui_backup_codes_regenerate_error": "There was an error regenerating backup codes",
|
||||||
"com_ui_backup_codes_regenerated": "Backup codes have been regenerated successfully",
|
"com_ui_backup_codes_regenerated": "Backup codes have been regenerated successfully",
|
||||||
|
"com_ui_backup_codes_security_info": "For security reasons, backup codes are only displayed once when generated. Please save them in a secure location.",
|
||||||
|
"com_ui_backup_codes_status": "Backup Codes Status",
|
||||||
"com_ui_basic": "Basic",
|
"com_ui_basic": "Basic",
|
||||||
"com_ui_basic_auth_header": "Basic authorization header",
|
"com_ui_basic_auth_header": "Basic authorization header",
|
||||||
"com_ui_bearer": "Bearer",
|
"com_ui_bearer": "Bearer",
|
||||||
|
|
@ -848,7 +851,6 @@
|
||||||
"com_ui_fork_split_target_setting": "Start fork from target message by default",
|
"com_ui_fork_split_target_setting": "Start fork from target message by default",
|
||||||
"com_ui_fork_success": "Successfully forked conversation",
|
"com_ui_fork_success": "Successfully forked conversation",
|
||||||
"com_ui_fork_visible": "Visible messages only",
|
"com_ui_fork_visible": "Visible messages only",
|
||||||
"com_ui_generate_backup": "Generate Backup Codes",
|
|
||||||
"com_ui_generate_qrcode": "Generate QR Code",
|
"com_ui_generate_qrcode": "Generate QR Code",
|
||||||
"com_ui_generating": "Generating...",
|
"com_ui_generating": "Generating...",
|
||||||
"com_ui_generation_settings": "Generation Settings",
|
"com_ui_generation_settings": "Generation Settings",
|
||||||
|
|
@ -938,7 +940,6 @@
|
||||||
"com_ui_new_conversation_title": "New Conversation Title",
|
"com_ui_new_conversation_title": "New Conversation Title",
|
||||||
"com_ui_next": "Next",
|
"com_ui_next": "Next",
|
||||||
"com_ui_no": "No",
|
"com_ui_no": "No",
|
||||||
"com_ui_no_backup_codes": "No backup codes available. Please generate new ones",
|
|
||||||
"com_ui_no_bookmarks": "it seems like you have no bookmarks yet. Click on a chat and add a new one",
|
"com_ui_no_bookmarks": "it seems like you have no bookmarks yet. Click on a chat and add a new one",
|
||||||
"com_ui_no_categories": "No categories available",
|
"com_ui_no_categories": "No categories available",
|
||||||
"com_ui_no_category": "No category",
|
"com_ui_no_category": "No category",
|
||||||
|
|
@ -1049,7 +1050,6 @@
|
||||||
"com_ui_shared_link_not_found": "Shared link not found",
|
"com_ui_shared_link_not_found": "Shared link not found",
|
||||||
"com_ui_shared_prompts": "Shared Prompts",
|
"com_ui_shared_prompts": "Shared Prompts",
|
||||||
"com_ui_shop": "Shopping",
|
"com_ui_shop": "Shopping",
|
||||||
"com_ui_show": "Show",
|
|
||||||
"com_ui_show_all": "Show All",
|
"com_ui_show_all": "Show All",
|
||||||
"com_ui_show_image_details": "Show Image Details",
|
"com_ui_show_image_details": "Show Image Details",
|
||||||
"com_ui_show_password": "Show password",
|
"com_ui_show_password": "Show password",
|
||||||
|
|
|
||||||
3
package-lock.json
generated
3
package-lock.json
generated
|
|
@ -51357,7 +51357,6 @@
|
||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.1.0",
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
"@rollup/plugin-replace": "^5.0.5",
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
|
||||||
"@rollup/plugin-typescript": "^12.1.2",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/bun": "^1.2.15",
|
"@types/bun": "^1.2.15",
|
||||||
"@types/diff": "^6.0.0",
|
"@types/diff": "^6.0.0",
|
||||||
|
|
@ -51877,7 +51876,7 @@
|
||||||
},
|
},
|
||||||
"packages/data-schemas": {
|
"packages/data-schemas": {
|
||||||
"name": "@librechat/data-schemas",
|
"name": "@librechat/data-schemas",
|
||||||
"version": "0.0.16",
|
"version": "0.0.17",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-alias": "^5.1.0",
|
"@rollup/plugin-alias": "^5.1.0",
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,15 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"build": "npm run clean && rollup -c --bundleConfigAsCjs",
|
"build": "npm run clean && rollup -c --bundleConfigAsCjs",
|
||||||
"build:watch": "rollup -c -w --bundleConfigAsCjs",
|
"build:dev": "npm run clean && NODE_ENV=development rollup -c --bundleConfigAsCjs",
|
||||||
|
"build:watch": "NODE_ENV=development rollup -c -w --bundleConfigAsCjs",
|
||||||
|
"build:watch:prod": "rollup -c -w --bundleConfigAsCjs",
|
||||||
"test": "jest --coverage --watch",
|
"test": "jest --coverage --watch",
|
||||||
"test:ci": "jest --coverage --ci",
|
"test:ci": "jest --coverage --ci",
|
||||||
"verify": "npm run test:ci",
|
"verify": "npm run test:ci",
|
||||||
"b:clean": "bun run rimraf dist",
|
"b:clean": "bun run rimraf dist",
|
||||||
"b:build": "bun run b:clean && bun run rollup -c --silent --bundleConfigAsCjs",
|
"b:build": "bun run b:clean && bun run rollup -c --silent --bundleConfigAsCjs",
|
||||||
|
"b:build:dev": "bun run b:clean && NODE_ENV=development bun run rollup -c --silent --bundleConfigAsCjs",
|
||||||
"start:everything-sse": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/examples/everything/sse.ts",
|
"start:everything-sse": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/examples/everything/sse.ts",
|
||||||
"start:everything": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/everything.ts",
|
"start:everything": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/everything.ts",
|
||||||
"start:filesystem": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/filesystem.ts",
|
"start:filesystem": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/filesystem.ts",
|
||||||
|
|
@ -45,7 +48,6 @@
|
||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.1.0",
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
"@rollup/plugin-replace": "^5.0.5",
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
|
||||||
"@rollup/plugin-typescript": "^12.1.2",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/bun": "^1.2.15",
|
"@types/bun": "^1.2.15",
|
||||||
"@types/diff": "^6.0.0",
|
"@types/diff": "^6.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// rollup.config.js
|
// rollup.config.js
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import json from '@rollup/plugin-json';
|
import json from '@rollup/plugin-json';
|
||||||
import terser from '@rollup/plugin-terser';
|
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
|
@ -10,13 +9,18 @@ import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
||||||
|
|
||||||
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if we're in development mode
|
||||||
|
*/
|
||||||
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
peerDepsExternal(),
|
peerDepsExternal(),
|
||||||
resolve({
|
resolve({
|
||||||
preferBuiltins: true,
|
preferBuiltins: true,
|
||||||
}),
|
}),
|
||||||
replace({
|
replace({
|
||||||
__IS_DEV__: process.env.NODE_ENV === 'development',
|
__IS_DEV__: isDevelopment,
|
||||||
preventAssignment: true,
|
preventAssignment: true,
|
||||||
}),
|
}),
|
||||||
commonjs({
|
commonjs({
|
||||||
|
|
@ -24,12 +28,18 @@ const plugins = [
|
||||||
requireReturnsDefault: 'auto',
|
requireReturnsDefault: 'auto',
|
||||||
}),
|
}),
|
||||||
typescript({
|
typescript({
|
||||||
tsconfig: './tsconfig.json',
|
tsconfig: './tsconfig.build.json',
|
||||||
outDir: './dist',
|
outDir: './dist',
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
inlineSourceMap: true,
|
/**
|
||||||
|
* Remove inline sourcemaps - they conflict with external sourcemaps
|
||||||
|
*/
|
||||||
|
inlineSourceMap: false,
|
||||||
|
/**
|
||||||
|
* Always include source content in sourcemaps for better debugging
|
||||||
|
*/
|
||||||
|
inlineSources: true,
|
||||||
}),
|
}),
|
||||||
terser(),
|
|
||||||
json(),
|
json(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -41,6 +51,20 @@ const cjsBuild = {
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
exports: 'named',
|
exports: 'named',
|
||||||
entryFileNames: '[name].js',
|
entryFileNames: '[name].js',
|
||||||
|
/**
|
||||||
|
* Always include sources in sourcemap for better debugging
|
||||||
|
*/
|
||||||
|
sourcemapExcludeSources: false,
|
||||||
|
/**
|
||||||
|
* Use absolute paths in sourcemaps for better IDE support
|
||||||
|
*/
|
||||||
|
sourcemapPathTransform: (relativeSourcePath) => {
|
||||||
|
/**
|
||||||
|
* Convert to absolute path for better debugger support
|
||||||
|
*/
|
||||||
|
const path = require('path');
|
||||||
|
return path.resolve(relativeSourcePath);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})],
|
external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})],
|
||||||
preserveSymlinks: true,
|
preserveSymlinks: true,
|
||||||
|
|
|
||||||
12
packages/api/tsconfig.build.json
Normal file
12
packages/api/tsconfig.build.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"removeComments": false,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"declarationMap": true
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist", "types", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@librechat/data-schemas",
|
"name": "@librechat/data-schemas",
|
||||||
"version": "0.0.16",
|
"version": "0.0.17",
|
||||||
"description": "Mongoose schemas and models for LibreChat",
|
"description": "Mongoose schemas and models for LibreChat",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.cjs",
|
"main": "dist/index.cjs",
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ const userSchema = new Schema<IUser>(
|
||||||
},
|
},
|
||||||
backupCodes: {
|
backupCodes: {
|
||||||
type: [BackupCodeSchema],
|
type: [BackupCodeSchema],
|
||||||
|
select: false,
|
||||||
},
|
},
|
||||||
refreshToken: {
|
refreshToken: {
|
||||||
type: [SessionSchema],
|
type: [SessionSchema],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue