diff --git a/apps/web/i18n.lock b/apps/web/i18n.lock index 18f3035f160f..9e6b861daf13 100644 --- a/apps/web/i18n.lock +++ b/apps/web/i18n.lock @@ -2036,12 +2036,12 @@ checksums: environments/workspace/look/advanced_styling_field_headline_size_description: 13debc3855e4edae992c7a1ebff599c3 environments/workspace/look/advanced_styling_field_headline_weight: 0c8b8262945c61f8e2978502362e0a42 environments/workspace/look/advanced_styling_field_headline_weight_description: 1a9c40bd76ff5098b1e48b1d3893171b - environments/workspace/look/advanced_styling_field_height: f4da6d7ecd26e3fa75cfea03abb60c00 + environments/workspace/look/advanced_styling_field_height: 40ca2224bb2936ad1329091b35a9ffe2 environments/workspace/look/advanced_styling_field_indicator_bg: 00febda2901af0f1b0c17e44f9917c38 environments/workspace/look/advanced_styling_field_indicator_bg_description: 7eb3b54a8b331354ec95c0dc1545c620 environments/workspace/look/advanced_styling_field_input_border_radius_description: 0007f1bb572b35d9a3720daeb7a55617 environments/workspace/look/advanced_styling_field_input_font_size_description: 5311f95dcbd083623e35c98ea5374c3b - environments/workspace/look/advanced_styling_field_input_height_description: b704fc67e805223992c811d6f86a9c00 + environments/workspace/look/advanced_styling_field_input_height_description: e19ec0dc432478def0fd1199ad765e38 environments/workspace/look/advanced_styling_field_input_padding_x_description: 10e14296468321c13fda77fd1ba58dfd environments/workspace/look/advanced_styling_field_input_padding_y_description: 98b4aeff2940516d05ea61bdc1211d0d environments/workspace/look/advanced_styling_field_input_placeholder_opacity_description: f55a6700884d24014404e58876121ddf diff --git a/apps/web/lib/styling/constants.ts b/apps/web/lib/styling/constants.ts index 37dcb81989a9..e6b10a71f288 100644 --- a/apps/web/lib/styling/constants.ts +++ b/apps/web/lib/styling/constants.ts @@ -118,10 +118,10 @@ export const STYLE_DEFAULTS: TProjectStyling = { // Inputs inputTextColor: { light: _colors["inputTextColor.light"] }, inputBorderRadius: 8, - inputHeight: 40, + inputHeight: 20, inputFontSize: 14, - inputPaddingX: 16, - inputPaddingY: 16, + inputPaddingX: 8, + inputPaddingY: 8, inputPlaceholderOpacity: 0.5, inputShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", diff --git a/apps/web/locales/de-DE.json b/apps/web/locales/de-DE.json index 9a3083283244..dee08ed3cc60 100644 --- a/apps/web/locales/de-DE.json +++ b/apps/web/locales/de-DE.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Skaliert den Überschriftentext.", "advanced_styling_field_headline_weight": "Schriftstärke der Überschrift", "advanced_styling_field_headline_weight_description": "Macht den Überschriftentext heller oder fetter.", - "advanced_styling_field_height": "Höhe", + "advanced_styling_field_height": "Mindesthöhe", "advanced_styling_field_indicator_bg": "Indikator-Hintergrund", "advanced_styling_field_indicator_bg_description": "Färbt den gefüllten Teil des Balkens.", "advanced_styling_field_input_border_radius_description": "Rundet die Eingabeecken ab.", "advanced_styling_field_input_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.", - "advanced_styling_field_input_height_description": "Steuert die Höhe des Eingabefelds.", + "advanced_styling_field_input_height_description": "Legt die Mindesthöhe des Eingabefelds fest.", "advanced_styling_field_input_padding_x_description": "Fügt links und rechts Abstand hinzu.", "advanced_styling_field_input_padding_y_description": "Fügt oben und unten Abstand hinzu.", "advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.", diff --git a/apps/web/locales/en-US.json b/apps/web/locales/en-US.json index fb6250156b8a..a0251ae59edd 100644 --- a/apps/web/locales/en-US.json +++ b/apps/web/locales/en-US.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Scales the headline text.", "advanced_styling_field_headline_weight": "Headline Font Weight", "advanced_styling_field_headline_weight_description": "Makes headline text lighter or bolder.", - "advanced_styling_field_height": "Height", + "advanced_styling_field_height": "Minimum Height", "advanced_styling_field_indicator_bg": "Indicator Background", "advanced_styling_field_indicator_bg_description": "Colors the filled portion of the bar.", "advanced_styling_field_input_border_radius_description": "Rounds the input corners.", "advanced_styling_field_input_font_size_description": "Scales the typed text in inputs.", - "advanced_styling_field_input_height_description": "Controls the input field height.", + "advanced_styling_field_input_height_description": "Controls the minimum height of the input field.", "advanced_styling_field_input_padding_x_description": "Adds space on the left and right.", "advanced_styling_field_input_padding_y_description": "Adds space on the top and bottom.", "advanced_styling_field_input_placeholder_opacity_description": "Fades the placeholder hint text.", diff --git a/apps/web/locales/es-ES.json b/apps/web/locales/es-ES.json index 523bf17b0217..bbb623278f8d 100644 --- a/apps/web/locales/es-ES.json +++ b/apps/web/locales/es-ES.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Escala el texto del titular.", "advanced_styling_field_headline_weight": "Grosor de fuente del titular", "advanced_styling_field_headline_weight_description": "Hace el texto del titular más ligero o más grueso.", - "advanced_styling_field_height": "Altura", + "advanced_styling_field_height": "Altura mínima", "advanced_styling_field_indicator_bg": "Fondo del indicador", "advanced_styling_field_indicator_bg_description": "Colorea la porción rellena de la barra.", "advanced_styling_field_input_border_radius_description": "Redondea las esquinas del campo.", "advanced_styling_field_input_font_size_description": "Escala el texto escrito en los campos.", - "advanced_styling_field_input_height_description": "Controla la altura del campo de entrada.", + "advanced_styling_field_input_height_description": "Controla la altura mínima del campo de entrada.", "advanced_styling_field_input_padding_x_description": "Añade espacio a la izquierda y a la derecha.", "advanced_styling_field_input_padding_y_description": "Añade espacio en la parte superior e inferior.", "advanced_styling_field_input_placeholder_opacity_description": "Atenúa el texto de sugerencia del marcador de posición.", diff --git a/apps/web/locales/fr-FR.json b/apps/web/locales/fr-FR.json index 38cadb6e9367..cb969de2b24b 100644 --- a/apps/web/locales/fr-FR.json +++ b/apps/web/locales/fr-FR.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Ajuste la taille du texte du titre.", "advanced_styling_field_headline_weight": "Graisse de police du titre", "advanced_styling_field_headline_weight_description": "Rend le texte du titre plus léger ou plus gras.", - "advanced_styling_field_height": "Hauteur", + "advanced_styling_field_height": "Hauteur minimale", "advanced_styling_field_indicator_bg": "Arrière-plan de l'indicateur", "advanced_styling_field_indicator_bg_description": "Colore la partie remplie de la barre.", "advanced_styling_field_input_border_radius_description": "Arrondit les coins du champ de saisie.", "advanced_styling_field_input_font_size_description": "Ajuste la taille du texte saisi dans les champs.", - "advanced_styling_field_input_height_description": "Contrôle la hauteur du champ de saisie.", + "advanced_styling_field_input_height_description": "Contrôle la hauteur minimale du champ de saisie.", "advanced_styling_field_input_padding_x_description": "Ajoute de l'espace à gauche et à droite.", "advanced_styling_field_input_padding_y_description": "Ajoute de l'espace en haut et en bas.", "advanced_styling_field_input_placeholder_opacity_description": "Atténue le texte d'indication du placeholder.", diff --git a/apps/web/locales/hu-HU.json b/apps/web/locales/hu-HU.json index 9fa18f822d15..15ad9deb5b3c 100644 --- a/apps/web/locales/hu-HU.json +++ b/apps/web/locales/hu-HU.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Átméretezi a címsor szövegét.", "advanced_styling_field_headline_weight": "Címsor betűvastagsága", "advanced_styling_field_headline_weight_description": "Vékonyabbá vagy vastagabbá teszi a címsor szövegét.", - "advanced_styling_field_height": "Magasság", + "advanced_styling_field_height": "Minimális magasság", "advanced_styling_field_indicator_bg": "Jelző háttere", "advanced_styling_field_indicator_bg_description": "Kiszínezi a sáv kitöltött részét.", "advanced_styling_field_input_border_radius_description": "Lekerekíti a beviteli mező sarkait.", "advanced_styling_field_input_font_size_description": "Átméretezi a beviteli mezőkbe beírt szöveget.", - "advanced_styling_field_input_height_description": "A beviteli mező magasságát vezérli.", + "advanced_styling_field_input_height_description": "A beviteli mező minimális magasságát szabályozza.", "advanced_styling_field_input_padding_x_description": "Térközt ad hozzá balra és jobbra.", "advanced_styling_field_input_padding_y_description": "Térközt ad hozzá fent és lent.", "advanced_styling_field_input_placeholder_opacity_description": "Elhalványítja a helykitöltő súgószöveget.", diff --git a/apps/web/locales/ja-JP.json b/apps/web/locales/ja-JP.json index c5f83af67ca7..5972a3d8e3f8 100644 --- a/apps/web/locales/ja-JP.json +++ b/apps/web/locales/ja-JP.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "見出しテキストのサイズを調整します。", "advanced_styling_field_headline_weight": "見出しのフォントの太さ", "advanced_styling_field_headline_weight_description": "見出しテキストを細くまたは太くします。", - "advanced_styling_field_height": "高さ", + "advanced_styling_field_height": "最小の高さ", "advanced_styling_field_indicator_bg": "インジケーターの背景", "advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。", "advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。", "advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。", - "advanced_styling_field_input_height_description": "入力フィールドの高さを調整します。", + "advanced_styling_field_input_height_description": "入力フィールドの最小の高さを制御します。", "advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。", "advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。", "advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。", diff --git a/apps/web/locales/nl-NL.json b/apps/web/locales/nl-NL.json index de434eb92d10..b4fc6acceae5 100644 --- a/apps/web/locales/nl-NL.json +++ b/apps/web/locales/nl-NL.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Schaalt de koptekst.", "advanced_styling_field_headline_weight": "Letterdikte kop", "advanced_styling_field_headline_weight_description": "Maakt koptekst lichter of vetter.", - "advanced_styling_field_height": "Hoogte", + "advanced_styling_field_height": "Minimale hoogte", "advanced_styling_field_indicator_bg": "Indicatorachtergrond", "advanced_styling_field_indicator_bg_description": "Kleurt het gevulde deel van de balk.", "advanced_styling_field_input_border_radius_description": "Rondt de invoerhoeken af.", "advanced_styling_field_input_font_size_description": "Schaalt de getypte tekst in invoervelden.", - "advanced_styling_field_input_height_description": "Bepaalt de hoogte van het invoerveld.", + "advanced_styling_field_input_height_description": "Bepaalt de minimale hoogte van het invoerveld.", "advanced_styling_field_input_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.", "advanced_styling_field_input_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.", "advanced_styling_field_input_placeholder_opacity_description": "Vervaagt de tijdelijke aanwijzingstekst.", diff --git a/apps/web/locales/pt-BR.json b/apps/web/locales/pt-BR.json index 91483066a4b2..235b4fa0c5a7 100644 --- a/apps/web/locales/pt-BR.json +++ b/apps/web/locales/pt-BR.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Ajusta o tamanho do texto do título.", "advanced_styling_field_headline_weight": "Peso da fonte do título", "advanced_styling_field_headline_weight_description": "Torna o texto do título mais leve ou mais negrito.", - "advanced_styling_field_height": "Altura", + "advanced_styling_field_height": "Altura mínima", "advanced_styling_field_indicator_bg": "Fundo do indicador", "advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.", "advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.", "advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.", - "advanced_styling_field_input_height_description": "Controla a altura do campo de entrada.", + "advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.", "advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.", "advanced_styling_field_input_padding_y_description": "Adiciona espaço na parte superior e inferior.", "advanced_styling_field_input_placeholder_opacity_description": "Esmaece o texto de dica do placeholder.", diff --git a/apps/web/locales/pt-PT.json b/apps/web/locales/pt-PT.json index cb475dc375d8..c09acd233f24 100644 --- a/apps/web/locales/pt-PT.json +++ b/apps/web/locales/pt-PT.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Ajusta o tamanho do texto do título.", "advanced_styling_field_headline_weight": "Peso da fonte do título", "advanced_styling_field_headline_weight_description": "Torna o texto do título mais leve ou mais negrito.", - "advanced_styling_field_height": "Altura", + "advanced_styling_field_height": "Altura mínima", "advanced_styling_field_indicator_bg": "Fundo do indicador", "advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.", "advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.", "advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.", - "advanced_styling_field_input_height_description": "Controla a altura do campo de entrada.", + "advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.", "advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.", "advanced_styling_field_input_padding_y_description": "Adiciona espaço no topo e na base.", "advanced_styling_field_input_placeholder_opacity_description": "Atenua o texto de sugestão do placeholder.", diff --git a/apps/web/locales/ro-RO.json b/apps/web/locales/ro-RO.json index 51e979d261f2..ad8f4ba384ad 100644 --- a/apps/web/locales/ro-RO.json +++ b/apps/web/locales/ro-RO.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Scalează textul titlului.", "advanced_styling_field_headline_weight": "Grosime font titlu", "advanced_styling_field_headline_weight_description": "Face textul titlului mai subțire sau mai îngroșat.", - "advanced_styling_field_height": "Înălțime", + "advanced_styling_field_height": "Înălțime minimă", "advanced_styling_field_indicator_bg": "Fundal indicator", "advanced_styling_field_indicator_bg_description": "Colorează partea umplută a barei.", "advanced_styling_field_input_border_radius_description": "Rotunjește colțurile câmpurilor de introducere.", "advanced_styling_field_input_font_size_description": "Scalează textul introdus în câmpuri.", - "advanced_styling_field_input_height_description": "Controlează înălțimea câmpului de introducere.", + "advanced_styling_field_input_height_description": "Controlează înălțimea minimă a câmpului de introducere.", "advanced_styling_field_input_padding_x_description": "Adaugă spațiu la stânga și la dreapta.", "advanced_styling_field_input_padding_y_description": "Adaugă spațiu deasupra și dedesubt.", "advanced_styling_field_input_placeholder_opacity_description": "Estompează textul de sugestie din placeholder.", diff --git a/apps/web/locales/ru-RU.json b/apps/web/locales/ru-RU.json index 6640bd6e8b99..2ddcb2cf8657 100644 --- a/apps/web/locales/ru-RU.json +++ b/apps/web/locales/ru-RU.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Масштабирует текст заголовка.", "advanced_styling_field_headline_weight": "Толщина шрифта заголовка", "advanced_styling_field_headline_weight_description": "Делает текст заголовка тоньше или жирнее.", - "advanced_styling_field_height": "Высота", + "advanced_styling_field_height": "Минимальная высота", "advanced_styling_field_indicator_bg": "Фон индикатора", "advanced_styling_field_indicator_bg_description": "Задаёт цвет заполненной части полосы.", "advanced_styling_field_input_border_radius_description": "Скругляет углы полей ввода.", "advanced_styling_field_input_font_size_description": "Масштабирует введённый текст в полях ввода.", - "advanced_styling_field_input_height_description": "Определяет высоту поля ввода.", + "advanced_styling_field_input_height_description": "Определяет минимальную высоту поля ввода.", "advanced_styling_field_input_padding_x_description": "Добавляет отступы слева и справа.", "advanced_styling_field_input_padding_y_description": "Добавляет пространство сверху и снизу.", "advanced_styling_field_input_placeholder_opacity_description": "Делает текст подсказки менее заметным.", diff --git a/apps/web/locales/sv-SE.json b/apps/web/locales/sv-SE.json index 519b345fbe89..0c29b1aa9ff4 100644 --- a/apps/web/locales/sv-SE.json +++ b/apps/web/locales/sv-SE.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "Ändrar storleken på rubriken.", "advanced_styling_field_headline_weight": "Rubrikens teckentjocklek", "advanced_styling_field_headline_weight_description": "Gör rubriktexten tunnare eller fetare.", - "advanced_styling_field_height": "Höjd", + "advanced_styling_field_height": "Minsta höjd", "advanced_styling_field_indicator_bg": "Indikatorns bakgrund", "advanced_styling_field_indicator_bg_description": "Färglägger den fyllda delen av stapeln.", "advanced_styling_field_input_border_radius_description": "Rundar av hörnen på inmatningsfält.", "advanced_styling_field_input_font_size_description": "Ändrar storleken på texten i inmatningsfält.", - "advanced_styling_field_input_height_description": "Styr höjden på inmatningsfältet.", + "advanced_styling_field_input_height_description": "Styr den minsta höjden på inmatningsfältet.", "advanced_styling_field_input_padding_x_description": "Lägger till utrymme till vänster och höger.", "advanced_styling_field_input_padding_y_description": "Lägger till utrymme upptill och nedtill.", "advanced_styling_field_input_placeholder_opacity_description": "Tonar ut platshållartexten.", diff --git a/apps/web/locales/zh-Hans-CN.json b/apps/web/locales/zh-Hans-CN.json index 2469d5819ec9..7b4ff2f65ffd 100644 --- a/apps/web/locales/zh-Hans-CN.json +++ b/apps/web/locales/zh-Hans-CN.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "调整主标题文字大小。", "advanced_styling_field_headline_weight": "标题字体粗细", "advanced_styling_field_headline_weight_description": "设置主标题文字的粗细。", - "advanced_styling_field_height": "高度", + "advanced_styling_field_height": "最小高度", "advanced_styling_field_indicator_bg": "指示器背景", "advanced_styling_field_indicator_bg_description": "设置进度条已填充部分的颜色。", "advanced_styling_field_input_border_radius_description": "设置输入框圆角。", "advanced_styling_field_input_font_size_description": "调整输入框内文字大小。", - "advanced_styling_field_input_height_description": "控制输入框高度。", + "advanced_styling_field_input_height_description": "设置输入框的最小高度。", "advanced_styling_field_input_padding_x_description": "增加输入框左右间距。", "advanced_styling_field_input_padding_y_description": "为输入框上下添加间距。", "advanced_styling_field_input_placeholder_opacity_description": "调整占位提示文字的透明度。", diff --git a/apps/web/locales/zh-Hant-TW.json b/apps/web/locales/zh-Hant-TW.json index 07d3db7264b3..7893c8f09396 100644 --- a/apps/web/locales/zh-Hant-TW.json +++ b/apps/web/locales/zh-Hant-TW.json @@ -2153,12 +2153,12 @@ "advanced_styling_field_headline_size_description": "調整標題文字的大小。", "advanced_styling_field_headline_weight": "標題字體粗細", "advanced_styling_field_headline_weight_description": "讓標題文字變細或變粗。", - "advanced_styling_field_height": "高度", + "advanced_styling_field_height": "最小高度", "advanced_styling_field_indicator_bg": "指示器背景", "advanced_styling_field_indicator_bg_description": "設定進度條已填滿部分的顏色。", "advanced_styling_field_input_border_radius_description": "調整輸入框的圓角。", "advanced_styling_field_input_font_size_description": "調整輸入框內輸入文字的大小。", - "advanced_styling_field_input_height_description": "調整輸入欄位的高度。", + "advanced_styling_field_input_height_description": "設定輸入欄位的最小高度。", "advanced_styling_field_input_padding_x_description": "在左右兩側增加間距。", "advanced_styling_field_input_padding_y_description": "在上方和下方增加間距。", "advanced_styling_field_input_placeholder_opacity_description": "讓提示文字變得更淡。", diff --git a/apps/web/modules/core/rate-limit/rate-limit-configs.ts b/apps/web/modules/core/rate-limit/rate-limit-configs.ts index 1b3a9203465c..d729d0dcea51 100644 --- a/apps/web/modules/core/rate-limit/rate-limit-configs.ts +++ b/apps/web/modules/core/rate-limit/rate-limit-configs.ts @@ -30,4 +30,4 @@ export const rateLimitConfigs = { upload: { interval: 60, allowedPerInterval: 5, namespace: "storage:upload" }, // 5 per minute delete: { interval: 60, allowedPerInterval: 5, namespace: "storage:delete" }, // 5 per minute }, -}; +} as const; diff --git a/apps/web/modules/ui/components/survey/index.tsx b/apps/web/modules/ui/components/survey/index.tsx index e3bc83e24711..1b6cc386d635 100644 --- a/apps/web/modules/ui/components/survey/index.tsx +++ b/apps/web/modules/ui/components/survey/index.tsx @@ -40,7 +40,10 @@ export const SurveyInline = (props: Omit) = isLoadingScript = true; try { const scriptUrl = props.appUrl ? `${props.appUrl}/js/surveys.umd.cjs` : "/js/surveys.umd.cjs"; - const response = await fetch(scriptUrl); + const response = await fetch( + scriptUrl, + process.env.NODE_ENV === "development" ? { cache: "no-store" } : {} + ); if (!response.ok) { throw new Error("Failed to load the surveys package"); diff --git a/apps/web/playwright/survey-styling.spec.ts b/apps/web/playwright/survey-styling.spec.ts index 68508d1486df..1d0c42217151 100644 --- a/apps/web/playwright/survey-styling.spec.ts +++ b/apps/web/playwright/survey-styling.spec.ts @@ -96,6 +96,7 @@ test.describe("Survey Styling", async () => { expect(css).toContain("--fb-input-background-color: #eeeeee"); expect(css).toContain("--fb-input-border-color: #cccccc"); expect(css).toContain("--fb-input-text-color: #024eff"); + expect(css).toContain("--fb-input-placeholder-color:"); expect(css).toContain("--fb-input-border-radius: 5px"); expect(css).toContain("--fb-input-height: 50px"); expect(css).toContain("--fb-input-font-size: 16px"); diff --git a/docs/self-hosting/advanced/rate-limiting.mdx b/docs/self-hosting/advanced/rate-limiting.mdx index 39631ebb1517..88e94d8ac9c5 100644 --- a/docs/self-hosting/advanced/rate-limiting.mdx +++ b/docs/self-hosting/advanced/rate-limiting.mdx @@ -1,41 +1,94 @@ --- title: "Rate Limiting" -description: "Rate limiting for Formbricks" +description: "Current request rate limits in Formbricks" icon: "timer" --- -To protect the platform from abuse and ensure fair usage, rate limiting is enforced by default on an IP-address basis. If a client exceeds the allowed number of requests within the specified time window, the API will return a `429 Too Many Requests` status code. +Formbricks applies request rate limits to protect against abuse and keep API usage fair. -## Default Rate Limits +Rate limits are scoped by identifier, depending on the endpoint: -The following rate limits apply to various endpoints: +- IP hash (for unauthenticated/client-side routes and public actions) +- API key ID (for authenticated API calls) +- User ID (for authenticated session-based calls and server actions) +- Organization ID (for follow-up email dispatch) -| **Endpoint** | **Rate Limit** | **Time Window** | -| ----------------------- | -------------- | --------------- | -| `POST /login` | 30 requests | 15 minutes | -| `POST /signup` | 30 requests | 60 minutes | -| `POST /verify-email` | 10 requests | 60 minutes | -| `POST /forgot-password` | 5 requests | 60 minutes | -| `GET /client-side-api` | 100 requests | 1 minute | -| `POST /share` | 100 requests | 60 minutes | +When a limit is exceeded, the API returns `429 Too Many Requests`. -If a request exceeds the defined rate limit, the server will respond with: +## Management API Rate Limits + +These are the current limits for Management APIs: + +| **Route Group** | **Limit** | **Window** | **Identifier** | +| --- | --- | --- | --- | +| `/api/v1/management/*` (except `/api/v1/management/storage`), `/api/v1/webhooks/*`, `/api/v1/integrations/*`, `/api/v1/management/me` | 100 requests | 1 minute | API key ID or session user ID | +| `/api/v2/management/*` (and other v2 authenticated routes that use `authenticatedApiClient`) | 100 requests | 1 minute | API key ID | +| `POST /api/v1/management/storage` | 5 requests | 1 minute | API key ID or session user ID | + +## All Enforced Limits + +| **Config** | **Limit** | **Window** | **Identifier** | **Used For** | +| --- | --- | --- | --- | --- | +| `auth.login` | 10 requests | 15 minutes | IP hash | Email/password login flow (`/api/auth/callback/credentials`) | +| `auth.signup` | 30 requests | 60 minutes | IP hash | Signup server action | +| `auth.forgotPassword` | 5 requests | 60 minutes | IP hash | Forgot password server action | +| `auth.verifyEmail` | 10 requests | 60 minutes | IP hash | Email verification callback + resend verification action | +| `api.v1` | 100 requests | 1 minute | API key ID or session user ID | v1 management, webhooks, integrations, and `/api/v1/management/me` | +| `api.v2` | 100 requests | 1 minute | API key ID | v2 authenticated API wrapper (`authenticatedApiClient`) | +| `api.client` | 100 requests | 1 minute | IP hash | v1 client API routes (except `/api/v1/client/og` and storage upload override), plus v2 routes that re-use those v1 handlers | +| `storage.upload` | 5 requests | 1 minute | IP hash or authenticated ID | Client storage upload and management storage upload | +| `storage.delete` | 5 requests | 1 minute | API key ID or session user ID | `DELETE /storage/[environmentId]/[accessType]/[fileName]` | +| `actions.emailUpdate` | 3 requests | 60 minutes | User ID | Profile email update action | +| `actions.surveyFollowUp` | 50 requests | 60 minutes | Organization ID | Survey follow-up email processing | +| `actions.sendLinkSurveyEmail` | 10 requests | 60 minutes | IP hash | Link survey email send action | +| `actions.licenseRecheck` | 5 requests | 1 minute | User ID | Enterprise license recheck action | + +## Current Endpoint Exceptions + +The following routes are currently not rate-limited by the server-side limiter: + +- `GET /api/v1/client/og` (explicitly excluded) +- `POST /api/v2/client/[environmentId]/responses` +- `POST /api/v2/client/[environmentId]/displays` +- `GET /api/v2/health` + +## 429 Response Shape + +v1-style endpoints return: ```json { - "code": 429, - "error": "Too many requests, Please try after a while!" + "code": "too_many_requests", + "message": "Maximum number of requests reached. Please try again later.", + "details": {} +} +``` + +v2-style endpoints return: + +```json +{ + "error": { + "code": 429, + "message": "Too Many Requests" + } } ``` ## Disabling Rate Limiting -For self-hosters, rate limiting can be disabled if necessary. However, we **strongly recommend keeping rate limiting enabled in production environments** to prevent abuse. +For self-hosters, rate limiting can be disabled if necessary. We strongly recommend keeping it enabled in production. -To disable rate limiting, set the following environment variable: +Set: ```bash RATE_LIMITING_DISABLED=1 ``` -After making this change, restart your server to apply the new setting. \ No newline at end of file +After changing this value, restart the server. + +## Operational Notes + +- Redis/Valkey is required for robust rate limiting (`REDIS_URL`). +- If Redis is unavailable at runtime, rate-limiter checks currently fail open (requests are allowed through without enforcement). +- Authentication failure audit logging uses a separate throttle (`shouldLogAuthFailure()`) and is intentionally **fail-closed**: when Redis is unavailable or errors occur, audit log entries are **skipped entirely** rather than written without throttle control. This prevents spam while preserving the hash-integrity chain required for compliance. In other words, if Redis is down, no authentication-failure audit logs will be recorded—requests themselves are still allowed (fail-open rate limiting above), but the audit trail for those failures will not be written. diff --git a/packages/survey-ui/src/styles/globals.css b/packages/survey-ui/src/styles/globals.css index 71ad9e1b8868..5c732355604b 100644 --- a/packages/survey-ui/src/styles/globals.css +++ b/packages/survey-ui/src/styles/globals.css @@ -127,12 +127,12 @@ --fb-input-font-size: 14px; --fb-input-font-weight: 400; --fb-input-color: #414b5a; - --fb-input-placeholder-color: var(--fb-input-color); + --fb-input-placeholder-color: var(--fb-input-text-color, var(--fb-input-color)); --fb-input-placeholder-opacity: 0.5; --fb-input-width: 100%; - --fb-input-height: 40px; - --fb-input-padding-x: 16px; - --fb-input-padding-y: 16px; + --fb-input-height: 20px; + --fb-input-padding-x: 8px; + --fb-input-padding-y: 8px; --fb-input-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* ── Progress Bar ──────────────────────────────────────────────────── */ @@ -244,4 +244,4 @@ #fbjs textarea::-webkit-scrollbar-thumb:hover { background-color: hsl(215.4 16.3% 46.9% / 0.5); -} +} \ No newline at end of file diff --git a/packages/surveys/src/lib/styles.test.ts b/packages/surveys/src/lib/styles.test.ts index d4955f45d917..4c6092a31088 100644 --- a/packages/surveys/src/lib/styles.test.ts +++ b/packages/surveys/src/lib/styles.test.ts @@ -443,6 +443,39 @@ describe("addCustomThemeToDom", () => { expect(variables["--fb-button-font-size"]).toBe("1.5rem"); }); + test("should derive input-placeholder-color from inputTextColor when set", () => { + const styling: TSurveyStyling = { + ...getBaseProjectStyling(), + questionColor: { light: "#AABBCC" }, + inputTextColor: { light: "#112233" }, + }; + addCustomThemeToDom({ styling }); + const styleElement = document.getElementById("formbricks__css__custom") as HTMLStyleElement; + const variables = getCssVariables(styleElement); + + // Placeholder should be derived from inputTextColor, not questionColor + expect(variables["--fb-input-placeholder-color"]).toBeDefined(); + expect(variables["--fb-placeholder-color"]).toBeDefined(); + // Both should be based on inputTextColor (#112233) mixed with white, not questionColor (#AABBCC) + // We can verify by checking the placeholder color doesn't contain the questionColor mix + expect(variables["--fb-input-placeholder-color"]).toBe(variables["--fb-placeholder-color"]); + }); + + test("should derive input-placeholder-color from questionColor when inputTextColor is not set", () => { + const styling: TSurveyStyling = { + ...getBaseProjectStyling(), + questionColor: { light: "#AABBCC" }, + }; + addCustomThemeToDom({ styling }); + const styleElement = document.getElementById("formbricks__css__custom") as HTMLStyleElement; + const variables = getCssVariables(styleElement); + + // Placeholder should fall back to questionColor when inputTextColor is not set + expect(variables["--fb-input-placeholder-color"]).toBeDefined(); + expect(variables["--fb-placeholder-color"]).toBeDefined(); + expect(variables["--fb-input-placeholder-color"]).toBe(variables["--fb-placeholder-color"]); + }); + test("should set signature and branding text colors for dark questionColor", () => { const styling = getBaseProjectStyling({ questionColor: { light: "#202020" }, // A dark color diff --git a/packages/surveys/src/lib/styles.ts b/packages/surveys/src/lib/styles.ts index ebccebdb0356..b767e0722368 100644 --- a/packages/surveys/src/lib/styles.ts +++ b/packages/surveys/src/lib/styles.ts @@ -111,8 +111,10 @@ export const addCustomThemeToDom = ({ styling }: { styling: TProjectStyling | TS // Backwards-compat: legacy variables still used by some consumers/tests appendCssVariable("subheading-color", styling.questionColor?.light); - if (styling.questionColor?.light) { - appendCssVariable("placeholder-color", mixColor(styling.questionColor.light, "#ffffff", 0.3)); + const placeholderBaseColor = styling.inputTextColor?.light ?? styling.questionColor?.light; + if (placeholderBaseColor) { + appendCssVariable("placeholder-color", mixColor(placeholderBaseColor, "#ffffff", 0.3)); + appendCssVariable("input-placeholder-color", mixColor(placeholderBaseColor, "#ffffff", 0.3)); } appendCssVariable("border-color", styling.inputBorderColor?.light); @@ -210,6 +212,12 @@ export const addCustomThemeToDom = ({ styling }: { styling: TProjectStyling | TS // Inputs (Advanced) appendCssVariable("input-background-color", styling.inputBgColor?.light ?? styling.inputColor?.light); appendCssVariable("input-text-color", styling.inputTextColor?.light); + if (styling.inputTextColor?.light) { + appendCssVariable( + "input-placeholder-color", + mixColor(styling.inputTextColor.light, "#ffffff", 0.3) + ); + } if (styling.inputBorderRadius !== undefined) appendCssVariable("input-border-radius", formatDimension(styling.inputBorderRadius)); if (styling.inputHeight !== undefined)