Compare commits
26 Commits
dev-stagin
...
feat/multi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97a6074edc | ||
|
|
126b1fe412 | ||
|
|
8053ef32bf | ||
|
|
a80c1dd2ce | ||
|
|
9962ec318c | ||
|
|
1dd644b72e | ||
|
|
c925f9f39c | ||
|
|
71effb1a66 | ||
|
|
e3acd18c07 | ||
|
|
c371491e71 | ||
|
|
122773a2be | ||
|
|
fcc1eb45f4 | ||
|
|
f434ddc125 | ||
|
|
5a9dcef1bc | ||
|
|
7c0324695a | ||
|
|
17c09e6e89 | ||
|
|
e488dab5db | ||
|
|
4b75890d1f | ||
|
|
4e82eab01c | ||
|
|
f4c50ed25b | ||
|
|
9885982233 | ||
|
|
6b60ee4df8 | ||
|
|
b170a57482 | ||
|
|
bb6a69e98a | ||
|
|
a88e99dcfb | ||
|
|
b7a6d7caa6 |
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
roots: ['<rootDir>/src'],
|
||||
roots: ['<rootDir>/src', '<rootDir>/../terms'],
|
||||
testEnvironment: 'jsdom',
|
||||
testEnvironmentOptions: {
|
||||
url: 'http://localhost:3080',
|
||||
@@ -29,6 +29,7 @@ module.exports = {
|
||||
'^test/(.*)$': '<rootDir>/test/$1',
|
||||
'^~/(.*)$': '<rootDir>/src/$1',
|
||||
'^librechat-data-provider/react-query$': '<rootDir>/../node_modules/librechat-data-provider/src/react-query',
|
||||
'^.+\\.md\\?raw$': '<rootDir>/test/rawFileMock.js',
|
||||
},
|
||||
restoreMocks: true,
|
||||
testResultsProcessor: 'jest-junit',
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { TTermsOfService } from 'librechat-data-provider';
|
||||
import React from 'react';
|
||||
import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { useAcceptTermsMutation } from '~/data-provider';
|
||||
import { OGDialog, Button, Spinner } from '~/components';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { OGDialog } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
interface TermsModalProps {
|
||||
open: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
onAccept: () => void;
|
||||
onDecline: () => void;
|
||||
title?: string;
|
||||
modalContent?: string;
|
||||
}
|
||||
|
||||
const TermsAndConditionsModal = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
@@ -14,17 +22,10 @@ const TermsAndConditionsModal = ({
|
||||
onDecline,
|
||||
title,
|
||||
modalContent,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
onAccept: () => void;
|
||||
onDecline: () => void;
|
||||
title?: string;
|
||||
contentUrl?: string;
|
||||
modalContent?: TTermsOfService['modalContent'];
|
||||
}) => {
|
||||
}: TermsModalProps) => {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
|
||||
const acceptTermsMutation = useAcceptTermsMutation({
|
||||
onSuccess: () => {
|
||||
onAccept();
|
||||
@@ -35,6 +36,8 @@ const TermsAndConditionsModal = ({
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = acceptTermsMutation.isLoading;
|
||||
|
||||
const handleAccept = () => {
|
||||
acceptTermsMutation.mutate();
|
||||
};
|
||||
@@ -51,36 +54,22 @@ const TermsAndConditionsModal = ({
|
||||
onOpenChange(isOpen);
|
||||
};
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (typeof modalContent === 'string') {
|
||||
return modalContent;
|
||||
}
|
||||
|
||||
if (Array.isArray(modalContent)) {
|
||||
return modalContent.join('\n');
|
||||
}
|
||||
|
||||
return '';
|
||||
}, [modalContent]);
|
||||
|
||||
return (
|
||||
<OGDialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogTemplate
|
||||
<OGDialogTemplate
|
||||
title={title ?? localize('com_ui_terms_and_conditions')}
|
||||
className="w-11/12 max-w-3xl sm:w-3/4 md:w-1/2 lg:w-2/5"
|
||||
showCloseButton={false}
|
||||
showCancelButton={false}
|
||||
main={
|
||||
<section
|
||||
// Motivation: This is a dialog, so its content should be focusable
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
tabIndex={0}
|
||||
className="max-h-[60vh] overflow-y-auto p-4"
|
||||
aria-label={localize('com_ui_terms_and_conditions')}
|
||||
>
|
||||
<div className="prose dark:prose-invert w-full max-w-none !text-text-primary">
|
||||
{content !== '' ? (
|
||||
<MarkdownLite content={content} />
|
||||
<div className="prose dark:prose-invert w-full max-w-none text-text-primary">
|
||||
{modalContent ? (
|
||||
<MarkdownLite content={modalContent} />
|
||||
) : (
|
||||
<p>{localize('com_ui_no_terms_content')}</p>
|
||||
)}
|
||||
@@ -89,18 +78,12 @@ const TermsAndConditionsModal = ({
|
||||
}
|
||||
buttons={
|
||||
<>
|
||||
<button
|
||||
onClick={handleDecline}
|
||||
className="inline-flex h-10 items-center justify-center rounded-lg border border-border-heavy bg-surface-secondary px-4 py-2 text-sm text-text-primary hover:bg-surface-active"
|
||||
>
|
||||
<Button onClick={handleDecline} variant="destructive" disabled={isLoading}>
|
||||
{localize('com_ui_decline')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleAccept}
|
||||
className="inline-flex h-10 items-center justify-center rounded-lg border border-border-heavy bg-surface-secondary px-4 py-2 text-sm text-text-primary hover:bg-green-500 hover:text-white focus:bg-green-500 focus:text-white dark:hover:bg-green-600 dark:focus:bg-green-600"
|
||||
>
|
||||
{localize('com_ui_accept')}
|
||||
</button>
|
||||
</Button>
|
||||
<Button onClick={handleAccept} variant="submit" disabled={isLoading}>
|
||||
{isLoading ? <Spinner /> : localize('com_ui_accept')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
@@ -14,11 +14,14 @@ import {
|
||||
FileMapContext,
|
||||
SetConvoProvider,
|
||||
} from '~/Providers';
|
||||
import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal';
|
||||
import TermsAndConditionsModal from '~/components/Chat/TermsAndConditionsModal';
|
||||
import { useUserTermsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
import { Nav, MobileNav } from '~/components/Nav';
|
||||
import { useHealthCheck } from '~/data-provider';
|
||||
import { Banner } from '~/components/Banners';
|
||||
import { getTermsMarkdown } from '~/utils';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import store from '~/store';
|
||||
|
||||
export default function Root() {
|
||||
const [showTerms, setShowTerms] = useState(false);
|
||||
@@ -37,6 +40,9 @@ export default function Root() {
|
||||
const agentsMap = useAgentsMap({ isAuthenticated });
|
||||
const fileMap = useFileMap({ isAuthenticated });
|
||||
|
||||
const lang = useRecoilValue(store.lang);
|
||||
const modalContent = getTermsMarkdown(lang);
|
||||
|
||||
const { data: config } = useGetStartupConfig();
|
||||
const { data: termsData } = useUserTermsQuery({
|
||||
enabled: isAuthenticated && config?.interface?.termsOfService?.modalAcceptance === true,
|
||||
@@ -86,7 +92,7 @@ export default function Root() {
|
||||
onAccept={handleAcceptTerms}
|
||||
onDecline={handleDeclineTerms}
|
||||
title={config.interface.termsOfService.modalTitle}
|
||||
modalContent={config.interface.termsOfService.modalContent}
|
||||
modalContent={modalContent}
|
||||
/>
|
||||
)}
|
||||
</AssistantsMapContext.Provider>
|
||||
|
||||
@@ -15,6 +15,7 @@ export * from './languages';
|
||||
export * from './endpoints';
|
||||
export * from './localStorage';
|
||||
export * from './promptGroups';
|
||||
export * from './termsContent';
|
||||
export { default as cn } from './cn';
|
||||
export { default as logger } from './logger';
|
||||
export { default as buildTree } from './buildTree';
|
||||
|
||||
45
client/src/utils/termsContent.ts
Normal file
45
client/src/utils/termsContent.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import terms_en from '../../../terms/terms_en.md?raw';
|
||||
import terms_de from '../../../terms/terms_de.md?raw';
|
||||
import terms_fr from '../../../terms/terms_fr.md?raw';
|
||||
|
||||
/**
|
||||
* A mapping of language codes to their respective terms markdown content.
|
||||
*
|
||||
* You can add both base language codes (e.g. 'en') and full codes (e.g. 'pt-BR') if needed.
|
||||
*
|
||||
* @type {Record<string, string>}
|
||||
*/
|
||||
const markdownMap: Record<string, string> = {
|
||||
en: terms_en,
|
||||
de: terms_de,
|
||||
fr: terms_fr,
|
||||
// For example, to support Brazilian Portuguese, you could add:
|
||||
// 'pt-BR': terms_ptBR,
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the terms markdown content for the specified language.
|
||||
*
|
||||
* The function first checks if an exact language code match exists in the markdown map.
|
||||
* If not, it attempts to extract the base language (e.g., 'pt' from 'pt-BR') and checks again.
|
||||
* If no match is found, it falls back to English.
|
||||
*
|
||||
* @param {string} lang - The language code, which may include a region (e.g., 'pt-BR', 'en-US').
|
||||
* @returns {string} The markdown content corresponding to the language,
|
||||
* or the English version if no matching language is found.
|
||||
*/
|
||||
export function getTermsMarkdown(lang: string): string {
|
||||
// Check for exact language code match (e.g., 'pt-BR').
|
||||
if (lang in markdownMap) {
|
||||
return markdownMap[lang];
|
||||
}
|
||||
|
||||
// Extract the base language (e.g., 'pt' from 'pt-BR') and check again.
|
||||
const baseLang = lang.split('-')[0];
|
||||
if (baseLang in markdownMap) {
|
||||
return markdownMap[baseLang];
|
||||
}
|
||||
|
||||
// Fall back to English if no match is found.
|
||||
return markdownMap['en'];
|
||||
}
|
||||
1
client/test/rawFileMock.js
Normal file
1
client/test/rawFileMock.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = 'MOCK_MARKDOWN_CONTENT';
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
- rag_api
|
||||
restart: always
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
- "host.docker.internal:host-gateway"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -30,7 +30,7 @@ services:
|
||||
- ./images:/app/client/public/images
|
||||
- ./uploads:/app/uploads
|
||||
- ./logs:/app/api/logs
|
||||
|
||||
- ./terms:/app/terms
|
||||
client:
|
||||
image: nginx:1.27.0-alpine
|
||||
container_name: LibreChat-NGINX
|
||||
|
||||
@@ -27,6 +27,7 @@ services:
|
||||
- ./images:/app/client/public/images
|
||||
- ./uploads:/app/uploads
|
||||
- ./logs:/app/api/logs
|
||||
- ./terms:/app/terms
|
||||
mongodb:
|
||||
container_name: chat-mongodb
|
||||
image: mongo
|
||||
|
||||
@@ -27,42 +27,6 @@ interface:
|
||||
openNewTab: true
|
||||
modalAcceptance: true
|
||||
modalTitle: "Terms of Service for LibreChat"
|
||||
modalContent: |
|
||||
# Terms and Conditions for LibreChat
|
||||
|
||||
*Effective Date: February 18, 2024*
|
||||
|
||||
Welcome to LibreChat, the informational website for the open-source AI chat platform, available at https://librechat.ai. These Terms of Service ("Terms") govern your use of our website and the services we offer. By accessing or using the Website, you agree to be bound by these Terms and our Privacy Policy, accessible at https://librechat.ai//privacy.
|
||||
|
||||
## 1. Ownership
|
||||
|
||||
Upon purchasing a package from LibreChat, you are granted the right to download and use the code for accessing an admin panel for LibreChat. While you own the downloaded code, you are expressly prohibited from reselling, redistributing, or otherwise transferring the code to third parties without explicit permission from LibreChat.
|
||||
|
||||
## 2. User Data
|
||||
|
||||
We collect personal data, such as your name, email address, and payment information, as described in our Privacy Policy. This information is collected to provide and improve our services, process transactions, and communicate with you.
|
||||
|
||||
## 3. Non-Personal Data Collection
|
||||
|
||||
The Website uses cookies to enhance user experience, analyze site usage, and facilitate certain functionalities. By using the Website, you consent to the use of cookies in accordance with our Privacy Policy.
|
||||
|
||||
## 4. Use of the Website
|
||||
|
||||
You agree to use the Website only for lawful purposes and in a manner that does not infringe the rights of, restrict, or inhibit anyone else's use and enjoyment of the Website. Prohibited behavior includes harassing or causing distress or inconvenience to any person, transmitting obscene or offensive content, or disrupting the normal flow of dialogue within the Website.
|
||||
|
||||
## 5. Governing Law
|
||||
|
||||
These Terms shall be governed by and construed in accordance with the laws of the United States, without giving effect to any principles of conflicts of law.
|
||||
|
||||
## 6. Changes to the Terms
|
||||
|
||||
We reserve the right to modify these Terms at any time. We will notify users of any changes by email. Your continued use of the Website after such changes have been notified will constitute your consent to such changes.
|
||||
|
||||
## 7. Contact Information
|
||||
|
||||
If you have any questions about these Terms, please contact us at contact@librechat.ai.
|
||||
|
||||
By using the Website, you acknowledge that you have read these Terms of Service and agree to be bound by them.
|
||||
|
||||
endpointsMenu: true
|
||||
modelSelect: true
|
||||
|
||||
@@ -477,7 +477,6 @@ const termsOfServiceSchema = z.object({
|
||||
openNewTab: z.boolean().optional(),
|
||||
modalAcceptance: z.boolean().optional(),
|
||||
modalTitle: z.string().optional(),
|
||||
modalContent: z.string().or(z.array(z.string())).optional(),
|
||||
});
|
||||
|
||||
export type TTermsOfService = z.infer<typeof termsOfServiceSchema>;
|
||||
|
||||
60
terms/terms_de.md
Normal file
60
terms/terms_de.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Allgemeine Geschäftsbedingungen für [Ihr Unternehmen/Produktname]
|
||||
|
||||
*Gültigkeitsdatum: [Datum einfügen]*
|
||||
|
||||
Willkommen bei [Ihre Webseite], erreichbar unter [URL einfügen]. Diese Allgemeinen Geschäftsbedingungen („AGB“) regeln Ihre Nutzung unserer Website und Dienstleistungen. Durch den Zugriff auf oder die Nutzung unserer Website stimmen Sie diesen Bedingungen und unserer Datenschutzerklärung zu, die unter [URL zur Datenschutzerklärung einfügen] verfügbar ist.
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. **Akzeptanz der Bedingungen**
|
||||
- Zustimmung durch Nutzung
|
||||
|
||||
2. **Eigentum und Lizenz**
|
||||
- Gewährte Rechte
|
||||
- Einschränkungen bei Weitergabe oder Weiterverkauf
|
||||
|
||||
3. **Benutzerkonten** (falls zutreffend)
|
||||
- Erstellung und Sicherheit des Kontos
|
||||
- Verantwortlichkeiten des Benutzers
|
||||
|
||||
4. **Benutzerdaten**
|
||||
- Erhebung und Nutzung personenbezogener Daten
|
||||
- Hinweis auf Datenschutzerklärung
|
||||
|
||||
5. **Cookies und nicht-personenbezogene Daten**
|
||||
- Nutzung und Zustimmung
|
||||
|
||||
6. **Richtlinien zur akzeptablen Nutzung**
|
||||
- Zulässige und verbotene Aktivitäten
|
||||
- Konsequenzen bei Verstößen
|
||||
|
||||
7. **Links und Dienste Dritter**
|
||||
- Haftungsausschluss für Inhalte Dritter
|
||||
|
||||
8. **Rechte am geistigen Eigentum**
|
||||
- Eigentum am Inhalt
|
||||
- Einschränkungen der Nutzung geistigen Eigentums
|
||||
|
||||
9. **Gewährleistungsausschluss**
|
||||
- Haftungsbeschränkungen
|
||||
|
||||
10. **Freistellung**
|
||||
- Verpflichtungen des Nutzers zur Freistellung des Unternehmens
|
||||
|
||||
11. **Haftungsbeschränkung**
|
||||
- Begrenzung der Haftung und Ausschlüsse
|
||||
|
||||
12. **Kündigung**
|
||||
- Gründe für eine Kündigung
|
||||
- Auswirkungen einer Kündigung
|
||||
|
||||
13. **Geltendes Recht und Gerichtsstand**
|
||||
- Anwendbares Recht und Gerichtsstand
|
||||
|
||||
14. **Änderungen dieser Bedingungen**
|
||||
- Benachrichtigung und Zustimmung zu Änderungen
|
||||
|
||||
15. **Kontaktinformationen**
|
||||
- Kontaktmöglichkeiten bei Fragen
|
||||
|
||||
Durch den Zugriff auf und die Nutzung von [Ihre Webseite/Dienstleistung] erkennen Sie an, dass Sie diese Allgemeinen Geschäftsbedingungen gelesen und verstanden haben und ihnen zustimmen.
|
||||
60
terms/terms_en.md
Normal file
60
terms/terms_en.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Terms and Conditions for [Your Company/Product Name]
|
||||
|
||||
*Effective Date: [Insert Effective Date]*
|
||||
|
||||
Welcome to [Your Website], accessible at [Insert URL]. These Terms and Conditions ("Terms") govern your use of our website and services. By accessing or using our Website, you agree to comply with these Terms and our Privacy Policy, accessible at [Insert URL to Privacy Policy].
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. **Acceptance of Terms**
|
||||
- Agreement to terms upon use
|
||||
|
||||
2. **Ownership and License**
|
||||
- Rights granted
|
||||
- Restrictions on redistribution or resale
|
||||
|
||||
3. **User Accounts** (if applicable)
|
||||
- Account creation and security
|
||||
- Responsibilities of the user
|
||||
|
||||
4. **User Data**
|
||||
- Collection and use of personal data
|
||||
- Reference to Privacy Policy
|
||||
|
||||
5. **Cookies and Non-Personal Data**
|
||||
- Usage and consent
|
||||
|
||||
6. **Acceptable Use Policy**
|
||||
- Permitted and prohibited activities
|
||||
- Consequences of violations
|
||||
|
||||
7. **Third-Party Links and Services**
|
||||
- Disclaimer for third-party content
|
||||
|
||||
8. **Intellectual Property Rights**
|
||||
- Ownership of content
|
||||
- Restrictions on use of intellectual property
|
||||
|
||||
9. **Disclaimer of Warranties**
|
||||
- Limitations on liability
|
||||
|
||||
10. **Indemnification**
|
||||
- User obligations to indemnify the company
|
||||
|
||||
11. **Limitation of Liability**
|
||||
- Cap on liability and exclusions
|
||||
|
||||
12. **Termination**
|
||||
- Grounds for termination
|
||||
- Effects of termination
|
||||
|
||||
13. **Governing Law and Jurisdiction**
|
||||
- Applicable laws and legal jurisdiction
|
||||
|
||||
14. **Changes to These Terms**
|
||||
- Notification and acceptance of changes
|
||||
|
||||
15. **Contact Information**
|
||||
- How users can reach you with questions
|
||||
|
||||
By accessing and using [Your Website/Service], you acknowledge that you have read, understood, and agree to be bound by these Terms and Conditions.
|
||||
60
terms/terms_fr.md
Normal file
60
terms/terms_fr.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Conditions Générales pour [Votre Entreprise/Nom du Produit]
|
||||
|
||||
*Date d'entrée en vigueur : [Insérer la date]*
|
||||
|
||||
Bienvenue sur [Votre site web], accessible à l'adresse [Insérer URL]. Ces Conditions Générales (« CG ») régissent votre utilisation de notre site web et de nos services. En accédant ou en utilisant notre site web, vous acceptez ces conditions ainsi que notre Politique de confidentialité disponible à l'adresse [Insérer URL vers la Politique de confidentialité].
|
||||
|
||||
## Table des matières
|
||||
|
||||
1. **Acceptation des conditions**
|
||||
- Accord des conditions par utilisation
|
||||
|
||||
2. **Propriété et Licence**
|
||||
- Droits accordés
|
||||
- Restrictions sur la redistribution ou la revente
|
||||
|
||||
3. **Comptes utilisateur** (le cas échéant)
|
||||
- Création et sécurité du compte
|
||||
- Responsabilités de l'utilisateur
|
||||
|
||||
4. **Données Utilisateur**
|
||||
- Collecte et utilisation des données personnelles
|
||||
- Référence à la Politique de confidentialité
|
||||
|
||||
5. **Cookies et données non personnelles**
|
||||
- Utilisation et consentement
|
||||
|
||||
6. **Politique d'utilisation acceptable**
|
||||
- Activités autorisées et interdites
|
||||
- Conséquences en cas de violations
|
||||
|
||||
7. **Liens et services tiers**
|
||||
- Exclusion de responsabilité pour les contenus tiers
|
||||
|
||||
8. **Droits de propriété intellectuelle**
|
||||
- Propriété du contenu
|
||||
- Restrictions sur l'utilisation de la propriété intellectuelle
|
||||
|
||||
9. **Exclusion de garanties**
|
||||
- Limitations de responsabilité
|
||||
|
||||
10. **Indemnisation**
|
||||
- Obligations de l'utilisateur à indemniser l'entreprise
|
||||
|
||||
11. **Limitation de responsabilité**
|
||||
- Plafond de responsabilité et exclusions
|
||||
|
||||
12. **Résiliation**
|
||||
- Motifs de résiliation
|
||||
- Effets de la résiliation
|
||||
|
||||
13. **Loi applicable et juridiction compétente**
|
||||
- Lois applicables et juridiction
|
||||
|
||||
14. **Modifications des présentes conditions**
|
||||
- Notification et acceptation des modifications
|
||||
|
||||
15. **Informations de contact**
|
||||
- Comment nous contacter en cas de questions
|
||||
|
||||
En accédant et en utilisant [Votre site web/service], vous reconnaissez avoir lu, compris et accepté d'être lié par ces Conditions Générales.
|
||||
Reference in New Issue
Block a user