From 99c702de17ebc2f6f81ae4d67a8a1f17b339d1d8 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Fri, 27 Dec 2024 17:22:35 +0100 Subject: [PATCH] fix: remove max upload size from settings / use env var instead to centralize the config --- .../channels/console/i18n/en/label.json | 1 - .../channels/console/i18n/fr/label.json | 1 - .../extensions/channels/console/settings.ts | 28 ++++++---------- .../channels/web/i18n/en/label.json | 1 - .../channels/web/i18n/fr/label.json | 1 - api/src/extensions/channels/web/settings.ts | 32 +++++++------------ api/src/extensions/channels/web/types.ts | 18 ----------- docker/.env.example | 2 +- frontend/src/pages/api/config.ts | 2 +- widget/src/ChatWidget.tsx | 9 +++--- widget/src/UiChatWidget.tsx | 7 ++-- widget/src/components/UserInput.tsx | 5 +-- widget/src/constants/defaultConfig.ts | 7 +++- widget/src/providers/ConfigProvider.tsx | 20 ++++-------- widget/src/providers/SettingsProvider.tsx | 4 --- widget/src/types/config.types.ts | 14 ++++++++ widget/src/utils/SocketIoClient.ts | 2 +- 17 files changed, 63 insertions(+), 91 deletions(-) create mode 100644 widget/src/types/config.types.ts diff --git a/api/src/extensions/channels/console/i18n/en/label.json b/api/src/extensions/channels/console/i18n/en/label.json index b9e4e4f1..30081824 100644 --- a/api/src/extensions/channels/console/i18n/en/label.json +++ b/api/src/extensions/channels/console/i18n/en/label.json @@ -17,6 +17,5 @@ "show_emoji": "Enable Emoji Picker", "show_file": "Enable Attachment Uploader", "show_location": "Enable Geolocation Share", - "allowed_upload_size": "Max Upload Size (in bytes)", "allowed_upload_types": "Allowed Upload Mime Types (comma separated)" } diff --git a/api/src/extensions/channels/console/i18n/fr/label.json b/api/src/extensions/channels/console/i18n/fr/label.json index ce5473a5..89590e1c 100644 --- a/api/src/extensions/channels/console/i18n/fr/label.json +++ b/api/src/extensions/channels/console/i18n/fr/label.json @@ -17,6 +17,5 @@ "show_emoji": "Activer le sélecteur d'Emojis", "show_file": "Activer l'upload de fichiers", "show_location": "Activer le partage de géolocalisation", - "allowed_upload_size": "Taille maximale de téléchargement (en octets)", "allowed_upload_types": "Types MIME autorisés pour le téléchargement (séparés par des virgules)" } diff --git a/api/src/extensions/channels/console/settings.ts b/api/src/extensions/channels/console/settings.ts index 62875828..6df17119 100644 --- a/api/src/extensions/channels/console/settings.ts +++ b/api/src/extensions/channels/console/settings.ts @@ -10,8 +10,6 @@ import { ChannelSetting } from '@/channel/types'; import { config } from '@/config'; import { SettingType } from '@/setting/schemas/types'; -import { Web } from '../web/types'; - export const CONSOLE_CHANNEL_NAME = 'console-channel'; export const CONSOLE_CHANNEL_NAMESPACE = 'console_channel'; @@ -19,68 +17,62 @@ export const CONSOLE_CHANNEL_NAMESPACE = 'console_channel'; export default [ { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.allowed_domains, + label: 'allowed_domains', value: config.security.cors.allowOrigins.join(','), type: SettingType.text, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.start_button, + label: 'start_button', value: true, type: SettingType.checkbox, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.input_disabled, + label: 'input_disabled', value: false, type: SettingType.checkbox, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.persistent_menu, + label: 'persistent_menu', value: true, type: SettingType.checkbox, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.greeting_message, + label: 'greeting_message', value: 'Welcome! Ready to start a conversation with our chatbot?', type: SettingType.textarea, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.theme_color, + label: 'theme_color', value: 'teal', type: SettingType.select, options: ['teal', 'orange', 'red', 'green', 'blue', 'dark'], }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.show_emoji, + label: 'show_emoji', value: true, type: SettingType.checkbox, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.show_file, + label: 'show_file', value: true, type: SettingType.checkbox, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.show_location, + label: 'show_location', value: true, type: SettingType.checkbox, }, { group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.allowed_upload_size, - value: 2500000, - type: SettingType.number, - }, - { - group: CONSOLE_CHANNEL_NAMESPACE, - label: Web.SettingLabel.allowed_upload_types, + label: 'allowed_upload_types', value: 'audio/mpeg,audio/x-ms-wma,audio/vnd.rn-realaudio,audio/x-wav,image/gif,image/jpeg,image/png,image/tiff,image/vnd.microsoft.icon,image/vnd.djvu,image/svg+xml,text/css,text/csv,text/html,text/plain,text/xml,video/mpeg,video/mp4,video/quicktime,video/x-ms-wmv,video/x-msvideo,video/x-flv,video/web,application/msword,application/vnd.ms-powerpoint,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.tex,application/vnd.oasis.opendocument.spreadsheet,application/vnd.oasis.opendocument.graphics,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.wordprocessingml.document', type: SettingType.textarea, diff --git a/api/src/extensions/channels/web/i18n/en/label.json b/api/src/extensions/channels/web/i18n/en/label.json index b9e4e4f1..30081824 100644 --- a/api/src/extensions/channels/web/i18n/en/label.json +++ b/api/src/extensions/channels/web/i18n/en/label.json @@ -17,6 +17,5 @@ "show_emoji": "Enable Emoji Picker", "show_file": "Enable Attachment Uploader", "show_location": "Enable Geolocation Share", - "allowed_upload_size": "Max Upload Size (in bytes)", "allowed_upload_types": "Allowed Upload Mime Types (comma separated)" } diff --git a/api/src/extensions/channels/web/i18n/fr/label.json b/api/src/extensions/channels/web/i18n/fr/label.json index ce5473a5..89590e1c 100644 --- a/api/src/extensions/channels/web/i18n/fr/label.json +++ b/api/src/extensions/channels/web/i18n/fr/label.json @@ -17,6 +17,5 @@ "show_emoji": "Activer le sélecteur d'Emojis", "show_file": "Activer l'upload de fichiers", "show_location": "Activer le partage de géolocalisation", - "allowed_upload_size": "Taille maximale de téléchargement (en octets)", "allowed_upload_types": "Types MIME autorisés pour le téléchargement (séparés par des virgules)" } diff --git a/api/src/extensions/channels/web/settings.ts b/api/src/extensions/channels/web/settings.ts index 0713449d..0fd656cc 100644 --- a/api/src/extensions/channels/web/settings.ts +++ b/api/src/extensions/channels/web/settings.ts @@ -9,8 +9,6 @@ import { ChannelSetting } from '@/channel/types'; import { SettingType } from '@/setting/schemas/types'; -import { Web } from './types'; - export const WEB_CHANNEL_NAME = 'web-channel' as const; export const WEB_CHANNEL_NAMESPACE = 'web_channel'; @@ -18,82 +16,76 @@ export const WEB_CHANNEL_NAMESPACE = 'web_channel'; export default [ { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.allowed_domains, + label: 'allowed_domains', value: 'http://localhost:8080,http://localhost:4000', type: SettingType.text, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.start_button, + label: 'start_button', value: true, type: SettingType.checkbox, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.input_disabled, + label: 'input_disabled', value: false, type: SettingType.checkbox, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.persistent_menu, + label: 'persistent_menu', value: true, type: SettingType.checkbox, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.greeting_message, + label: 'greeting_message', value: 'Welcome! Ready to start a conversation with our chatbot?', type: SettingType.textarea, translatable: true, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.theme_color, + label: 'theme_color', value: 'teal', type: SettingType.select, options: ['teal', 'orange', 'red', 'green', 'blue', 'dark'], }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.window_title, + label: 'window_title', value: 'Widget Title', type: SettingType.text, translatable: true, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.avatar_url, + label: 'avatar_url', value: '', type: SettingType.text, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.show_emoji, + label: 'show_emoji', value: true, type: SettingType.checkbox, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.show_file, + label: 'show_file', value: true, type: SettingType.checkbox, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.show_location, + label: 'show_location', value: true, type: SettingType.checkbox, }, { group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.allowed_upload_size, - value: 2500000, - type: SettingType.number, - }, - { - group: WEB_CHANNEL_NAMESPACE, - label: Web.SettingLabel.allowed_upload_types, + label: 'allowed_upload_types', value: 'audio/mpeg,audio/x-ms-wma,audio/vnd.rn-realaudio,audio/x-wav,image/gif,image/jpeg,image/png,image/tiff,image/vnd.microsoft.icon,image/vnd.djvu,image/svg+xml,text/css,text/csv,text/html,text/plain,text/xml,video/mpeg,video/mp4,video/quicktime,video/x-ms-wmv,video/x-msvideo,video/x-flv,video/web,application/msword,application/vnd.ms-powerpoint,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.tex,application/vnd.oasis.opendocument.spreadsheet,application/vnd.oasis.opendocument.graphics,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.wordprocessingml.document', type: SettingType.textarea, diff --git a/api/src/extensions/channels/web/types.ts b/api/src/extensions/channels/web/types.ts index ec263bf8..e8e0180e 100644 --- a/api/src/extensions/channels/web/types.ts +++ b/api/src/extensions/channels/web/types.ts @@ -12,24 +12,6 @@ import { FileType } from '@/chat/schemas/types/message'; import { StdQuickReply } from '@/chat/schemas/types/quick-reply'; export namespace Web { - export enum SettingLabel { - allowed_domains = 'allowed_domains', - start_button = 'start_button', - input_disabled = 'input_disabled', - persistent_menu = 'persistent_menu', - greeting_message = 'greeting_message', - theme_color = 'theme_color', - window_title = 'window_title', - avatar_url = 'avatar_url', - show_emoji = 'show_emoji', - show_file = 'show_file', - show_location = 'show_location', - allowed_upload_size = 'allowed_upload_size', - allowed_upload_types = 'allowed_upload_types', - } - - export type Settings = Record; - export type RequestSession = { web?: { profile: SubscriberFull; diff --git a/docker/.env.example b/docker/.env.example index 834b3e17..aeb135a1 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -16,7 +16,7 @@ SESSION_SECRET=f661ff500fff6b0c8f91310b6fff6b0c SESSION_NAME=s.id UPLOAD_DIR=/uploads AVATAR_DIR=/avatars -UPLOAD_MAX_SIZE_IN_BYTES=2000000 +UPLOAD_MAX_SIZE_IN_BYTES=20971520 INVITATION_JWT_SECRET=dev_only INVITATION_EXPIRES_IN=24h PASSWORD_RESET_JWT_SECRET=dev_only diff --git a/frontend/src/pages/api/config.ts b/frontend/src/pages/api/config.ts index ce57a910..fe3653c5 100644 --- a/frontend/src/pages/api/config.ts +++ b/frontend/src/pages/api/config.ts @@ -23,6 +23,6 @@ export default function handler( ssoEnabled: process.env.NEXT_PUBLIC_SSO_ENABLED === "true" || false, maxUploadSize: process.env.UPLOAD_MAX_SIZE_IN_BYTES ? Number(process.env.UPLOAD_MAX_SIZE_IN_BYTES) - : 50 * 1024 * 1024, // 50 MB in bytes + : 20 * 1024 * 1024, // 20 MB in bytes }); } diff --git a/widget/src/ChatWidget.tsx b/widget/src/ChatWidget.tsx index df53bea0..9c7966c8 100644 --- a/widget/src/ChatWidget.tsx +++ b/widget/src/ChatWidget.tsx @@ -6,20 +6,21 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ +import "normalize.css"; +import "./ChatWidget.css"; import Launcher from "./components/Launcher"; import UserSubscription from "./components/UserSubscription"; import ChatProvider from "./providers/ChatProvider"; import { ColorProvider } from "./providers/ColorProvider"; -import { Config, ConfigProvider } from "./providers/ConfigProvider"; +import { ConfigProvider } from "./providers/ConfigProvider"; import { CookieProvider } from "./providers/CookieProvider"; import { SettingsProvider } from "./providers/SettingsProvider"; import { SocketProvider } from "./providers/SocketProvider"; import { TranslationProvider } from "./providers/TranslationProvider"; import WidgetProvider from "./providers/WidgetProvider"; -import "normalize.css"; -import "./ChatWidget.css"; +import { Config } from "./types/config.types"; -function ChatWidget(props: Config) { +function ChatWidget(props: Partial) { return ( diff --git a/widget/src/UiChatWidget.tsx b/widget/src/UiChatWidget.tsx index ee729e9d..96dc5de4 100644 --- a/widget/src/UiChatWidget.tsx +++ b/widget/src/UiChatWidget.tsx @@ -12,13 +12,14 @@ import Launcher from "./components/Launcher"; import UserSubscription from "./components/UserSubscription"; import ChatProvider from "./providers/ChatProvider"; import { ColorProvider } from "./providers/ColorProvider"; -import { Config, ConfigProvider } from "./providers/ConfigProvider"; +import { ConfigProvider } from "./providers/ConfigProvider"; import { SettingsProvider } from "./providers/SettingsProvider"; import { SocketProvider } from "./providers/SocketProvider"; import { TranslationProvider } from "./providers/TranslationProvider"; import WidgetProvider, { WidgetContextType } from "./providers/WidgetProvider"; -import "./UiChatWidget.css"; +import { Config } from "./types/config.types"; import { ConnectionState } from "./types/state.types"; +import "./UiChatWidget.css"; type UiChatWidgetProps = PropsWithChildren<{ CustomLauncher?: (props: { widget: WidgetContextType }) => JSX.Element; @@ -26,7 +27,7 @@ type UiChatWidgetProps = PropsWithChildren<{ CustomAvatar?: () => JSX.Element; PreChat?: React.FC; PostChat?: React.FC; - config: Config; + config: Partial; }>; function UiChatWidget({ diff --git a/widget/src/components/UserInput.tsx b/widget/src/components/UserInput.tsx index 84416a52..47957c6f 100644 --- a/widget/src/components/UserInput.tsx +++ b/widget/src/components/UserInput.tsx @@ -11,6 +11,7 @@ import React, { useEffect, useRef, useState } from "react"; import { useTranslation } from "../hooks/useTranslation"; import { useChat } from "../providers/ChatProvider"; import { useColors } from "../providers/ColorProvider"; +import { useConfig } from "../providers/ConfigProvider"; import { useSettings } from "../providers/SettingsProvider"; import { TOutgoingMessageType } from "../types/message.types"; import { OutgoingMessageState } from "../types/state.types"; @@ -27,6 +28,7 @@ import Suggestions from "./Suggestions"; import "./UserInput.scss"; const UserInput: React.FC = () => { + const { maxUploadSize } = useConfig(); const { t } = useTranslation(); const { colors } = useColors(); const { @@ -43,7 +45,6 @@ const UserInput: React.FC = () => { focusOnOpen, autoFlush, allowedUploadTypes, - allowedUploadSize, showEmoji, showLocation, showFile, @@ -106,7 +107,7 @@ const UserInput: React.FC = () => { if (!typeCheck) { setFileError(t("messages.file_message.unsupported_file_type")); - } else if (file.size > (allowedUploadSize || 0)) { + } else if (file.size > maxUploadSize) { setFileError(t("messages.file_message.unsupported_file_size")); } else { send({ diff --git a/widget/src/constants/defaultConfig.ts b/widget/src/constants/defaultConfig.ts index 4ee2697f..b1b58050 100644 --- a/widget/src/constants/defaultConfig.ts +++ b/widget/src/constants/defaultConfig.ts @@ -6,8 +6,13 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -export const DEFAULT_CONFIG = { +import { Config } from "../types/config.types"; + +export const DEFAULT_CONFIG: Config = { apiUrl: process.env.REACT_APP_WIDGET_API_URL || "http://localhost:4000", channel: process.env.REACT_APP_WIDGET_CHANNEL || "console-channel", language: "en", + maxUploadSize: process.env.UPLOAD_MAX_SIZE_IN_BYTES + ? Number(process.env.UPLOAD_MAX_SIZE_IN_BYTES) + : 20 * 1024 * 1024, // 20 MB in bytes }; diff --git a/widget/src/providers/ConfigProvider.tsx b/widget/src/providers/ConfigProvider.tsx index 783c9b70..7058087b 100644 --- a/widget/src/providers/ConfigProvider.tsx +++ b/widget/src/providers/ConfigProvider.tsx @@ -9,23 +9,15 @@ import React, { createContext, ReactNode, useContext, useRef } from "react"; import { DEFAULT_CONFIG } from "../constants/defaultConfig"; +import { Config } from "../types/config.types"; -// Define the type for your config, including all possible properties -export type Config = { - apiUrl: string; - channel: string; - language: string; -}; - -// Create a context with a specific type, providing better type-checking const ConfigContext = createContext(DEFAULT_CONFIG); -export const ConfigProvider: React.FC<{ - apiUrl?: string; - channel?: string; - language?: string; - children: ReactNode; -}> = ({ children, ...providedConfig }) => { +export const ConfigProvider: React.FC< + Partial & { + children: ReactNode; + } +> = ({ children, ...providedConfig }) => { const config = useRef({ ...DEFAULT_CONFIG, ...providedConfig, diff --git a/widget/src/providers/SettingsProvider.tsx b/widget/src/providers/SettingsProvider.tsx index 72248413..666398c8 100644 --- a/widget/src/providers/SettingsProvider.tsx +++ b/widget/src/providers/SettingsProvider.tsx @@ -35,7 +35,6 @@ type ChannelSettings = { show_location: boolean; allowed_upload_types: string; greeting_message: string; - allowed_upload_size: number; }; type ChatSettings = { @@ -52,7 +51,6 @@ type ChatSettings = { menu: IMenuNode[]; autoFlush: boolean; allowedUploadTypes: string[]; - allowedUploadSize: number; color: string; greetingMessage: string; avatarUrl: string; @@ -72,7 +70,6 @@ const defaultSettings: ChatSettings = { menu: [], autoFlush: true, allowedUploadTypes: ["image/gif", "image/png", "image/jpeg"], - allowedUploadSize: 2500000, color: "blue", greetingMessage: "Welcome !", avatarUrl: "", @@ -106,7 +103,6 @@ export const SettingsProvider: React.FC = ({ titleImageUrl: settings.avatar_url, menu: settings.menu, allowedUploadTypes: settings.allowed_upload_types.split(","), - allowedUploadSize: settings.allowed_upload_size, inputDisabled: settings.input_disabled, color: settings.theme_color, greetingMessage: settings.greeting_message, diff --git a/widget/src/types/config.types.ts b/widget/src/types/config.types.ts new file mode 100644 index 00000000..694a9442 --- /dev/null +++ b/widget/src/types/config.types.ts @@ -0,0 +1,14 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +export type Config = { + apiUrl: string; + channel: string; + language: string; + maxUploadSize: number; +}; diff --git a/widget/src/utils/SocketIoClient.ts b/widget/src/utils/SocketIoClient.ts index a260f0ab..538754b6 100644 --- a/widget/src/utils/SocketIoClient.ts +++ b/widget/src/utils/SocketIoClient.ts @@ -8,7 +8,7 @@ import { io, ManagerOptions, Socket, SocketOptions } from "socket.io-client"; -import { Config } from "../providers/ConfigProvider"; +import { Config } from "../types/config.types"; import { IOIncomingMessage, IOOutgoingMessage,