Compare commits
4 Commits
v0.7.3
...
v0.7.4-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
302b28fc9b | ||
|
|
dad25bd297 | ||
|
|
a338decf90 | ||
|
|
2cf5228021 |
@@ -81,7 +81,7 @@ LibreChat brings together the future of assistant AIs with the revolutionary tec
|
||||
|
||||
With LibreChat, you no longer need to opt for ChatGPT Plus and can instead use free or pay-per-call APIs. We welcome contributions, cloning, and forking to enhance the capabilities of this advanced chatbot platform.
|
||||
|
||||
[](https://www.youtube.com/watch?v=YLVUW5UP9N0)
|
||||
[](https://www.youtube.com/watch?v=bSVHEbVPNl4)
|
||||
Click on the thumbnail to open the video☝️
|
||||
|
||||
---
|
||||
|
||||
@@ -56,10 +56,11 @@ const updateUser = async function (userId, updateData) {
|
||||
* Creates a new user, optionally with a TTL of 1 week.
|
||||
* @param {MongoUser} data - The user data to be created, must contain user_id.
|
||||
* @param {boolean} [disableTTL=true] - Whether to disable the TTL. Defaults to `true`.
|
||||
* @param {boolean} [returnUser=false] - Whether to disable the TTL. Defaults to `true`.
|
||||
* @returns {Promise<ObjectId>} A promise that resolves to the created user document ID.
|
||||
* @throws {Error} If a user with the same user_id already exists.
|
||||
*/
|
||||
const createUser = async (data, disableTTL = true) => {
|
||||
const createUser = async (data, disableTTL = true, returnUser = false) => {
|
||||
const userData = {
|
||||
...data,
|
||||
expiresAt: disableTTL ? null : new Date(Date.now() + 604800 * 1000), // 1 week in milliseconds
|
||||
@@ -71,6 +72,9 @@ const createUser = async (data, disableTTL = true) => {
|
||||
|
||||
try {
|
||||
const user = await User.create(userData);
|
||||
if (returnUser) {
|
||||
return user.toObject();
|
||||
}
|
||||
return user._id;
|
||||
} catch (error) {
|
||||
if (error.code === 11000) {
|
||||
|
||||
@@ -62,7 +62,7 @@ const sendVerificationEmail = async (user) => {
|
||||
let verifyToken = crypto.randomBytes(32).toString('hex');
|
||||
const hash = bcrypt.hashSync(verifyToken, 10);
|
||||
|
||||
const verificationLink = `${domains.client}/verify?token=${verifyToken}&email=${user.email}`;
|
||||
const verificationLink = `${domains.client}/verify?token=${verifyToken}&email=${encodeURIComponent(user.email)}`;
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
subject: 'Verify your email',
|
||||
@@ -91,7 +91,7 @@ const sendVerificationEmail = async (user) => {
|
||||
*/
|
||||
const verifyEmail = async (req) => {
|
||||
const { email, token } = req.body;
|
||||
let emailVerificationData = await Token.findOne({ email });
|
||||
let emailVerificationData = await Token.findOne({ email: decodeURIComponent(email) });
|
||||
|
||||
if (!emailVerificationData) {
|
||||
logger.warn(`[verifyEmail] [No email verification data found] [Email: ${email}]`);
|
||||
@@ -363,7 +363,7 @@ const resendVerificationEmail = async (req) => {
|
||||
let verifyToken = crypto.randomBytes(32).toString('hex');
|
||||
const hash = bcrypt.hashSync(verifyToken, 10);
|
||||
|
||||
const verificationLink = `${domains.client}/verify?token=${verifyToken}&email=${user.email}`;
|
||||
const verificationLink = `${domains.client}/verify?token=${verifyToken}&email=${encodeURIComponent(user.email)}`;
|
||||
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
|
||||
@@ -164,8 +164,7 @@ async function setupOpenId() {
|
||||
emailVerified: userinfo.email_verified || false,
|
||||
name: fullName,
|
||||
};
|
||||
const userId = await createUser();
|
||||
user._id = userId;
|
||||
user = await createUser(user, true, true);
|
||||
} else {
|
||||
user.provider = 'openid';
|
||||
user.openidId = userinfo.sub;
|
||||
|
||||
@@ -5,6 +5,7 @@ import Plugin from '~/components/Messages/Content/Plugin';
|
||||
import Error from '~/components/Messages/Content/Error';
|
||||
import { DelayedRender } from '~/components/ui';
|
||||
import EditMessage from './EditMessage';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import Container from './Container';
|
||||
import Markdown from './Markdown';
|
||||
import { cn } from '~/utils';
|
||||
@@ -14,6 +15,38 @@ export const ErrorMessage = ({
|
||||
message,
|
||||
className = '',
|
||||
}: Pick<TDisplayProps, 'text' | 'className' | 'message'>) => {
|
||||
const localize = useLocalize();
|
||||
if (text === 'Error connecting to server, try refreshing the page.') {
|
||||
console.log('error message', message);
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="text-message mb-[0.625rem] mt-1 flex min-h-[20px] flex-col items-start gap-3 overflow-x-auto">
|
||||
<div className="markdown prose dark:prose-invert light w-full break-words dark:text-gray-100">
|
||||
<div className="absolute">
|
||||
<p className="relative">
|
||||
<span className="result-thinking" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<DelayedRender delay={5500}>
|
||||
<Container message={message}>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-md border border-red-500 bg-red-500/10 px-3 py-2 text-sm text-gray-600 dark:text-gray-200',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{localize('com_ui_error_connection')}
|
||||
</div>
|
||||
</Container>
|
||||
</DelayedRender>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Container message={message}>
|
||||
<div
|
||||
|
||||
@@ -58,7 +58,11 @@ export default function HoverButtons({
|
||||
return null;
|
||||
}
|
||||
|
||||
const { isCreatedByUser } = message;
|
||||
const { isCreatedByUser, error } = message;
|
||||
|
||||
if (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onEdit = () => {
|
||||
if (isEditing) {
|
||||
|
||||
@@ -22,6 +22,7 @@ export default function Message(props: TMessageProps) {
|
||||
edit,
|
||||
index,
|
||||
isLast,
|
||||
assistant,
|
||||
enterEdit,
|
||||
handleScroll,
|
||||
conversation,
|
||||
@@ -44,6 +45,8 @@ export default function Message(props: TMessageProps) {
|
||||
let messageLabel = '';
|
||||
if (isCreatedByUser) {
|
||||
messageLabel = UsernameDisplay ? user?.name || user?.username : localize('com_user_message');
|
||||
} else if (assistant) {
|
||||
messageLabel = assistant.name ?? 'Assistant';
|
||||
} else {
|
||||
messageLabel = message.sender;
|
||||
}
|
||||
@@ -61,7 +64,7 @@ export default function Message(props: TMessageProps) {
|
||||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon message={message} conversation={conversation} />
|
||||
<Icon message={message} conversation={conversation} assistant={assistant} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const useDelayedRender = (delay: number) => {
|
||||
const [delayed, setDelayed] = useState(true);
|
||||
const timerPromiseRef = useRef<Promise<void> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => setDelayed(false), delay);
|
||||
return () => clearTimeout(timeout);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
return (fn: () => ReactNode) => !delayed && fn();
|
||||
if (delayed) {
|
||||
const timerPromise = new Promise<void>((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
setDelayed(false);
|
||||
resolve();
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
});
|
||||
|
||||
timerPromiseRef.current = timerPromise;
|
||||
}
|
||||
|
||||
return () => {
|
||||
timerPromiseRef.current = null;
|
||||
};
|
||||
}, [delay, delayed]);
|
||||
|
||||
return (fn: () => ReactNode) => {
|
||||
if (delayed && timerPromiseRef.current) {
|
||||
throw timerPromiseRef.current;
|
||||
}
|
||||
return fn();
|
||||
};
|
||||
};
|
||||
|
||||
export default useDelayedRender;
|
||||
|
||||
@@ -117,6 +117,7 @@ export default {
|
||||
com_ui_instructions: 'Instructions',
|
||||
com_ui_description: 'Description',
|
||||
com_ui_error: 'Error',
|
||||
com_ui_error_connection: 'Error connecting to server, try refreshing the page.',
|
||||
com_ui_select: 'Select',
|
||||
com_ui_input: 'Input',
|
||||
com_ui_close: 'Close',
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -29325,7 +29325,7 @@
|
||||
},
|
||||
"packages/data-provider": {
|
||||
"name": "librechat-data-provider",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "LibreChat",
|
||||
"version": "0.7.3",
|
||||
"version": "0.7.4-rc1",
|
||||
"description": "",
|
||||
"workspaces": [
|
||||
"api",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "librechat-data-provider",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"description": "data services for librechat apps",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.es.js",
|
||||
|
||||
@@ -786,7 +786,7 @@ export enum SettingsTabValues {
|
||||
/** Enum for app-wide constants */
|
||||
export enum Constants {
|
||||
/** Key for the app's version. */
|
||||
VERSION = 'v0.7.3',
|
||||
VERSION = 'v0.7.4-rc1',
|
||||
/** Key for the Custom Config's version (librechat.yaml). */
|
||||
CONFIG_VERSION = '1.1.4',
|
||||
/** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */
|
||||
|
||||
Reference in New Issue
Block a user