mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-12 11:28:58 -05:00
chore: merge with epic/v5
This commit is contained in:
@@ -64,6 +64,9 @@ DATABASE_URL='postgresql://postgres:postgres@localhost:5432/formbricks?schema=pu
|
||||
HUB_API_KEY=dev-api-key
|
||||
HUB_API_URL=http://localhost:8080
|
||||
HUB_DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres?sslmode=disable
|
||||
# Hub image tag used by docker-compose.dev.yml (hub + hub-migrate). Leave unset to use the
|
||||
# pinned default in the compose file; override here when testing a specific Hub release.
|
||||
# HUB_IMAGE_TAG=0.2.0
|
||||
|
||||
###########################
|
||||
# CUBE ANALYTICS (XM V5) #
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
"use server";
|
||||
|
||||
import { z } from "zod";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
||||
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
||||
import { AuthenticatedActionClientCtx } from "@/lib/utils/action-client/types/context";
|
||||
import { getOrganizationIdFromWorkspaceId } from "@/lib/utils/helper";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { semanticSearchFeedbackRecords } from "@/modules/hub/service";
|
||||
import type { SemanticSearchResultItem } from "@/modules/hub/types";
|
||||
|
||||
const TOPICS_PREVIEW_LIMIT = 10;
|
||||
const SEARCH_CONCURRENCY = 4;
|
||||
|
||||
const ZSemanticSearchFeedbackRecordsAction = z.object({
|
||||
workspaceId: ZId,
|
||||
query: z.string().trim().min(1).max(500),
|
||||
limit: z.number().min(1).max(50).optional(),
|
||||
minScore: z.number().min(0).max(1).optional(),
|
||||
});
|
||||
|
||||
export type TTopicsPreviewSearchResult = SemanticSearchResultItem & {
|
||||
tenant_id: string;
|
||||
directory_name: string;
|
||||
};
|
||||
|
||||
export type TTopicsPreviewSearchActionResult = {
|
||||
results: TTopicsPreviewSearchResult[];
|
||||
unavailable: boolean;
|
||||
unavailableMessage?: string;
|
||||
};
|
||||
|
||||
const ensureReadAccess = async (userId: string, workspaceId: string): Promise<void> => {
|
||||
const organizationId = await getOrganizationIdFromWorkspaceId(workspaceId);
|
||||
await checkAuthorizationUpdated({
|
||||
userId,
|
||||
organizationId,
|
||||
access: [
|
||||
{
|
||||
type: "organization",
|
||||
roles: ["owner", "manager"],
|
||||
},
|
||||
{
|
||||
type: "workspaceTeam",
|
||||
minPermission: "read",
|
||||
workspaceId,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export const semanticSearchFeedbackRecordsAction = authenticatedActionClient
|
||||
.inputSchema(ZSemanticSearchFeedbackRecordsAction)
|
||||
.action(
|
||||
async ({
|
||||
ctx,
|
||||
parsedInput,
|
||||
}: {
|
||||
ctx: AuthenticatedActionClientCtx;
|
||||
parsedInput: z.infer<typeof ZSemanticSearchFeedbackRecordsAction>;
|
||||
}): Promise<TTopicsPreviewSearchActionResult> => {
|
||||
await ensureReadAccess(ctx.user.id, parsedInput.workspaceId);
|
||||
|
||||
const directories = await getFeedbackDirectoriesByWorkspaceId(parsedInput.workspaceId);
|
||||
if (directories.length === 0) {
|
||||
return { results: [], unavailable: false };
|
||||
}
|
||||
|
||||
const limit = parsedInput.limit ?? TOPICS_PREVIEW_LIMIT;
|
||||
const searches: {
|
||||
directory: (typeof directories)[number];
|
||||
result: Awaited<ReturnType<typeof semanticSearchFeedbackRecords>>;
|
||||
}[] = [];
|
||||
for (let i = 0; i < directories.length; i += SEARCH_CONCURRENCY) {
|
||||
const chunk = directories.slice(i, i + SEARCH_CONCURRENCY);
|
||||
const chunkResults = await Promise.all(
|
||||
chunk.map(async (directory) => {
|
||||
const result = await semanticSearchFeedbackRecords({
|
||||
tenant_id: directory.id,
|
||||
query: parsedInput.query,
|
||||
limit,
|
||||
min_score: parsedInput.minScore,
|
||||
});
|
||||
return { directory, result };
|
||||
})
|
||||
);
|
||||
searches.push(...chunkResults);
|
||||
}
|
||||
|
||||
const successfulResults = searches.flatMap(({ directory, result }) =>
|
||||
(result.data?.data ?? []).map((item) => ({
|
||||
...item,
|
||||
tenant_id: directory.id,
|
||||
directory_name: directory.name,
|
||||
}))
|
||||
);
|
||||
|
||||
if (successfulResults.length > 0) {
|
||||
return {
|
||||
results: successfulResults.toSorted((a, b) => b.score - a.score).slice(0, limit),
|
||||
unavailable: false,
|
||||
};
|
||||
}
|
||||
|
||||
const firstError = searches.find(({ result }) => result.error)?.result.error;
|
||||
if (firstError?.status === 0 || firstError?.status === 503) {
|
||||
return {
|
||||
results: [],
|
||||
unavailable: true,
|
||||
unavailableMessage: firstError.message,
|
||||
};
|
||||
}
|
||||
|
||||
if (firstError) {
|
||||
throw new Error(firstError.message);
|
||||
}
|
||||
|
||||
return { results: [], unavailable: false };
|
||||
}
|
||||
);
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
"use client";
|
||||
|
||||
import { SearchIcon, SparklesIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { UnifyConfigNavigation } from "../../components/UnifyConfigNavigation";
|
||||
import { semanticSearchFeedbackRecordsAction } from "../actions";
|
||||
import type { TTopicsPreviewSearchResult } from "../actions";
|
||||
|
||||
interface TopicsSubtopicsPreviewProps {
|
||||
workspaceId: string;
|
||||
directoryMap: Record<string, string>;
|
||||
}
|
||||
|
||||
export const TopicsSubtopicsPreview = ({
|
||||
workspaceId,
|
||||
directoryMap,
|
||||
}: Readonly<TopicsSubtopicsPreviewProps>) => {
|
||||
const { t } = useTranslation();
|
||||
const [query, setQuery] = useState("");
|
||||
const [results, setResults] = useState<TTopicsPreviewSearchResult[]>([]);
|
||||
const [hasSearched, setHasSearched] = useState(false);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [unavailableMessage, setUnavailableMessage] = useState<string | null>(null);
|
||||
|
||||
const hasDirectories = Object.keys(directoryMap).length > 0;
|
||||
|
||||
const exampleSearches = [
|
||||
t("workspace.unify.semantic_topics_example_slow_checkout"),
|
||||
t("workspace.unify.semantic_topics_example_pricing_complaints"),
|
||||
t("workspace.unify.semantic_topics_example_confusing_onboarding"),
|
||||
];
|
||||
|
||||
const runSearch = async (searchQuery: string) => {
|
||||
const trimmedQuery = searchQuery.trim();
|
||||
if (!trimmedQuery || isSearching) return;
|
||||
|
||||
setQuery(trimmedQuery);
|
||||
setIsSearching(true);
|
||||
setHasSearched(true);
|
||||
setError(null);
|
||||
setUnavailableMessage(null);
|
||||
|
||||
try {
|
||||
const response = await semanticSearchFeedbackRecordsAction({
|
||||
workspaceId,
|
||||
query: trimmedQuery,
|
||||
limit: 10,
|
||||
minScore: 0.7,
|
||||
});
|
||||
|
||||
if (response?.data) {
|
||||
setResults(response.data.results);
|
||||
setUnavailableMessage(response.data.unavailable ? (response.data.unavailableMessage ?? "") : null);
|
||||
} else {
|
||||
setResults([]);
|
||||
setError(getFormattedErrorMessage(response) ?? t("workspace.unify.semantic_search_failed"));
|
||||
}
|
||||
} catch {
|
||||
setResults([]);
|
||||
setError(t("workspace.unify.semantic_search_failed"));
|
||||
} finally {
|
||||
setIsSearching(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (event: { preventDefault: () => void }) => {
|
||||
event.preventDefault();
|
||||
await runSearch(query);
|
||||
};
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("workspace.unify.feedback_records")}>
|
||||
<UnifyConfigNavigation workspaceId={workspaceId} activeId="topics-subtopics" />
|
||||
</PageHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
|
||||
<div className="max-w-2xl space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<SparklesIcon className="h-5 w-5 text-slate-500" aria-hidden="true" />
|
||||
<h2 className="text-lg font-semibold text-slate-900">
|
||||
{t("workspace.unify.semantic_topics_preview_title")}
|
||||
</h2>
|
||||
<Badge text={t("common.preview")} type="gray" size="tiny" />
|
||||
</div>
|
||||
<p className="text-sm text-slate-600">
|
||||
{t("workspace.unify.semantic_topics_preview_description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form className="mt-5 flex flex-col gap-3 sm:flex-row" onSubmit={handleSubmit}>
|
||||
<Input
|
||||
value={query}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder={t("workspace.unify.semantic_search_placeholder")}
|
||||
disabled={!hasDirectories || isSearching}
|
||||
aria-label={t("workspace.unify.semantic_search_input_label")}
|
||||
/>
|
||||
<Button type="submit" disabled={!query.trim() || !hasDirectories} loading={isSearching}>
|
||||
<SearchIcon className="h-4 w-4" aria-hidden="true" />
|
||||
{t("workspace.unify.search_feedback")}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="mt-4 flex flex-wrap items-center gap-2">
|
||||
<span className="text-xs text-slate-500">{t("workspace.unify.try_searching_for")}</span>
|
||||
{exampleSearches.map((label) => (
|
||||
<Button
|
||||
key={label}
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
disabled={!hasDirectories || isSearching}
|
||||
onClick={() => runSearch(label)}>
|
||||
{label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!hasDirectories && (
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-6 text-center shadow-sm">
|
||||
<p className="text-sm text-slate-600">{t("workspace.unify.semantic_search_no_directories")}</p>
|
||||
<Button className="mt-4" size="sm" asChild>
|
||||
<Link href={`/workspaces/${workspaceId}/feedback-sources`}>
|
||||
{t("workspace.unify.manage_feedback_sources")}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700">{error}</div>
|
||||
)}
|
||||
|
||||
{unavailableMessage !== null && (
|
||||
<div className="rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800">
|
||||
{t("workspace.unify.semantic_search_unavailable")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasSearched && !isSearching && !error && unavailableMessage === null && results.length === 0 && (
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-6 text-center shadow-sm">
|
||||
<p className="text-sm text-slate-600">{t("workspace.unify.semantic_search_no_results")}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{results.length > 0 && (
|
||||
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<div className="border-b border-slate-200 px-4 py-3">
|
||||
<p className="text-sm font-medium text-slate-900">
|
||||
{t("workspace.unify.semantic_search_results_count", { count: results.length })}
|
||||
</p>
|
||||
</div>
|
||||
<div className="divide-y divide-slate-100">
|
||||
{results.map((result) => (
|
||||
<div key={`${result.tenant_id}-${result.feedback_record_id}`} className="space-y-2 p-4">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge text={result.directory_name} type="gray" size="tiny" />
|
||||
<span className="text-xs text-slate-500">
|
||||
{t("workspace.unify.semantic_search_relevance", {
|
||||
score: Math.round(result.score * 100),
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm font-medium text-slate-900">
|
||||
{result.field_label || t("workspace.unify.field_label")}
|
||||
</p>
|
||||
<p className="whitespace-pre-wrap text-sm text-slate-700">
|
||||
{result.value_text || t("workspace.unify.semantic_search_missing_text")}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PageContentWrapper>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getFeedbackDirectoriesByWorkspaceId } from "@/modules/ee/feedback-directory/lib/feedback-directory";
|
||||
import { getWorkspaceAuth } from "@/modules/workspaces/lib/utils";
|
||||
import { TopicsSubtopicsPreview } from "./components/topics-subtopics-preview";
|
||||
|
||||
export default async function UnifyTopicsSubtopicsPage(
|
||||
props: Readonly<{ params: Promise<{ workspaceId: string }> }>
|
||||
) {
|
||||
const t = await getTranslate();
|
||||
const params = await props.params;
|
||||
|
||||
const { isOwner, isManager, hasReadAccess, hasReadWriteAccess, hasManageAccess, session } =
|
||||
await getWorkspaceAuth(params.workspaceId);
|
||||
|
||||
if (!session) {
|
||||
throw new Error(t("common.session_not_found"));
|
||||
}
|
||||
|
||||
const hasAccess = isOwner || isManager || hasReadAccess || hasReadWriteAccess || hasManageAccess;
|
||||
if (!hasAccess) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const directories = await getFeedbackDirectoriesByWorkspaceId(params.workspaceId);
|
||||
const directoryMap = Object.fromEntries(directories.map((directory) => [directory.id, directory.name]));
|
||||
|
||||
return <TopicsSubtopicsPreview workspaceId={params.workspaceId} directoryMap={directoryMap} />;
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import { TAuthenticationApiKey } from "@formbricks/types/auth";
|
||||
import { authenticateRequest } from "@/app/api/v1/auth";
|
||||
import { reportApiError } from "@/app/lib/api/api-error-reporter";
|
||||
import { responses } from "@/app/lib/api/response";
|
||||
import { getApiKeyFromHeaders } from "@/modules/api/lib/api-key-auth";
|
||||
import {
|
||||
AuthenticationMethod,
|
||||
isClientSideApiRoute,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
isManagementApiRoute,
|
||||
} from "@/app/middleware/endpoint-validator";
|
||||
import { AUDIT_LOG_ENABLED } from "@/lib/constants";
|
||||
import { getApiKeyFromHeaders } from "@/modules/api/lib/api-key-auth";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
TEnvoyRateLimitAuthType,
|
||||
|
||||
+16
-1
@@ -407,7 +407,6 @@ checksums:
|
||||
common/some_files_failed_to_upload: a0e26efeb29ae905257ecf93b112dff0
|
||||
common/something_went_wrong: a3cd2f01c073f1f5ff436d4b132d39cf
|
||||
common/something_went_wrong_please_try_again: c62a7718d9a1e9c4ffb707807550f836
|
||||
common/soon: b12e79beb0aef9414a445a1b95dd4322
|
||||
common/sort_by: 8adf3dbc5668379558957662f0c43563
|
||||
common/start_free_trial: e346e4ed7d138dcc873db187922369da
|
||||
common/status: 4e1fcce15854d824919b4a582c697c90
|
||||
@@ -3579,6 +3578,7 @@ checksums:
|
||||
workspace/unify/request_feedback_source: 51045caa2c81dee971d23a1841d19a7e
|
||||
workspace/unify/required: 04d7fb6f37ffe0a6ca97d49e2a8b6eb5
|
||||
workspace/unify/save_changes: 53dd9f4f0a4accc822fa5c1f2f6d118a
|
||||
workspace/unify/search_feedback: db1e8dd05944bb928b96e3822aee3379
|
||||
workspace/unify/select_a_survey_to_see_questions: 792eba3d2f6d210231a2266401111a20
|
||||
workspace/unify/select_a_value: 115002bf2d9eec536165a7b7efc62862
|
||||
workspace/unify/select_feedback_directory: 88afbf2c2a322249908ee5d00ec5f65d
|
||||
@@ -3588,6 +3588,20 @@ checksums:
|
||||
workspace/unify/select_survey: bac52e59c7847417bef6fe7b7096b475
|
||||
workspace/unify/select_survey_and_questions: 53914988a2f48caecea23f3b3b868b9f
|
||||
workspace/unify/select_survey_questions_description: 3386ed56085eabebefa3cc453269fc5b
|
||||
workspace/unify/semantic_search_failed: 6adf5f85d453ef2923861ad7b188787a
|
||||
workspace/unify/semantic_search_input_label: 3b8af322b080da8b8cb4fcce6c3f3d1e
|
||||
workspace/unify/semantic_search_missing_text: e1ad1ba8f3ab2e05f4f73732543d0ed5
|
||||
workspace/unify/semantic_search_no_directories: 2bcebe10f5898f5422ee17ed66295044
|
||||
workspace/unify/semantic_search_no_results: 50f0572ad7584c91af5a0a28523f40f2
|
||||
workspace/unify/semantic_search_placeholder: b5dbff2cdd334d7b86f18a12c56ffbb1
|
||||
workspace/unify/semantic_search_relevance: ddd1a91cd29944d5af7b899168b988a2
|
||||
workspace/unify/semantic_search_results_count: 199b822e4f709787b79dd42ccd70e58f
|
||||
workspace/unify/semantic_search_unavailable: eb66fd42fc327627e74fe54a76f33b16
|
||||
workspace/unify/semantic_topics_example_confusing_onboarding: ac612953829e6a7f58e34796a472ca71
|
||||
workspace/unify/semantic_topics_example_pricing_complaints: fdf96d24d56620f79c31de48e6c1936b
|
||||
workspace/unify/semantic_topics_example_slow_checkout: 42579a662e637ffe40de7078def55805
|
||||
workspace/unify/semantic_topics_preview_description: 330871cf6f36128bfdbc7d9d20c500a4
|
||||
workspace/unify/semantic_topics_preview_title: 2d59d672921b4807a40770e5d40b485e
|
||||
workspace/unify/set_value: b8a86f8da957ebd599ece4b1b1936a78
|
||||
workspace/unify/setup_connection: cce7d9c488d737d04e70bed929a46f8a
|
||||
workspace/unify/showing_count_loaded: f443aae08223b65fbd5521d6e69534a4
|
||||
@@ -3615,6 +3629,7 @@ checksums:
|
||||
workspace/unify/submission_id: 02edf76883b47079dbe20f3f36b7c1a7
|
||||
workspace/unify/survey_has_no_questions: c08514b6bce5eb464a4492239be5934d
|
||||
workspace/unify/topics_and_subtopics: 1148eca01a1993fadca932efcdea7641
|
||||
workspace/unify/try_searching_for: 8bc02885a2efdc53d7323aac26ae1110
|
||||
workspace/unify/unify_feedback: cd68c8ce0445767e7dcfb4de789903d5
|
||||
workspace/unify/update_mapping_description: 58d5966c0c9b406c037dff3aa8bcb396
|
||||
workspace/unify/updated_at: 8fdb85248e591254973403755dcc3724
|
||||
|
||||
@@ -3,17 +3,17 @@ import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import * as crypto from "@/lib/crypto";
|
||||
import {
|
||||
createGatewayServiceToken,
|
||||
createFeedbackRecordsGatewayToken,
|
||||
createEmailChangeToken,
|
||||
createEmailToken,
|
||||
createFeedbackRecordsGatewayToken,
|
||||
createGatewayServiceToken,
|
||||
createInviteToken,
|
||||
createToken,
|
||||
createTokenForLinkSurvey,
|
||||
getEmailFromEmailToken,
|
||||
verifyGatewayServiceToken,
|
||||
verifyFeedbackRecordsGatewayToken,
|
||||
verifyEmailChangeToken,
|
||||
verifyFeedbackRecordsGatewayToken,
|
||||
verifyGatewayServiceToken,
|
||||
verifyInviteToken,
|
||||
verifyToken,
|
||||
verifyTokenForLinkSurvey,
|
||||
|
||||
+9
-3
@@ -3,7 +3,7 @@ import { prisma } from "@formbricks/database";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { ENCRYPTION_KEY, NEXTAUTH_SECRET } from "@/lib/constants";
|
||||
import { symmetricDecrypt, symmetricEncrypt } from "@/lib/crypto";
|
||||
import { getGatewayAuthServiceTokenPurpose, TGatewayAuthService } from "@/modules/gateway-auth/lib/service";
|
||||
import { TGatewayAuthService, getGatewayAuthServiceTokenPurpose } from "@/modules/gateway-auth/lib/service";
|
||||
|
||||
const FEEDBACK_RECORDS_GATEWAY_TOKEN_TTL_SECONDS = 60 * 10;
|
||||
|
||||
@@ -29,7 +29,10 @@ export const createToken = (userId: string, options = {}): string => {
|
||||
return jwt.sign({ id: encryptedUserId }, NEXTAUTH_SECRET, options);
|
||||
};
|
||||
|
||||
export const createGatewayServiceToken = (userId: string, service: TGatewayAuthService): {
|
||||
export const createGatewayServiceToken = (
|
||||
userId: string,
|
||||
service: TGatewayAuthService
|
||||
): {
|
||||
token: string;
|
||||
expiresAt: string;
|
||||
} => {
|
||||
@@ -104,7 +107,10 @@ export const verifyEmailChangeToken = async (token: string): Promise<{ id: strin
|
||||
};
|
||||
};
|
||||
|
||||
export const verifyGatewayServiceToken = (token: string, service: TGatewayAuthService): {
|
||||
export const verifyGatewayServiceToken = (
|
||||
token: string,
|
||||
service: TGatewayAuthService
|
||||
): {
|
||||
userId: string;
|
||||
} => {
|
||||
if (!NEXTAUTH_SECRET) {
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Einige Dateien konnten nicht hochgeladen werden",
|
||||
"something_went_wrong": "Etwas ist schiefgelaufen",
|
||||
"something_went_wrong_please_try_again": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
|
||||
"soon": "Bald",
|
||||
"sort_by": "Sortieren nach",
|
||||
"start_free_trial": "Kostenlose Testversion starten",
|
||||
"status": "Status",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Daten filtern",
|
||||
"filters": "Filter",
|
||||
"filters_toggle_description": "Nur Daten einbeziehen, die die folgenden Bedingungen erfüllen.",
|
||||
"go_to_feedback_directories": "Zu Feedback-Verzeichnissen",
|
||||
"go_to_feedback_directories": "Zu Feedback-Verzeichnissen gehen",
|
||||
"granularity": "Granularität",
|
||||
"granularity_day": "Tag",
|
||||
"granularity_hour": "Stunde",
|
||||
@@ -1852,9 +1851,9 @@
|
||||
"delete_api_key_confirmation": "Alle Anwendungen, die diesen Schlüssel verwenden, können nicht mehr auf deine Formbricks-Daten zugreifen.",
|
||||
"duplicate_access": "Doppelter Workspace-Zugriff ist nicht erlaubt",
|
||||
"duplicate_directory_access": "Doppelter Zugriff auf Feedback-Verzeichnis nicht erlaubt",
|
||||
"feedback_directory_access": "Zugriff auf Feedback-Verzeichnis",
|
||||
"feedback_directory_access": "Feedback-Verzeichnis-Zugriff",
|
||||
"no_api_keys_yet": "Du hast noch keine API-Schlüssel",
|
||||
"no_directory_permissions_found": "Keine Berechtigungen für Feedback-Verzeichnis gefunden",
|
||||
"no_directory_permissions_found": "Keine Berechtigungen für Feedback-Verzeichnisse gefunden",
|
||||
"no_workspace_permissions_found": "Keine Workspace-Berechtigungen gefunden",
|
||||
"organization_access": "Organisations-Zugriff",
|
||||
"organization_access_description": "Wähle Lese- oder Schreibrechte für organisationsweite Ressourcen aus.",
|
||||
@@ -2547,10 +2546,10 @@
|
||||
"archive_directory": "Verzeichnis archivieren",
|
||||
"archive_not_allowed": "Du darfst dieses Verzeichnis nicht archivieren.",
|
||||
"are_you_sure_you_want_to_archive": "Bist du sicher, dass du dieses Verzeichnis archivieren möchtest? Workspaces haben dann keinen Zugriff mehr darauf.",
|
||||
"assign_workspaces_description": "Lege fest, welche Workspaces auf dieses Feedback-Verzeichnis zugreifen können.",
|
||||
"assign_workspaces_description": "Steuere, welche Workspaces auf dieses Feedback-Verzeichnis zugreifen können.",
|
||||
"connectors_description": "Connectoren, die Feedback-Datensätze an dieses Verzeichnis senden.",
|
||||
"create_feedback_directory": "Feedback-Verzeichnis erstellen",
|
||||
"description": "Verwalte Feedback-Verzeichnisse und ihre Workspace-Zuordnungen.",
|
||||
"description": "Verwalte Feedback-Verzeichnisse und ihre Workspace-Zuweisungen.",
|
||||
"directory_archived_successfully": "Verzeichnis erfolgreich archiviert",
|
||||
"directory_created_successfully": "Verzeichnis erfolgreich erstellt",
|
||||
"directory_id": "Verzeichnis-ID",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Schreibgeschützte Metadatenwerte (keine Zeichenfolge)",
|
||||
"metadata_value": "Metadatenwert",
|
||||
"missing_feedback_source_title": "Feedback-Quelle fehlt?",
|
||||
"no_feedback_directory_available": "Diesem Workspace ist kein Feedback-Verzeichnis zugewiesen. Erstelle oder weise zuerst eines zu.",
|
||||
"no_feedback_directory_available": "Diesem Workspace ist kein Feedback-Verzeichnis zugewiesen. Erstelle oder weise zuerst eins zu.",
|
||||
"no_feedback_records": "Noch keine Feedback-Einträge vorhanden. Einträge erscheinen hier, sobald deine Konnektoren Daten senden.",
|
||||
"no_source_fields_loaded": "Noch keine Quellfelder geladen",
|
||||
"no_sources_connected": "Noch keine Quellen verbunden. Füge eine Quelle hinzu, um loszulegen.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Quellen-Integration anfragen",
|
||||
"required": "Erforderlich",
|
||||
"save_changes": "Änderungen speichern",
|
||||
"search_feedback": "Feedback durchsuchen",
|
||||
"select_a_survey_to_see_questions": "Wähle eine Umfrage aus, um ihre Fragen zu sehen",
|
||||
"select_a_value": "Wähle einen Wert aus...",
|
||||
"select_feedback_directory": "Verzeichnis auswählen",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Umfrage auswählen",
|
||||
"select_survey_and_questions": "Umfrage & Fragen auswählen",
|
||||
"select_survey_questions_description": "Wähle aus, welche Umfragefragen FeedbackRecords erstellen sollen.",
|
||||
"semantic_search_failed": "Suche nach Feedback-Einträgen fehlgeschlagen",
|
||||
"semantic_search_input_label": "Feedback-Einträge nach Thema durchsuchen",
|
||||
"semantic_search_missing_text": "Dieser Feedback-Eintrag hat keinen anzuzeigenden Text.",
|
||||
"semantic_search_no_directories": "Diesem Workspace ist noch kein Feedback-Verzeichnis zugewiesen. Füge eine Feedback-Quelle hinzu, um Feedback nach Bedeutung zu durchsuchen.",
|
||||
"semantic_search_no_results": "Keine passenden Feedback-Einträge gefunden. Versuche ein breiteres Thema oder eine andere Formulierung.",
|
||||
"semantic_search_placeholder": "Suche nach einem Thema, z. B. Preisbeschwerden",
|
||||
"semantic_search_relevance": "{score} % Relevanz",
|
||||
"semantic_search_results_count": "{count, plural, one {# passender Feedback-Eintrag} other {# passende Feedback-Einträge}}",
|
||||
"semantic_search_unavailable": "Semantische Suche ist noch nicht verfügbar. Konfiguriere Hub-Embeddings, um diese Vorschau zu nutzen.",
|
||||
"semantic_topics_example_confusing_onboarding": "verwirrende Einführung",
|
||||
"semantic_topics_example_pricing_complaints": "Preisbeschwerden",
|
||||
"semantic_topics_example_slow_checkout": "langsamer Checkout",
|
||||
"semantic_topics_preview_description": "Gib ein Thema oder einen Begriff ein, um Feedback-Einträge nach Bedeutung zu finden. Dies ist eine frühe Vorschau auf zukünftige Themen & Unterthemen.",
|
||||
"semantic_topics_preview_title": "Feedback nach Thema durchsuchen",
|
||||
"set_value": "Wert festlegen",
|
||||
"setup_connection": "Verbindung einrichten",
|
||||
"showing_count_loaded": "{count} Datensätze werden angezeigt",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Einreichungs-ID",
|
||||
"survey_has_no_questions": "Diese Umfrage hat keine Fragen",
|
||||
"topics_and_subtopics": "Themen & Unterthemen",
|
||||
"try_searching_for": "Versuche zu suchen nach",
|
||||
"unify_feedback": "Feedback vereinheitlichen",
|
||||
"update_mapping_description": "Aktualisiere die Zuordnungskonfiguration für diese Quelle.",
|
||||
"updated_at": "Aktualisiert am",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Some files failed to upload",
|
||||
"something_went_wrong": "Something went wrong",
|
||||
"something_went_wrong_please_try_again": "Something went wrong. Please try again.",
|
||||
"soon": "Soon",
|
||||
"sort_by": "Sort by",
|
||||
"start_free_trial": "Start free trial",
|
||||
"status": "Status",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Request source integration",
|
||||
"required": "Required",
|
||||
"save_changes": "Save changes",
|
||||
"search_feedback": "Search feedback",
|
||||
"select_a_survey_to_see_questions": "Select a survey to see its questions",
|
||||
"select_a_value": "Select a value...",
|
||||
"select_feedback_directory": "Select a directory",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Select Survey",
|
||||
"select_survey_and_questions": "Select Survey & Questions",
|
||||
"select_survey_questions_description": "Choose which survey questions should create FeedbackRecords.",
|
||||
"semantic_search_failed": "Failed to search feedback records",
|
||||
"semantic_search_input_label": "Search feedback records by topic",
|
||||
"semantic_search_missing_text": "This feedback record has no text to display.",
|
||||
"semantic_search_no_directories": "No feedback record directory is assigned to this workspace yet. Add a feedback source to start searching feedback by meaning.",
|
||||
"semantic_search_no_results": "No matching feedback records found. Try a broader topic or a different phrase.",
|
||||
"semantic_search_placeholder": "Search for a topic, e.g. pricing complaints",
|
||||
"semantic_search_relevance": "{score}% relevance",
|
||||
"semantic_search_results_count": "{count, plural, one {# matching feedback record} other {# matching feedback records}}",
|
||||
"semantic_search_unavailable": "Semantic search is not available yet. Configure Hub embeddings to use this preview.",
|
||||
"semantic_topics_example_confusing_onboarding": "confusing onboarding",
|
||||
"semantic_topics_example_pricing_complaints": "pricing complaints",
|
||||
"semantic_topics_example_slow_checkout": "slow checkout",
|
||||
"semantic_topics_preview_description": "Enter a topic or phrase to surface feedback records by meaning. This is an early preview of future Topics & Subtopics.",
|
||||
"semantic_topics_preview_title": "Search feedback by topic",
|
||||
"set_value": "set value",
|
||||
"setup_connection": "Setup connection",
|
||||
"showing_count_loaded": "Showing {count} records",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Submission ID",
|
||||
"survey_has_no_questions": "This survey has no questions",
|
||||
"topics_and_subtopics": "Topics & Subtopics",
|
||||
"try_searching_for": "Try searching for",
|
||||
"unify_feedback": "Unify Feedback",
|
||||
"update_mapping_description": "Update the mapping configuration for this source.",
|
||||
"updated_at": "Updated at",
|
||||
|
||||
+25
-10
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Algunos archivos no se han podido subir",
|
||||
"something_went_wrong": "Algo ha salido mal",
|
||||
"something_went_wrong_please_try_again": "Algo ha salido mal. Por favor, inténtalo de nuevo.",
|
||||
"soon": "Próximamente",
|
||||
"sort_by": "Ordenar por",
|
||||
"start_free_trial": "Iniciar prueba gratuita",
|
||||
"status": "Estado",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Filtrar datos",
|
||||
"filters": "Filtros",
|
||||
"filters_toggle_description": "Incluye solo los datos que cumplan las siguientes condiciones.",
|
||||
"go_to_feedback_directories": "Ir a Directorios de Comentarios",
|
||||
"go_to_feedback_directories": "Ir a directorios de feedback",
|
||||
"granularity": "Granularidad",
|
||||
"granularity_day": "Día",
|
||||
"granularity_hour": "Hora",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "No hay datos disponibles",
|
||||
"no_data_returned": "La consulta no ha devuelto datos",
|
||||
"no_data_returned_for_chart": "No se han devuelto datos para el gráfico",
|
||||
"no_data_source_available": "No hay ningún directorio de comentarios asignado a este espacio de trabajo.",
|
||||
"no_data_source_available": "No hay ningún directorio de feedback asignado a este espacio de trabajo.",
|
||||
"no_grouping": "Ninguno (solo filtro)",
|
||||
"no_valid_data_to_display": "No hay datos válidos para mostrar",
|
||||
"not_contains": "no contiene",
|
||||
@@ -1851,10 +1850,10 @@
|
||||
"api_key_updated": "Clave API actualizada",
|
||||
"delete_api_key_confirmation": "Cualquier aplicación que use esta clave ya no podrá acceder a tus datos de Formbricks.",
|
||||
"duplicate_access": "No se permite el acceso duplicado al espacio de trabajo",
|
||||
"duplicate_directory_access": "No se permite el acceso duplicado al directorio de comentarios",
|
||||
"feedback_directory_access": "Acceso al Directorio de Comentarios",
|
||||
"duplicate_directory_access": "No se permite el acceso duplicado al directorio de feedback",
|
||||
"feedback_directory_access": "Acceso al directorio de feedback",
|
||||
"no_api_keys_yet": "Aún no tienes ninguna clave API",
|
||||
"no_directory_permissions_found": "No se encontraron permisos de directorio de comentarios",
|
||||
"no_directory_permissions_found": "No se encontraron permisos para el directorio de feedback",
|
||||
"no_workspace_permissions_found": "No se encontraron permisos del espacio de trabajo",
|
||||
"organization_access": "Acceso a la organización",
|
||||
"organization_access_description": "Selecciona privilegios de lectura o escritura para recursos de toda la organización.",
|
||||
@@ -2566,13 +2565,13 @@
|
||||
"error_directory_workspaces_invalid_org": "Algunos de los espacios de trabajo especificados no pertenecen a esta organización.",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "Directorios de Feedback",
|
||||
"no_access": "No tienes permiso para gestionar los directorios de feedback.",
|
||||
"no_access": "No tienes permiso para gestionar directorios de feedback.",
|
||||
"no_connectors": "Aún no hay conectores vinculados a este directorio.",
|
||||
"pause_connectors_confirmation_description": "Si pausas estos conectores, no se añadirán nuevos registros.",
|
||||
"pause_connectors_confirmation_title": "¿Pausar conectores vinculados?",
|
||||
"select_workspaces_placeholder": "Selecciona espacios de trabajo...",
|
||||
"show_archived": "Mostrar archivados",
|
||||
"title": "Directorios de Feedback",
|
||||
"title": "Directorios de feedback",
|
||||
"unarchive": "Desarchivar",
|
||||
"unarchive_workspace_conflict": "No se puede desarchivar este directorio porque uno o más espacios de trabajo asignados están archivados.",
|
||||
"upgrade_prompt_description": "Organiza los registros de feedback en directorios y dirige los datos al espacio de trabajo adecuado. Disponible en los planes Pro y Scale.",
|
||||
@@ -3687,7 +3686,7 @@
|
||||
"enum": "enum",
|
||||
"failed_to_load_feedback_records": "Error al cargar los registros de comentarios",
|
||||
"feedback_date": "Fecha actual",
|
||||
"feedback_directory": "Directorio de Feedback",
|
||||
"feedback_directory": "Directorio de feedback",
|
||||
"feedback_record_created_successfully": "Registro de comentarios creado correctamente",
|
||||
"feedback_record_details": "Detalles del registro de comentarios",
|
||||
"feedback_record_details_description": "Revise y actualice los campos del registro de comentarios.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Valores de metadatos de solo lectura (no cadenas)",
|
||||
"metadata_value": "Valor de metadatos",
|
||||
"missing_feedback_source_title": "¿Falta alguna fuente de feedback?",
|
||||
"no_feedback_directory_available": "No hay ningún directorio de feedback asignado a este espacio de trabajo. Crea o asigna uno primero.",
|
||||
"no_feedback_directory_available": "No hay ningún directorio de feedback asignado a este espacio de trabajo. Primero crea o asigna uno.",
|
||||
"no_feedback_records": "Aún no hay registros de comentarios. Los registros aparecerán aquí una vez que tus conectores empiecen a enviar datos.",
|
||||
"no_source_fields_loaded": "Aún no se han cargado campos de origen",
|
||||
"no_sources_connected": "Aún no hay fuentes conectadas. Añade una fuente para empezar.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Solicitar integración de fuente",
|
||||
"required": "Obligatorio",
|
||||
"save_changes": "Guardar cambios",
|
||||
"search_feedback": "Buscar feedback",
|
||||
"select_a_survey_to_see_questions": "Selecciona una encuesta para ver sus preguntas",
|
||||
"select_a_value": "Selecciona un valor...",
|
||||
"select_feedback_directory": "Selecciona un directorio",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Seleccionar encuesta",
|
||||
"select_survey_and_questions": "Seleccionar encuesta y preguntas",
|
||||
"select_survey_questions_description": "Elige qué preguntas de la encuesta deben crear FeedbackRecords.",
|
||||
"semantic_search_failed": "No se pudieron buscar los registros de feedback",
|
||||
"semantic_search_input_label": "Buscar registros de feedback por tema",
|
||||
"semantic_search_missing_text": "Este registro de feedback no tiene texto para mostrar.",
|
||||
"semantic_search_no_directories": "Todavía no hay ningún directorio de registros de feedback asignado a este espacio de trabajo. Añade una fuente de feedback para empezar a buscar feedback por significado.",
|
||||
"semantic_search_no_results": "No se encontraron registros de feedback coincidentes. Prueba con un tema más amplio o una frase diferente.",
|
||||
"semantic_search_placeholder": "Busca un tema, por ejemplo, quejas sobre precios",
|
||||
"semantic_search_relevance": "{score}% de relevancia",
|
||||
"semantic_search_results_count": "{count, plural, one {# registro de feedback coincidente} other {# registros de feedback coincidentes}}",
|
||||
"semantic_search_unavailable": "La búsqueda semántica aún no está disponible. Configura los embeddings del Hub para usar esta vista previa.",
|
||||
"semantic_topics_example_confusing_onboarding": "onboarding confuso",
|
||||
"semantic_topics_example_pricing_complaints": "quejas sobre precios",
|
||||
"semantic_topics_example_slow_checkout": "proceso de pago lento",
|
||||
"semantic_topics_preview_description": "Introduce un tema o frase para encontrar registros de comentarios por significado. Esta es una vista previa temprana de futuros Temas y Subtemas.",
|
||||
"semantic_topics_preview_title": "Buscar comentarios por tema",
|
||||
"set_value": "establecer valor",
|
||||
"setup_connection": "Configurar conexión",
|
||||
"showing_count_loaded": "Mostrando {count} registros",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "ID de envío",
|
||||
"survey_has_no_questions": "Esta encuesta no tiene preguntas",
|
||||
"topics_and_subtopics": "Temas y subtemas",
|
||||
"try_searching_for": "Prueba a buscar",
|
||||
"unify_feedback": "Unificar feedback",
|
||||
"update_mapping_description": "Actualiza la configuración de mapeo para esta fuente.",
|
||||
"updated_at": "Actualizado el",
|
||||
|
||||
+29
-14
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Certains fichiers n'ont pas pu être téléchargés",
|
||||
"something_went_wrong": "Quelque chose s'est mal passé.",
|
||||
"something_went_wrong_please_try_again": "Une erreur s'est produite. Veuillez réessayer.",
|
||||
"soon": "Bientôt",
|
||||
"sort_by": "Trier par",
|
||||
"start_free_trial": "Commencer l'essai gratuit",
|
||||
"status": "Statut",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Filtrer les données",
|
||||
"filters": "Filtres",
|
||||
"filters_toggle_description": "Inclure uniquement les données qui répondent aux conditions suivantes.",
|
||||
"go_to_feedback_directories": "Accéder aux répertoires de commentaires",
|
||||
"go_to_feedback_directories": "Accéder aux répertoires de retours",
|
||||
"granularity": "Granularité",
|
||||
"granularity_day": "Jour",
|
||||
"granularity_hour": "Heure",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "Aucune donnée disponible",
|
||||
"no_data_returned": "Aucune donnée retournée par la requête",
|
||||
"no_data_returned_for_chart": "Aucune donnée retournée pour le graphique",
|
||||
"no_data_source_available": "Aucun répertoire de commentaires n'est attribué à cet espace de travail.",
|
||||
"no_data_source_available": "Aucun répertoire de retours n'est attribué à cet espace de travail.",
|
||||
"no_grouping": "Aucun (filtre uniquement)",
|
||||
"no_valid_data_to_display": "Aucune donnée valide à afficher",
|
||||
"not_contains": "ne contient pas",
|
||||
@@ -1851,10 +1850,10 @@
|
||||
"api_key_updated": "Clé API mise à jour",
|
||||
"delete_api_key_confirmation": "Toute application utilisant cette clé ne pourra plus accéder à vos données Formbricks.",
|
||||
"duplicate_access": "Accès en double à l'espace de travail non autorisé",
|
||||
"duplicate_directory_access": "L'accès en double au répertoire de commentaires n'est pas autorisé",
|
||||
"feedback_directory_access": "Accès au répertoire de commentaires",
|
||||
"duplicate_directory_access": "L'accès en double au répertoire de retours n'est pas autorisé",
|
||||
"feedback_directory_access": "Accès au répertoire de retours",
|
||||
"no_api_keys_yet": "Vous n'avez pas encore de clés API",
|
||||
"no_directory_permissions_found": "Aucune autorisation de répertoire de commentaires trouvée",
|
||||
"no_directory_permissions_found": "Aucune autorisation de répertoire de retours trouvée",
|
||||
"no_workspace_permissions_found": "Aucune autorisation d'espace de travail trouvée",
|
||||
"organization_access": "Accès à l'organisation",
|
||||
"organization_access_description": "Sélectionnez les privilèges de lecture ou d'écriture pour les ressources à l'échelle de l'organisation.",
|
||||
@@ -2547,10 +2546,10 @@
|
||||
"archive_directory": "Archiver le répertoire",
|
||||
"archive_not_allowed": "Vous n'êtes pas autorisé à archiver ce répertoire.",
|
||||
"are_you_sure_you_want_to_archive": "Es-tu sûr de vouloir archiver ce répertoire ? Les espaces de travail n'y auront plus accès.",
|
||||
"assign_workspaces_description": "Contrôle quels espaces de travail peuvent accéder à ce répertoire de feedback.",
|
||||
"assign_workspaces_description": "Contrôle quels espaces de travail peuvent accéder à ce répertoire de retours.",
|
||||
"connectors_description": "Connecteurs qui envoient des enregistrements de retour d'expérience vers ce répertoire.",
|
||||
"create_feedback_directory": "Créer un répertoire de commentaires",
|
||||
"description": "Gère les répertoires de feedback et leurs affectations aux espaces de travail.",
|
||||
"description": "Gère les répertoires de retours et leurs attributions d'espaces de travail.",
|
||||
"directory_archived_successfully": "Répertoire archivé avec succès",
|
||||
"directory_created_successfully": "Répertoire créé avec succès",
|
||||
"directory_id": "ID du répertoire",
|
||||
@@ -2559,20 +2558,20 @@
|
||||
"directory_settings_title": "Paramètres de {directoryName}",
|
||||
"directory_unarchived_successfully": "Répertoire désarchivé avec succès",
|
||||
"directory_updated_successfully": "Répertoire mis à jour avec succès",
|
||||
"empty_state": "Aucun répertoire de feedback trouvé. Crée-en un pour commencer.",
|
||||
"empty_state": "Aucun répertoire de retours trouvé. Crée-en un pour commencer.",
|
||||
"error_directory_has_connectors": "Impossible d'archiver un répertoire auquel des connecteurs sont liés. Supprimez d'abord tous les connecteurs.",
|
||||
"error_directory_name_duplicate": "Un répertoire de feedback avec ce nom existe déjà.",
|
||||
"error_directory_name_duplicate": "Un répertoire de retours avec ce nom existe déjà.",
|
||||
"error_directory_name_required": "Le nom du répertoire est requis.",
|
||||
"error_directory_workspaces_invalid_org": "Certains espaces de travail spécifiés n'appartiennent pas à cette organisation.",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "Répertoires de feedback",
|
||||
"no_access": "Tu n'as pas la permission de gérer les répertoires de feedback.",
|
||||
"no_access": "Tu n'as pas la permission de gérer les répertoires de retours.",
|
||||
"no_connectors": "Aucun connecteur lié à ce répertoire pour le moment.",
|
||||
"pause_connectors_confirmation_description": "Si vous mettez ces connecteurs en pause, aucun nouvel enregistrement ne sera ajouté.",
|
||||
"pause_connectors_confirmation_title": "Mettre en pause les connecteurs liés ?",
|
||||
"select_workspaces_placeholder": "Sélectionner des espaces de travail...",
|
||||
"show_archived": "Afficher les éléments archivés",
|
||||
"title": "Répertoires de feedback",
|
||||
"title": "Répertoires de retours",
|
||||
"unarchive": "Désarchiver",
|
||||
"unarchive_workspace_conflict": "Impossible de désarchiver ce répertoire, car un ou plusieurs espaces de travail attribués sont archivés.",
|
||||
"upgrade_prompt_description": "Organisez les enregistrements de feedback dans des répertoires et dirigez les données vers le bon espace de travail. Disponible avec les forfaits Pro et Scale.",
|
||||
@@ -3687,7 +3686,7 @@
|
||||
"enum": "enum",
|
||||
"failed_to_load_feedback_records": "Échec du chargement des enregistrements de feedback",
|
||||
"feedback_date": "Date actuelle",
|
||||
"feedback_directory": "Répertoire de feedback",
|
||||
"feedback_directory": "Répertoire de retours",
|
||||
"feedback_record_created_successfully": "Enregistrement de commentaires créé avec succès",
|
||||
"feedback_record_details": "Détails de l'enregistrement des commentaires",
|
||||
"feedback_record_details_description": "Examiner et mettre à jour les champs d’enregistrement des commentaires.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Valeurs de métadonnées en lecture seule (non-chaîne)",
|
||||
"metadata_value": "Valeur des métadonnées",
|
||||
"missing_feedback_source_title": "Il manque une source de feedback ?",
|
||||
"no_feedback_directory_available": "Aucun répertoire de feedback n'est assigné à cet espace de travail. Créez-en un ou assignez-en un d'abord.",
|
||||
"no_feedback_directory_available": "Aucun répertoire de retours attribué à cet espace de travail. Crée-en un ou attribue-en un d'abord.",
|
||||
"no_feedback_records": "Aucun enregistrement de feedback pour le moment. Les enregistrements apparaîtront ici une fois que vos connecteurs commenceront à envoyer des données.",
|
||||
"no_source_fields_loaded": "Aucun champ source chargé pour le moment",
|
||||
"no_sources_connected": "Aucune source connectée pour le moment. Ajoutez une source pour commencer.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Demander une intégration de source",
|
||||
"required": "Requis",
|
||||
"save_changes": "Enregistrer les modifications",
|
||||
"search_feedback": "Rechercher des retours",
|
||||
"select_a_survey_to_see_questions": "Sélectionnez une enquête pour voir ses questions",
|
||||
"select_a_value": "Sélectionnez une valeur...",
|
||||
"select_feedback_directory": "Sélectionner un répertoire",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Sélectionner l'enquête",
|
||||
"select_survey_and_questions": "Sélectionner l'enquête et les questions",
|
||||
"select_survey_questions_description": "Choisissez quelles questions d'enquête doivent créer des FeedbackRecords.",
|
||||
"semantic_search_failed": "Échec de la recherche des enregistrements de retours",
|
||||
"semantic_search_input_label": "Recherche des enregistrements de retours par sujet",
|
||||
"semantic_search_missing_text": "Cet enregistrement de retours n'a pas de texte à afficher.",
|
||||
"semantic_search_no_directories": "Aucun répertoire d'enregistrements de retours n'est encore attribué à cet espace de travail. Ajoute une source de retours pour commencer à rechercher des retours par signification.",
|
||||
"semantic_search_no_results": "Aucun enregistrement de retours correspondant trouvé. Essaie un sujet plus large ou une formulation différente.",
|
||||
"semantic_search_placeholder": "Recherche un sujet, par ex. plaintes sur les prix",
|
||||
"semantic_search_relevance": "{score} % de pertinence",
|
||||
"semantic_search_results_count": "{count, plural, one {# enregistrement de retours correspondant} other {# enregistrements de retours correspondants}}",
|
||||
"semantic_search_unavailable": "La recherche sémantique n'est pas encore disponible. Configure les embeddings Hub pour utiliser cet aperçu.",
|
||||
"semantic_topics_example_confusing_onboarding": "intégration confuse",
|
||||
"semantic_topics_example_pricing_complaints": "plaintes sur les prix",
|
||||
"semantic_topics_example_slow_checkout": "passage en caisse lent",
|
||||
"semantic_topics_preview_description": "Saisissez un sujet ou une phrase pour retrouver des retours clients par signification. Ceci est un aperçu des futurs Sujets et Sous-sujets.",
|
||||
"semantic_topics_preview_title": "Rechercher des retours par sujet",
|
||||
"set_value": "définir la valeur",
|
||||
"setup_connection": "Configurer la connexion",
|
||||
"showing_count_loaded": "Affichage de {count} enregistrements",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "ID de soumission",
|
||||
"survey_has_no_questions": "Ce sondage n'a pas de questions",
|
||||
"topics_and_subtopics": "Thèmes et sous-thèmes",
|
||||
"try_searching_for": "Essayez de rechercher",
|
||||
"unify_feedback": "Unifier les retours",
|
||||
"update_mapping_description": "Mettre à jour la configuration de mappage pour cette source.",
|
||||
"updated_at": "Mis à jour à",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Néhány fájlt nem sikerült feltölteni",
|
||||
"something_went_wrong": "Valami probléma történt",
|
||||
"something_went_wrong_please_try_again": "Valami probléma történt. Próbálja meg újra.",
|
||||
"soon": "Hamarosan",
|
||||
"sort_by": "Rendezési sorrend",
|
||||
"start_free_trial": "Ingyenes próbaidőszak indítása",
|
||||
"status": "Állapot",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "Nincsenek elérhető adatok",
|
||||
"no_data_returned": "A lekérdezés nem adott vissza adatokat",
|
||||
"no_data_returned_for_chart": "A diagram nem adott vissza adatokat",
|
||||
"no_data_source_available": "Ehhez a munkaterülethez nem tartozik visszajelzési könyvtár.",
|
||||
"no_data_source_available": "Ehhez a munkaterülethez nincs visszajelzési könyvtár hozzárendelve.",
|
||||
"no_grouping": "Nincs (csak szűrés)",
|
||||
"no_valid_data_to_display": "Nincsenek megjeleníthető érvényes adatok",
|
||||
"not_contains": "nem tartalmazza",
|
||||
@@ -2559,20 +2558,20 @@
|
||||
"directory_settings_title": "{directoryName} beállításai",
|
||||
"directory_unarchived_successfully": "A könyvtár archiválása sikeresen visszavonva",
|
||||
"directory_updated_successfully": "A könyvtár sikeresen frissítve",
|
||||
"empty_state": "Nem található visszajelzési könyvtár. Hozzon létre egyet a kezdéshez.",
|
||||
"empty_state": "Nem találhatók visszajelzési könyvtárak. Hozzon létre egyet a kezdéshez.",
|
||||
"error_directory_has_connectors": "Nem archiválható olyan könyvtár, amelyhez csatlakozók vannak társítva. Először távolítson el minden csatlakozót.",
|
||||
"error_directory_name_duplicate": "Ezzel a névvel már létezik visszajelzési könyvtár.",
|
||||
"error_directory_name_duplicate": "Már létezik visszajelzési könyvtár ezzel a névvel.",
|
||||
"error_directory_name_required": "A könyvtár neve kötelező megadni.",
|
||||
"error_directory_workspaces_invalid_org": "Egyes megadott munkaterületek nem ehhez a szervezethez tartoznak.",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "Visszajelzési könyvtárak",
|
||||
"no_access": "Nem rendelkezik jogosultsággal a visszajelzési könyvtárak kezeléséhez.",
|
||||
"no_access": "Önnek nincs jogosultsága a visszajelzési könyvtárak kezeléséhez.",
|
||||
"no_connectors": "Még nincsenek csatlakozók társítva ehhez a könyvtárhoz.",
|
||||
"pause_connectors_confirmation_description": "Ha szünetelteti ezeket a csatlakozókat, nem kerülnek be új rekordok.",
|
||||
"pause_connectors_confirmation_title": "Szünetelteti a kapcsolódó csatlakozókat?",
|
||||
"select_workspaces_placeholder": "Munkaterületek kiválasztása...",
|
||||
"show_archived": "Archivált elemek megjelenítése",
|
||||
"title": "Visszajelzési Könyvtárak",
|
||||
"title": "Visszajelzési könyvtárak",
|
||||
"unarchive": "Archiválás visszavonása",
|
||||
"unarchive_workspace_conflict": "A könyvtár nem állítható vissza, mert egy vagy több hozzárendelt munkaterület archiválva van.",
|
||||
"upgrade_prompt_description": "Szervezze a visszajelzési rekordokat könyvtárakba, és irányítsa az adatokat a megfelelő munkaterületre. A Pro és Scale csomagokban érhető el.",
|
||||
@@ -3687,7 +3686,7 @@
|
||||
"enum": "felsorolás",
|
||||
"failed_to_load_feedback_records": "Nem sikerült betölteni a visszajelzési rekordokat",
|
||||
"feedback_date": "Aktuális dátum",
|
||||
"feedback_directory": "Visszajelzési Könyvtár",
|
||||
"feedback_directory": "Visszajelzési könyvtár",
|
||||
"feedback_record_created_successfully": "A visszajelzési rekord sikeresen létrehozva",
|
||||
"feedback_record_details": "A visszajelzési rekord részletei",
|
||||
"feedback_record_details_description": "Tekintse át és frissítse a visszajelzési rekordmezőket.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Csak olvasható metaadatértékek (nem karakterlánc)",
|
||||
"metadata_value": "A metaadat értéke",
|
||||
"missing_feedback_source_title": "Hiányzik egy visszajelzési forrás?",
|
||||
"no_feedback_directory_available": "Ehhez a munkaterülethez nem tartozik visszajelzési könyvtár. Először hozzon létre vagy rendeljen hozzá egyet.",
|
||||
"no_feedback_directory_available": "Ehhez a munkaterülethez nincs visszajelzési könyvtár hozzárendelve. Hozzon létre vagy rendeljen hozzá egyet először.",
|
||||
"no_feedback_records": "Még nincsenek visszajelzési rekordok. A rekordok itt fognak megjelenni, amint a csatlakozók elkezdik küldeni az adatokat.",
|
||||
"no_source_fields_loaded": "Még nincsenek forrás mezők betöltve",
|
||||
"no_sources_connected": "Még nincsenek források csatlakoztatva. Adj hozzá egy forrást a kezdéshez.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Forrásintegráció kérése",
|
||||
"required": "Kötelező",
|
||||
"save_changes": "Változtatások mentése",
|
||||
"search_feedback": "Visszajelzések keresése",
|
||||
"select_a_survey_to_see_questions": "Válassz egy kérdőívet a kérdések megtekintéséhez",
|
||||
"select_a_value": "Válassz egy értéket...",
|
||||
"select_feedback_directory": "Válasszon egy könyvtárat",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Kérdőív kiválasztása",
|
||||
"select_survey_and_questions": "Kérdőív és kérdések kiválasztása",
|
||||
"select_survey_questions_description": "Válassza ki, mely kérdőívkérdések hozzanak létre visszajelzési rekordokat.",
|
||||
"semantic_search_failed": "A visszajelzési rekordok keresése sikertelen",
|
||||
"semantic_search_input_label": "Visszajelzési rekordok keresése téma szerint",
|
||||
"semantic_search_missing_text": "Ez a visszajelzési rekord nem tartalmaz megjeleníthető szöveget.",
|
||||
"semantic_search_no_directories": "Ehhez a munkaterülethez még nincs visszajelzési rekord könyvtár hozzárendelve. Adjon hozzá egy visszajelzési forrást a jelentés szerinti keresés megkezdéséhez.",
|
||||
"semantic_search_no_results": "Nem találhatók egyező visszajelzési rekordok. Próbálkozzon tágabb témával vagy más kifejezéssel.",
|
||||
"semantic_search_placeholder": "Keressen egy témát, például árazási panaszok",
|
||||
"semantic_search_relevance": "{score}% relevancia",
|
||||
"semantic_search_results_count": "{count, plural, one {# egyező visszajelzési rekord} other {# egyező visszajelzési rekord}}",
|
||||
"semantic_search_unavailable": "A szemantikai keresés még nem érhető el. Konfigurálja a Hub beágyazásokat az előnézet használatához.",
|
||||
"semantic_topics_example_confusing_onboarding": "zavaró regisztráció",
|
||||
"semantic_topics_example_pricing_complaints": "árazási panaszok",
|
||||
"semantic_topics_example_slow_checkout": "lassú pénztári folyamat",
|
||||
"semantic_topics_preview_description": "Adjon meg egy témát vagy kifejezést, hogy tartalmilag kapcsolódó visszajelzési bejegyzéseket jelenítsen meg. Ez egy korai előnézet a jövőbeli Témák és Altémák funkcióról.",
|
||||
"semantic_topics_preview_title": "Visszajelzések keresése téma szerint",
|
||||
"set_value": "érték beállítása",
|
||||
"setup_connection": "Kapcsolat beállítása",
|
||||
"showing_count_loaded": "{count} rekord megjelenítése",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Beküldés azonosítója",
|
||||
"survey_has_no_questions": "Ez a felmérés nem tartalmaz kérdéseket",
|
||||
"topics_and_subtopics": "Témák és altémák",
|
||||
"try_searching_for": "Próbálja ki a következő keresést",
|
||||
"unify_feedback": "Visszajelzések egyesítése",
|
||||
"update_mapping_description": "Frissítse a leképezési konfigurációt ehhez a forráshoz.",
|
||||
"updated_at": "Frissítve",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "一部のファイルのアップロードに失敗しました",
|
||||
"something_went_wrong": "問題が発生しました",
|
||||
"something_went_wrong_please_try_again": "問題が発生しました。もう一度お試しください。",
|
||||
"soon": "近日公開",
|
||||
"sort_by": "並び替え",
|
||||
"start_free_trial": "無料トライアルを開始",
|
||||
"status": "ステータス",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "データをフィルター",
|
||||
"filters": "フィルター",
|
||||
"filters_toggle_description": "以下の条件を満たすデータのみを含めます。",
|
||||
"go_to_feedback_directories": "フィードバックディレクトリへ移動",
|
||||
"go_to_feedback_directories": "フィードバックディレクトリに移動",
|
||||
"granularity": "粒度",
|
||||
"granularity_day": "日",
|
||||
"granularity_hour": "時間",
|
||||
@@ -2550,7 +2549,7 @@
|
||||
"assign_workspaces_description": "このフィードバックディレクトリにアクセスできるワークスペースを管理します。",
|
||||
"connectors_description": "このディレクトリにフィードバックレコードを送信するコネクタ。",
|
||||
"create_feedback_directory": "フィードバックディレクトリを作成",
|
||||
"description": "フィードバックディレクトリとワークスペースの割り当てを管理します。",
|
||||
"description": "フィードバックディレクトリとワークスペースへの割り当てを管理します。",
|
||||
"directory_archived_successfully": "ディレクトリをアーカイブしました",
|
||||
"directory_created_successfully": "ディレクトリを作成しました",
|
||||
"directory_id": "ディレクトリID",
|
||||
@@ -2559,9 +2558,9 @@
|
||||
"directory_settings_title": "{directoryName}の設定",
|
||||
"directory_unarchived_successfully": "ディレクトリのアーカイブを解除しました",
|
||||
"directory_updated_successfully": "ディレクトリを更新しました",
|
||||
"empty_state": "フィードバックディレクトリが見つかりません。最初のディレクトリを作成してください。",
|
||||
"empty_state": "フィードバックディレクトリが見つかりません。作成して始めましょう。",
|
||||
"error_directory_has_connectors": "コネクタがリンクされているディレクトリはアーカイブできません。まずすべてのコネクタを削除してください。",
|
||||
"error_directory_name_duplicate": "この名前のフィードバックディレクトリは既に存在します。",
|
||||
"error_directory_name_duplicate": "この名前のフィードバックディレクトリはすでに存在します。",
|
||||
"error_directory_name_required": "ディレクトリ名は必須です。",
|
||||
"error_directory_workspaces_invalid_org": "指定されたワークスペースの一部がこの組織に属していません。",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "読み取り専用メタデータ値 (非文字列)",
|
||||
"metadata_value": "メタデータ値",
|
||||
"missing_feedback_source_title": "フィードバックソースが見つかりませんか?",
|
||||
"no_feedback_directory_available": "このワークスペースにフィードバックディレクトリが割り当てられていません。まず作成または割り当てを行ってください。",
|
||||
"no_feedback_directory_available": "このワークスペースにはフィードバックディレクトリが割り当てられていません。まずディレクトリを作成または割り当ててください。",
|
||||
"no_feedback_records": "フィードバックレコードはまだありません。コネクタがデータの送信を開始すると、ここにレコードが表示されます。",
|
||||
"no_source_fields_loaded": "ソースフィールドがまだ読み込まれていません",
|
||||
"no_sources_connected": "ソースがまだ接続されていません。開始するにはソースを追加してください。",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "ソース統合をリクエスト",
|
||||
"required": "必須",
|
||||
"save_changes": "変更を保存",
|
||||
"search_feedback": "フィードバックを検索",
|
||||
"select_a_survey_to_see_questions": "フォームを選択して質問を表示",
|
||||
"select_a_value": "値を選択...",
|
||||
"select_feedback_directory": "ディレクトリを選択",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "フォームを選択",
|
||||
"select_survey_and_questions": "フォームと質問を選択",
|
||||
"select_survey_questions_description": "フィードバックレコードを作成するフォームの質問を選択してください。",
|
||||
"semantic_search_failed": "フィードバック記録の検索に失敗しました",
|
||||
"semantic_search_input_label": "トピック別にフィードバック記録を検索",
|
||||
"semantic_search_missing_text": "このフィードバック記録には表示するテキストがありません。",
|
||||
"semantic_search_no_directories": "このワークスペースにはまだフィードバック記録ディレクトリが割り当てられていません。フィードバックソースを追加して、意味による検索を開始しましょう。",
|
||||
"semantic_search_no_results": "一致するフィードバック記録が見つかりませんでした。より広いトピックや別のフレーズを試してください。",
|
||||
"semantic_search_placeholder": "トピックを検索、例: 価格に関する苦情",
|
||||
"semantic_search_relevance": "関連性 {score}%",
|
||||
"semantic_search_results_count": "{count, plural, other {# 件のフィードバック記録}}",
|
||||
"semantic_search_unavailable": "セマンティック検索はまだ利用できません。このプレビューを使用するには、Hub埋め込みを設定してください。",
|
||||
"semantic_topics_example_confusing_onboarding": "わかりにくいオンボーディング",
|
||||
"semantic_topics_example_pricing_complaints": "価格に関する苦情",
|
||||
"semantic_topics_example_slow_checkout": "チェックアウトが遅い",
|
||||
"semantic_topics_preview_description": "トピックやフレーズを入力すると、意味に基づいてフィードバック記録を表示できます。これは、今後のトピックとサブトピック機能の早期プレビューです。",
|
||||
"semantic_topics_preview_title": "トピック別にフィードバックを検索",
|
||||
"set_value": "値を設定",
|
||||
"setup_connection": "接続を設定",
|
||||
"showing_count_loaded": "{count}件のレコードを表示中",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "提出ID",
|
||||
"survey_has_no_questions": "このアンケートには質問がありません",
|
||||
"topics_and_subtopics": "トピックとサブトピック",
|
||||
"try_searching_for": "検索してみる",
|
||||
"unify_feedback": "フィードバックを統合",
|
||||
"update_mapping_description": "このソースのマッピング設定を更新します。",
|
||||
"updated_at": "更新日時",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Sommige bestanden konden niet worden geüpload",
|
||||
"something_went_wrong": "Er is iets misgegaan",
|
||||
"something_went_wrong_please_try_again": "Er is iets misgegaan. Probeer het opnieuw.",
|
||||
"soon": "Binnenkort",
|
||||
"sort_by": "Sorteer op",
|
||||
"start_free_trial": "Start gratis proefperiode",
|
||||
"status": "Status",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Data filteren",
|
||||
"filters": "Filters",
|
||||
"filters_toggle_description": "Neem alleen gegevens op die aan de volgende voorwaarden voldoen.",
|
||||
"go_to_feedback_directories": "Ga naar Feedbackmappen",
|
||||
"go_to_feedback_directories": "Ga naar feedbackmappen",
|
||||
"granularity": "Granulariteit",
|
||||
"granularity_day": "Dag",
|
||||
"granularity_hour": "Uur",
|
||||
@@ -2550,7 +2549,7 @@
|
||||
"assign_workspaces_description": "Bepaal welke workspaces toegang hebben tot deze feedbackmap.",
|
||||
"connectors_description": "Connectoren die feedbackrecords naar deze map sturen.",
|
||||
"create_feedback_directory": "Feedbackmap maken",
|
||||
"description": "Beheer feedbackmappen en hun workspace-toewijzingen.",
|
||||
"description": "Beheer feedbackmappen en hun workspacetoewijzingen.",
|
||||
"directory_archived_successfully": "Map succesvol gearchiveerd",
|
||||
"directory_created_successfully": "Map succesvol aangemaakt",
|
||||
"directory_id": "Map-ID",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Bronintegratie aanvragen",
|
||||
"required": "Vereist",
|
||||
"save_changes": "Wijzigingen opslaan",
|
||||
"search_feedback": "Zoek feedback",
|
||||
"select_a_survey_to_see_questions": "Selecteer een enquête om de vragen te zien",
|
||||
"select_a_value": "Selecteer een waarde...",
|
||||
"select_feedback_directory": "Selecteer een map",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Selecteer enquête",
|
||||
"select_survey_and_questions": "Selecteer enquête & vragen",
|
||||
"select_survey_questions_description": "Kies welke enquêtevragen FeedbackRecords moeten aanmaken.",
|
||||
"semantic_search_failed": "Feedbackrecords zoeken is mislukt",
|
||||
"semantic_search_input_label": "Zoek feedbackrecords op onderwerp",
|
||||
"semantic_search_missing_text": "Dit feedbackrecord heeft geen tekst om weer te geven.",
|
||||
"semantic_search_no_directories": "Er is nog geen feedbackmap toegewezen aan deze workspace. Voeg een feedbackbron toe om te beginnen met zoeken op betekenis.",
|
||||
"semantic_search_no_results": "Geen overeenkomende feedbackrecords gevonden. Probeer een breder onderwerp of een andere zoekopdracht.",
|
||||
"semantic_search_placeholder": "Zoek naar een onderwerp, bijv. klachten over prijzen",
|
||||
"semantic_search_relevance": "{score}% relevant",
|
||||
"semantic_search_results_count": "{count, plural, one {# overeenkomend feedbackrecord} other {# overeenkomende feedbackrecords}}",
|
||||
"semantic_search_unavailable": "Semantisch zoeken is nog niet beschikbaar. Configureer Hub-embeddings om deze preview te gebruiken.",
|
||||
"semantic_topics_example_confusing_onboarding": "verwarrende onboarding",
|
||||
"semantic_topics_example_pricing_complaints": "klachten over prijzen",
|
||||
"semantic_topics_example_slow_checkout": "trage afrekening",
|
||||
"semantic_topics_preview_description": "Voer een onderwerp of zin in om feedbackrecords op betekenis te vinden. Dit is een vroege preview van toekomstige Onderwerpen & Subonderwerpen.",
|
||||
"semantic_topics_preview_title": "Zoek feedback op onderwerp",
|
||||
"set_value": "waarde instellen",
|
||||
"setup_connection": "Verbinding instellen",
|
||||
"showing_count_loaded": "Er worden {count} records weergegeven",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Inzendings-ID",
|
||||
"survey_has_no_questions": "Deze enquête heeft geen vragen",
|
||||
"topics_and_subtopics": "Onderwerpen en subonderwerpen",
|
||||
"try_searching_for": "Probeer te zoeken naar",
|
||||
"unify_feedback": "Feedback verenigen",
|
||||
"update_mapping_description": "Werk de mappingconfiguratie voor deze bron bij.",
|
||||
"updated_at": "Bijgewerkt op",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Alguns arquivos falharam ao enviar",
|
||||
"something_went_wrong": "Algo deu errado",
|
||||
"something_went_wrong_please_try_again": "Algo deu errado. Tente novamente.",
|
||||
"soon": "Em breve",
|
||||
"sort_by": "Ordenar por",
|
||||
"start_free_trial": "Iniciar teste gratuito",
|
||||
"status": "status",
|
||||
@@ -2547,10 +2546,10 @@
|
||||
"archive_directory": "Arquivar Diretório",
|
||||
"archive_not_allowed": "Você não tem permissão para arquivar este diretório.",
|
||||
"are_you_sure_you_want_to_archive": "Tem certeza de que deseja arquivar este diretório? Os espaços de trabalho não terão mais acesso a ele.",
|
||||
"assign_workspaces_description": "Controle quais espaços de trabalho podem acessar este diretório de feedback.",
|
||||
"assign_workspaces_description": "Controle quais workspaces podem acessar este diretório de feedback.",
|
||||
"connectors_description": "Conectores que enviam registros de feedback para este diretório.",
|
||||
"create_feedback_directory": "Criar diretório de feedback",
|
||||
"description": "Gerencie diretórios de feedback e suas atribuições de espaços de trabalho.",
|
||||
"description": "Gerencie diretórios de feedback e suas atribuições de workspace.",
|
||||
"directory_archived_successfully": "Diretório arquivado com sucesso",
|
||||
"directory_created_successfully": "Diretório criado com sucesso",
|
||||
"directory_id": "ID do Diretório",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Solicitar integração de fonte",
|
||||
"required": "Obrigatório",
|
||||
"save_changes": "Salvar alterações",
|
||||
"search_feedback": "Buscar feedback",
|
||||
"select_a_survey_to_see_questions": "Selecione uma pesquisa para ver suas perguntas",
|
||||
"select_a_value": "Selecione um valor...",
|
||||
"select_feedback_directory": "Selecione um diretório",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Selecionar pesquisa",
|
||||
"select_survey_and_questions": "Selecionar pesquisa e perguntas",
|
||||
"select_survey_questions_description": "Escolha quais perguntas da pesquisa devem criar FeedbackRecords.",
|
||||
"semantic_search_failed": "Falha ao buscar registros de feedback",
|
||||
"semantic_search_input_label": "Busque registros de feedback por tópico",
|
||||
"semantic_search_missing_text": "Este registro de feedback não possui texto para exibir.",
|
||||
"semantic_search_no_directories": "Nenhum diretório de registro de feedback está atribuído a este workspace ainda. Adicione uma fonte de feedback para começar a buscar feedback por significado.",
|
||||
"semantic_search_no_results": "Nenhum registro de feedback correspondente encontrado. Tente um tópico mais amplo ou uma frase diferente.",
|
||||
"semantic_search_placeholder": "Busque por um tópico, ex.: reclamações sobre preço",
|
||||
"semantic_search_relevance": "{score}% de relevância",
|
||||
"semantic_search_results_count": "{count, plural, one {# registro de feedback correspondente} other {# registros de feedback correspondentes}}",
|
||||
"semantic_search_unavailable": "A busca semântica ainda não está disponível. Configure os embeddings do Hub para usar esta prévia.",
|
||||
"semantic_topics_example_confusing_onboarding": "onboarding confuso",
|
||||
"semantic_topics_example_pricing_complaints": "reclamações sobre preço",
|
||||
"semantic_topics_example_slow_checkout": "checkout lento",
|
||||
"semantic_topics_preview_description": "Digite um tópico ou frase para encontrar registros de feedback por significado. Esta é uma prévia antecipada dos futuros Tópicos e Subtópicos.",
|
||||
"semantic_topics_preview_title": "Buscar feedback por tópico",
|
||||
"set_value": "definir valor",
|
||||
"setup_connection": "Configurar conexão",
|
||||
"showing_count_loaded": "Mostrando {count} registros",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "ID de envio",
|
||||
"survey_has_no_questions": "Esta pesquisa não possui perguntas",
|
||||
"topics_and_subtopics": "Tópicos e subtópicos",
|
||||
"try_searching_for": "Tente buscar por",
|
||||
"unify_feedback": "Unificar feedback",
|
||||
"update_mapping_description": "Atualize a configuração de mapeamento para esta fonte.",
|
||||
"updated_at": "Atualizado em",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Alguns ficheiros falharam ao carregar",
|
||||
"something_went_wrong": "Algo correu mal",
|
||||
"something_went_wrong_please_try_again": "Algo correu mal. Por favor, tente novamente.",
|
||||
"soon": "Em breve",
|
||||
"sort_by": "Ordem",
|
||||
"start_free_trial": "Iniciar teste gratuito",
|
||||
"status": "Estado",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "Nenhum dado disponível",
|
||||
"no_data_returned": "Nenhum dado devolvido pela consulta",
|
||||
"no_data_returned_for_chart": "Nenhum dado devolvido para o gráfico",
|
||||
"no_data_source_available": "Nenhum diretório de feedback está atribuído a este espaço de trabalho.",
|
||||
"no_data_source_available": "Nenhum diretório de feedback está atribuído a este workspace.",
|
||||
"no_grouping": "Nenhum (apenas filtro)",
|
||||
"no_valid_data_to_display": "Nenhum dado válido para exibir",
|
||||
"not_contains": "não contém",
|
||||
@@ -1854,7 +1853,7 @@
|
||||
"duplicate_directory_access": "Não é permitido acesso duplicado ao diretório de feedback",
|
||||
"feedback_directory_access": "Acesso ao Diretório de Feedback",
|
||||
"no_api_keys_yet": "Ainda não tem nenhuma chave de API",
|
||||
"no_directory_permissions_found": "Não foram encontradas permissões de diretório de feedback",
|
||||
"no_directory_permissions_found": "Nenhuma permissão de diretório de feedback encontrada",
|
||||
"no_workspace_permissions_found": "Não foram encontradas permissões de Espaço de Trabalho",
|
||||
"organization_access": "Acesso à organização",
|
||||
"organization_access_description": "Selecione privilégios de leitura ou escrita para recursos de toda a organização.",
|
||||
@@ -2547,10 +2546,10 @@
|
||||
"archive_directory": "Arquivar Diretório",
|
||||
"archive_not_allowed": "Não tens permissão para arquivar este diretório.",
|
||||
"are_you_sure_you_want_to_archive": "Tens a certeza de que queres arquivar este diretório? Os espaços de trabalho deixarão de ter acesso ao mesmo.",
|
||||
"assign_workspaces_description": "Controla quais os espaços de trabalho que podem aceder a este diretório de feedback.",
|
||||
"assign_workspaces_description": "Controla quais workspaces podem aceder a este diretório de feedback.",
|
||||
"connectors_description": "Conectores que enviam registos de feedback para este diretório.",
|
||||
"create_feedback_directory": "Criar diretório de feedback",
|
||||
"description": "Gere diretórios de feedback e as suas atribuições de espaços de trabalho.",
|
||||
"description": "Gere diretórios de feedback e as suas atribuições de workspace.",
|
||||
"directory_archived_successfully": "Diretório arquivado com sucesso",
|
||||
"directory_created_successfully": "Diretório criado com sucesso",
|
||||
"directory_id": "ID do Diretório",
|
||||
@@ -2559,7 +2558,7 @@
|
||||
"directory_settings_title": "Definições de {directoryName}",
|
||||
"directory_unarchived_successfully": "Diretório desarquivado com sucesso",
|
||||
"directory_updated_successfully": "Diretório atualizado com sucesso",
|
||||
"empty_state": "Não foram encontrados diretórios de feedback. Cria um para começar.",
|
||||
"empty_state": "Nenhum diretório de feedback encontrado. Cria um para começar.",
|
||||
"error_directory_has_connectors": "Não é possível arquivar um diretório que tem conectores associados. Remove todos os conectores primeiro.",
|
||||
"error_directory_name_duplicate": "Já existe um diretório de feedback com este nome.",
|
||||
"error_directory_name_required": "O nome do diretório é obrigatório.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Valores de metadados somente leitura (sem string)",
|
||||
"metadata_value": "Valor dos metadados",
|
||||
"missing_feedback_source_title": "Falta alguma fonte de feedback?",
|
||||
"no_feedback_directory_available": "Não há nenhum diretório de feedback atribuído a este espaço de trabalho. Cria ou atribui um primeiro.",
|
||||
"no_feedback_directory_available": "Nenhum diretório de feedback atribuído a este workspace. Cria ou atribui um primeiro.",
|
||||
"no_feedback_records": "Ainda não há registos de feedback. Os registos aparecerão aqui assim que os teus conectores começarem a enviar dados.",
|
||||
"no_source_fields_loaded": "Ainda não foram carregados campos de origem",
|
||||
"no_sources_connected": "Ainda não há origens ligadas. Adicione uma origem para começar.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Solicitar integração de fonte",
|
||||
"required": "Obrigatório",
|
||||
"save_changes": "Guardar alterações",
|
||||
"search_feedback": "Pesquisar feedback",
|
||||
"select_a_survey_to_see_questions": "Selecione um inquérito para ver as suas perguntas",
|
||||
"select_a_value": "Selecione um valor...",
|
||||
"select_feedback_directory": "Selecionar um diretório",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Selecionar inquérito",
|
||||
"select_survey_and_questions": "Selecionar inquérito e perguntas",
|
||||
"select_survey_questions_description": "Escolha quais perguntas do inquérito devem criar FeedbackRecords.",
|
||||
"semantic_search_failed": "Falha ao pesquisar registos de feedback",
|
||||
"semantic_search_input_label": "Pesquisar registos de feedback por tópico",
|
||||
"semantic_search_missing_text": "Este registo de feedback não tem texto para mostrar.",
|
||||
"semantic_search_no_directories": "Ainda não há nenhum diretório de registos de feedback atribuído a este workspace. Adiciona uma fonte de feedback para começar a pesquisar por significado.",
|
||||
"semantic_search_no_results": "Nenhum registo de feedback correspondente encontrado. Tenta um tópico mais abrangente ou uma frase diferente.",
|
||||
"semantic_search_placeholder": "Pesquisar por um tópico, ex: reclamações sobre preços",
|
||||
"semantic_search_relevance": "{score}% de relevância",
|
||||
"semantic_search_results_count": "{count, plural, one {# registo de feedback correspondente} other {# registos de feedback correspondentes}}",
|
||||
"semantic_search_unavailable": "A pesquisa semântica ainda não está disponível. Configura os embeddings do Hub para usar esta pré-visualização.",
|
||||
"semantic_topics_example_confusing_onboarding": "onboarding confuso",
|
||||
"semantic_topics_example_pricing_complaints": "reclamações sobre preços",
|
||||
"semantic_topics_example_slow_checkout": "checkout lento",
|
||||
"semantic_topics_preview_description": "Introduz um tópico ou frase para encontrar registos de feedback por significado. Esta é uma pré-visualização de futuros Tópicos e Subtópicos.",
|
||||
"semantic_topics_preview_title": "Pesquisar feedback por tópico",
|
||||
"set_value": "definir valor",
|
||||
"setup_connection": "Configurar ligação",
|
||||
"showing_count_loaded": "A mostrar {count} registos",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "ID de envio",
|
||||
"survey_has_no_questions": "Este inquérito não tem perguntas",
|
||||
"topics_and_subtopics": "Tópicos e subtópicos",
|
||||
"try_searching_for": "Experimenta pesquisar por",
|
||||
"unify_feedback": "Unificar feedback",
|
||||
"update_mapping_description": "Atualiza a configuração de mapeamento para esta origem.",
|
||||
"updated_at": "Atualizado em",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Unele fișiere nu au reușit să se încarce",
|
||||
"something_went_wrong": "Ceva nu a mers bine",
|
||||
"something_went_wrong_please_try_again": "Ceva nu a mers bine. Vă rugăm să încercați din nou.",
|
||||
"soon": "În curând",
|
||||
"sort_by": "Sortare după",
|
||||
"start_free_trial": "Începe perioada de probă gratuită",
|
||||
"status": "Stare",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Filtrează datele",
|
||||
"filters": "Filtre",
|
||||
"filters_toggle_description": "Include doar datele care îndeplinesc următoarele condiții.",
|
||||
"go_to_feedback_directories": "Mergi la Directoarele de Feedback",
|
||||
"go_to_feedback_directories": "Mergi la Directoare de Feedback",
|
||||
"granularity": "Granularitate",
|
||||
"granularity_day": "Zi",
|
||||
"granularity_hour": "Oră",
|
||||
@@ -1854,7 +1853,7 @@
|
||||
"duplicate_directory_access": "Accesul duplicat la directorul de feedback nu este permis",
|
||||
"feedback_directory_access": "Acces la Directorul de Feedback",
|
||||
"no_api_keys_yet": "Nu ai încă nicio cheie API",
|
||||
"no_directory_permissions_found": "Nu s-au găsit permisiuni pentru directorul de feedback",
|
||||
"no_directory_permissions_found": "Nu au fost găsite permisiuni pentru directoare de feedback",
|
||||
"no_workspace_permissions_found": "Nu s-au găsit permisiuni pentru Workspace",
|
||||
"organization_access": "Acces organizație",
|
||||
"organization_access_description": "Selectează privilegii de citire sau scriere pentru resursele la nivel de organizație.",
|
||||
@@ -2547,7 +2546,7 @@
|
||||
"archive_directory": "Arhivează directorul",
|
||||
"archive_not_allowed": "Nu ai permisiunea să arhivezi acest director.",
|
||||
"are_you_sure_you_want_to_archive": "Ești sigur că vrei să arhivezi acest director? Spațiile de lucru nu vor mai avea acces la el.",
|
||||
"assign_workspaces_description": "Controlează care spații de lucru pot accesa acest director de feedback.",
|
||||
"assign_workspaces_description": "Controlează ce spații de lucru pot accesa acest director de feedback.",
|
||||
"connectors_description": "Conectori care trimit înregistrări de feedback către acest director.",
|
||||
"create_feedback_directory": "Creează director de feedback",
|
||||
"description": "Gestionează directoarele de feedback și atribuirile lor la spații de lucru.",
|
||||
@@ -3687,7 +3686,7 @@
|
||||
"enum": "enum",
|
||||
"failed_to_load_feedback_records": "Nu s-au putut încărca înregistrările de feedback",
|
||||
"feedback_date": "Data curentă",
|
||||
"feedback_directory": "Director de feedback",
|
||||
"feedback_directory": "Director de Feedback",
|
||||
"feedback_record_created_successfully": "Înregistrare de feedback creată cu succes",
|
||||
"feedback_record_details": "Detaliile înregistrării feedback-ului",
|
||||
"feedback_record_details_description": "Examinați și actualizați câmpurile pentru înregistrarea de feedback.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Valori de metadate numai pentru citire (fără șir)",
|
||||
"metadata_value": "Valoarea metadatelor",
|
||||
"missing_feedback_source_title": "Lipsește o sursă de feedback?",
|
||||
"no_feedback_directory_available": "Niciun director de feedback atribuit acestui spațiu de lucru. Creează sau atribuie unul mai întâi.",
|
||||
"no_feedback_directory_available": "Niciun director de feedback nu este atribuit acestui spațiu de lucru. Creează sau atribuie unul mai întâi.",
|
||||
"no_feedback_records": "Nu există încă înregistrări de feedback. Înregistrările vor apărea aici după ce conectorii tăi vor începe să trimită date.",
|
||||
"no_source_fields_loaded": "Nu au fost încă încărcate câmpuri sursă",
|
||||
"no_sources_connected": "Nicio sursă conectată încă. Adaugă o sursă pentru a începe.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Solicită integrarea sursei",
|
||||
"required": "Obligatoriu",
|
||||
"save_changes": "Salvează modificările",
|
||||
"search_feedback": "Caută feedback",
|
||||
"select_a_survey_to_see_questions": "Selectează un chestionar pentru a vedea întrebările",
|
||||
"select_a_value": "Selectează o valoare...",
|
||||
"select_feedback_directory": "Selectează un director",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Selectează chestionar",
|
||||
"select_survey_and_questions": "Selectează chestionar și întrebări",
|
||||
"select_survey_questions_description": "Alege ce întrebări din chestionar vor crea FeedbackRecords.",
|
||||
"semantic_search_failed": "Căutarea înregistrărilor de feedback a eșuat",
|
||||
"semantic_search_input_label": "Caută înregistrări de feedback după subiect",
|
||||
"semantic_search_missing_text": "Această înregistrare de feedback nu conține text de afișat.",
|
||||
"semantic_search_no_directories": "Încă nu este atribuit niciun director de înregistrări de feedback acestui spațiu de lucru. Adaugă o sursă de feedback pentru a începe căutarea după înțeles.",
|
||||
"semantic_search_no_results": "Nu au fost găsite înregistrări de feedback corespunzătoare. Încearcă un subiect mai general sau o altă formulare.",
|
||||
"semantic_search_placeholder": "Caută un subiect, de ex. plângeri despre prețuri",
|
||||
"semantic_search_relevance": "{score}% relevanță",
|
||||
"semantic_search_results_count": "{count, plural, one {# înregistrare de feedback corespunzătoare} few {# înregistrări de feedback corespunzătoare} other {# de înregistrări de feedback corespunzătoare}}",
|
||||
"semantic_search_unavailable": "Căutarea semantică nu este disponibilă încă. Configurează încorporările Hub pentru a folosi această previzualizare.",
|
||||
"semantic_topics_example_confusing_onboarding": "onboarding confuz",
|
||||
"semantic_topics_example_pricing_complaints": "plângeri despre prețuri",
|
||||
"semantic_topics_example_slow_checkout": "finalizare lentă",
|
||||
"semantic_topics_preview_description": "Introdu un subiect sau o frază pentru a identifica înregistrările de feedback după sens. Aceasta este o previzualizare timpurie a viitoarelor Subiecte și Subsubiecte.",
|
||||
"semantic_topics_preview_title": "Caută feedback după subiect",
|
||||
"set_value": "setează valoare",
|
||||
"setup_connection": "Configurează conexiunea",
|
||||
"showing_count_loaded": "Se afișează {count} înregistrări",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "ID-ul trimiterii",
|
||||
"survey_has_no_questions": "Acest sondaj nu are întrebări",
|
||||
"topics_and_subtopics": "Subiecte și subiecte secundare",
|
||||
"try_searching_for": "Încearcă să cauți",
|
||||
"unify_feedback": "Unify Feedback",
|
||||
"update_mapping_description": "Actualizează configurația de mapare pentru această sursă.",
|
||||
"updated_at": "Actualizat la",
|
||||
|
||||
+28
-13
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Не удалось загрузить некоторые файлы",
|
||||
"something_went_wrong": "Что-то пошло не так",
|
||||
"something_went_wrong_please_try_again": "Что-то пошло не так. Пожалуйста, попробуйте ещё раз.",
|
||||
"soon": "Скоро",
|
||||
"sort_by": "Сортировать по",
|
||||
"start_free_trial": "Начать бесплатный пробный период",
|
||||
"status": "Статус",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Фильтровать данные",
|
||||
"filters": "Фильтры",
|
||||
"filters_toggle_description": "Включай только те данные, которые соответствуют следующим условиям.",
|
||||
"go_to_feedback_directories": "Перейти к директориям отзывов",
|
||||
"go_to_feedback_directories": "Перейти к директориям обратной связи",
|
||||
"granularity": "Детализация",
|
||||
"granularity_day": "День",
|
||||
"granularity_hour": "Час",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "Нет доступных данных",
|
||||
"no_data_returned": "Запрос не вернул данных",
|
||||
"no_data_returned_for_chart": "Для графика не получено данных",
|
||||
"no_data_source_available": "К этому рабочему пространству не назначена директория отзывов.",
|
||||
"no_data_source_available": "К этому рабочему пространству не привязана директория обратной связи.",
|
||||
"no_grouping": "Нет (только фильтр)",
|
||||
"no_valid_data_to_display": "Нет корректных данных для отображения",
|
||||
"not_contains": "не содержит",
|
||||
@@ -1851,10 +1850,10 @@
|
||||
"api_key_updated": "API-ключ обновлён",
|
||||
"delete_api_key_confirmation": "Любые приложения, использующие этот ключ, больше не смогут получить доступ к вашим данным Formbricks.",
|
||||
"duplicate_access": "Дублированный доступ к рабочему пространству не разрешён",
|
||||
"duplicate_directory_access": "Дублирование доступа к директории отзывов не разрешено",
|
||||
"feedback_directory_access": "Доступ к директории отзывов",
|
||||
"duplicate_directory_access": "Дублирование доступа к директории обратной связи запрещено",
|
||||
"feedback_directory_access": "Доступ к директории обратной связи",
|
||||
"no_api_keys_yet": "У вас ещё нет API-ключей",
|
||||
"no_directory_permissions_found": "Разрешения для директории отзывов не найдены",
|
||||
"no_directory_permissions_found": "Разрешения для директорий обратной связи не найдены",
|
||||
"no_workspace_permissions_found": "Разрешения для рабочего пространства не найдены",
|
||||
"organization_access": "Доступ к организации",
|
||||
"organization_access_description": "Выберите права на чтение или запись для ресурсов всей организации.",
|
||||
@@ -2547,10 +2546,10 @@
|
||||
"archive_directory": "Архивировать каталог",
|
||||
"archive_not_allowed": "У тебя нет прав для архивирования этого каталога.",
|
||||
"are_you_sure_you_want_to_archive": "Ты уверен, что хочешь архивировать этот каталог? Рабочие пространства больше не будут иметь к нему доступа.",
|
||||
"assign_workspaces_description": "Управляй тем, какие рабочие пространства могут получить доступ к этому каталогу отзывов.",
|
||||
"assign_workspaces_description": "Управляй, какие рабочие пространства могут получить доступ к этой директории обратной связи.",
|
||||
"connectors_description": "Коннекторы, которые отправляют записи обратной связи в этот каталог.",
|
||||
"create_feedback_directory": "Создать директорию для отзывов",
|
||||
"description": "Управляй каталогами отзывов и их назначением рабочим пространствам.",
|
||||
"description": "Управляй директориями обратной связи и их привязками к рабочим пространствам.",
|
||||
"directory_archived_successfully": "Каталог успешно архивирован",
|
||||
"directory_created_successfully": "Каталог успешно создан",
|
||||
"directory_id": "ID каталога",
|
||||
@@ -2559,14 +2558,14 @@
|
||||
"directory_settings_title": "Настройки {directoryName}",
|
||||
"directory_unarchived_successfully": "Каталог успешно разархивирован",
|
||||
"directory_updated_successfully": "Каталог успешно обновлён",
|
||||
"empty_state": "Каталоги отзывов не найдены. Создай один, чтобы начать.",
|
||||
"empty_state": "Директории обратной связи не найдены. Создай одну, чтобы начать.",
|
||||
"error_directory_has_connectors": "Невозможно архивировать каталог, к которому привязаны коннекторы. Сначала удалите все коннекторы.",
|
||||
"error_directory_name_duplicate": "Директория обратной связи с таким именем уже существует.",
|
||||
"error_directory_name_duplicate": "Директория обратной связи с таким названием уже существует.",
|
||||
"error_directory_name_required": "Необходимо указать имя директории.",
|
||||
"error_directory_workspaces_invalid_org": "Некоторые указанные рабочие пространства не принадлежат этой организации.",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "Каталоги отзывов",
|
||||
"no_access": "У тебя нет прав для управления каталогами отзывов.",
|
||||
"no_access": "У тебя нет прав для управления директориями обратной связи.",
|
||||
"no_connectors": "К этому каталогу пока не привязано ни одного коннектора.",
|
||||
"pause_connectors_confirmation_description": "Если приостановить эти коннекторы, новые записи больше не будут добавляться.",
|
||||
"pause_connectors_confirmation_title": "Приостановить связанные коннекторы?",
|
||||
@@ -3687,7 +3686,7 @@
|
||||
"enum": "enum",
|
||||
"failed_to_load_feedback_records": "Не удалось загрузить отзывы",
|
||||
"feedback_date": "Текущая дата",
|
||||
"feedback_directory": "Каталог обратной связи",
|
||||
"feedback_directory": "Директория обратной связи",
|
||||
"feedback_record_created_successfully": "Запись отзыва успешно создана",
|
||||
"feedback_record_details": "Детали записи обратной связи",
|
||||
"feedback_record_details_description": "Просмотрите и обновите поля записи отзыва.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Значения метаданных только для чтения (нестроковые)",
|
||||
"metadata_value": "Значение метаданных",
|
||||
"missing_feedback_source_title": "Не нашли нужный источник обратной связи?",
|
||||
"no_feedback_directory_available": "К этому рабочему пространству не назначен каталог обратной связи. Сначала создайте или назначьте каталог.",
|
||||
"no_feedback_directory_available": "К этому рабочему пространству не привязана директория обратной связи. Сначала создай или привяжи её.",
|
||||
"no_feedback_records": "Пока нет записей отзывов. Они появятся здесь, когда коннекторы начнут отправлять данные.",
|
||||
"no_source_fields_loaded": "Поля источника ещё не загружены",
|
||||
"no_sources_connected": "Нет подключённых источников. Добавьте источник, чтобы начать.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Запросить интеграцию источника",
|
||||
"required": "Обязательно",
|
||||
"save_changes": "Сохранить изменения",
|
||||
"search_feedback": "Искать обратную связь",
|
||||
"select_a_survey_to_see_questions": "Выберите опрос, чтобы увидеть его вопросы",
|
||||
"select_a_value": "Выберите значение...",
|
||||
"select_feedback_directory": "Выберите каталог",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Выбрать опрос",
|
||||
"select_survey_and_questions": "Выбрать опрос и вопросы",
|
||||
"select_survey_questions_description": "Выберите, какие вопросы опроса должны создавать FeedbackRecords.",
|
||||
"semantic_search_failed": "Не удалось найти записи обратной связи",
|
||||
"semantic_search_input_label": "Ищи записи обратной связи по теме",
|
||||
"semantic_search_missing_text": "В этой записи обратной связи нет текста для отображения.",
|
||||
"semantic_search_no_directories": "К этому рабочему пространству пока не привязана директория с записями обратной связи. Добавь источник обратной связи, чтобы начать поиск по смыслу.",
|
||||
"semantic_search_no_results": "Подходящие записи обратной связи не найдены. Попробуй более широкую тему или другую фразу.",
|
||||
"semantic_search_placeholder": "Ищи по теме, например, жалобы на цены",
|
||||
"semantic_search_relevance": "Релевантность {score}%",
|
||||
"semantic_search_results_count": "{count, plural, one {# подходящая запись обратной связи} few {# подходящие записи обратной связи} many {# подходящих записей обратной связи} other {# подходящих записей обратной связи}}",
|
||||
"semantic_search_unavailable": "Семантический поиск пока недоступен. Настрой эмбеддинги Hub, чтобы использовать эту функцию.",
|
||||
"semantic_topics_example_confusing_onboarding": "запутанная регистрация",
|
||||
"semantic_topics_example_pricing_complaints": "жалобы на цены",
|
||||
"semantic_topics_example_slow_checkout": "медленное оформление заказа",
|
||||
"semantic_topics_preview_description": "Введите тему или фразу, чтобы найти записи отзывов по смыслу. Это ранний предварительный просмотр будущих Тем и Подтем.",
|
||||
"semantic_topics_preview_title": "Поиск отзывов по теме",
|
||||
"set_value": "установить значение",
|
||||
"setup_connection": "Настроить подключение",
|
||||
"showing_count_loaded": "Показано записей: {count}",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Идентификатор отправки",
|
||||
"survey_has_no_questions": "В этом опросе нет вопросов",
|
||||
"topics_and_subtopics": "Темы и подтемы",
|
||||
"try_searching_for": "Попробуйте поискать",
|
||||
"unify_feedback": "Обратная связь Unify",
|
||||
"update_mapping_description": "Обнови настройки сопоставления для этого источника.",
|
||||
"updated_at": "Обновлено",
|
||||
|
||||
+29
-14
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Några filer misslyckades att laddas upp",
|
||||
"something_went_wrong": "Något gick fel",
|
||||
"something_went_wrong_please_try_again": "Något gick fel. Försök igen.",
|
||||
"soon": "Snart",
|
||||
"sort_by": "Sortera efter",
|
||||
"start_free_trial": "Starta gratis provperiod",
|
||||
"status": "Status",
|
||||
@@ -1746,7 +1745,7 @@
|
||||
"filter_data": "Filtrera data",
|
||||
"filters": "Filter",
|
||||
"filters_toggle_description": "Inkludera bara data som uppfyller följande villkor.",
|
||||
"go_to_feedback_directories": "Gå till Feedbackkataloger",
|
||||
"go_to_feedback_directories": "Gå till feedback-kataloger",
|
||||
"granularity": "Detaljnivå",
|
||||
"granularity_day": "Dag",
|
||||
"granularity_hour": "timme",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "Ingen data tillgänglig",
|
||||
"no_data_returned": "Ingen data returnerades från frågan",
|
||||
"no_data_returned_for_chart": "Ingen data returnerades för diagrammet",
|
||||
"no_data_source_available": "Ingen feedbackkatalog är tilldelad till den här arbetsytan.",
|
||||
"no_data_source_available": "Ingen feedback-katalog är tilldelad till denna arbetsyta.",
|
||||
"no_grouping": "Ingen (endast filter)",
|
||||
"no_valid_data_to_display": "Ingen giltig data att visa",
|
||||
"not_contains": "innehåller inte",
|
||||
@@ -1851,10 +1850,10 @@
|
||||
"api_key_updated": "API-nyckel uppdaterad",
|
||||
"delete_api_key_confirmation": "Alla applikationer som använder denna nyckel kommer inte längre att kunna komma åt din Formbricks-data.",
|
||||
"duplicate_access": "Duplicerad arbetsyteåtkomst är inte tillåten",
|
||||
"duplicate_directory_access": "Duplicerad feedbackkatalogåtkomst är inte tillåten",
|
||||
"feedback_directory_access": "Feedbackkatalogåtkomst",
|
||||
"duplicate_directory_access": "Duplicerad åtkomst till feedback-katalog är inte tillåten",
|
||||
"feedback_directory_access": "Åtkomst till feedback-katalog",
|
||||
"no_api_keys_yet": "Du har inga API-nycklar ännu",
|
||||
"no_directory_permissions_found": "Inga feedbackkatalogbehörigheter hittades",
|
||||
"no_directory_permissions_found": "Inga behörigheter för feedback-katalog hittades",
|
||||
"no_workspace_permissions_found": "Inga behörigheter för arbetsytan hittades",
|
||||
"organization_access": "Organisationsåtkomst",
|
||||
"organization_access_description": "Välj läs- eller skrivbehörighet för resurser på organisationsnivå.",
|
||||
@@ -2547,10 +2546,10 @@
|
||||
"archive_directory": "Arkivera katalog",
|
||||
"archive_not_allowed": "Du har inte behörighet att arkivera den här katalogen.",
|
||||
"are_you_sure_you_want_to_archive": "Är du säker på att du vill arkivera den här katalogen? Arbetsytor kommer inte längre ha tillgång till den.",
|
||||
"assign_workspaces_description": "Styr vilka arbetsytor som kan komma åt den här feedbackkatalogen.",
|
||||
"assign_workspaces_description": "Styr vilka arbetsytor som kan komma åt denna feedback-katalog.",
|
||||
"connectors_description": "Kopplingar som skickar feedbackposter till den här katalogen.",
|
||||
"create_feedback_directory": "Skapa feedbackkatalog",
|
||||
"description": "Hantera feedbackkataloger och deras arbetsytstilldelningar.",
|
||||
"description": "Hantera feedback-kataloger och deras arbetsytetilldelningar.",
|
||||
"directory_archived_successfully": "Katalogen arkiverades",
|
||||
"directory_created_successfully": "Katalogen skapades",
|
||||
"directory_id": "Katalog-ID",
|
||||
@@ -2559,20 +2558,20 @@
|
||||
"directory_settings_title": "Inställningar för {directoryName}",
|
||||
"directory_unarchived_successfully": "Katalogen återställdes från arkivet",
|
||||
"directory_updated_successfully": "Katalogen uppdaterades",
|
||||
"empty_state": "Inga feedbackkataloger hittades. Skapa en för att komma igång.",
|
||||
"empty_state": "Inga feedback-kataloger hittades. Skapa en för att komma igång.",
|
||||
"error_directory_has_connectors": "Kan inte arkivera en katalog som har kopplingar kopplade till den. Ta bort alla kopplingar först.",
|
||||
"error_directory_name_duplicate": "En feedbackkatalog med detta namn finns redan.",
|
||||
"error_directory_name_duplicate": "En feedback-katalog med detta namn finns redan.",
|
||||
"error_directory_name_required": "Katalognamn krävs.",
|
||||
"error_directory_workspaces_invalid_org": "Vissa angivna arbetsytor tillhör inte denna organisation.",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "Feedbackkataloger",
|
||||
"no_access": "Du har inte behörighet att hantera feedbackkataloger.",
|
||||
"no_access": "Du har inte behörighet att hantera feedback-kataloger.",
|
||||
"no_connectors": "Inga kopplingar länkade till den här katalogen ännu.",
|
||||
"pause_connectors_confirmation_description": "Om du pausar dessa kopplingar kommer inga nya poster att läggas till.",
|
||||
"pause_connectors_confirmation_title": "Pausa länkade kopplingar?",
|
||||
"select_workspaces_placeholder": "Välj arbetsytor...",
|
||||
"show_archived": "Visa arkiverade",
|
||||
"title": "Feedbackkataloger",
|
||||
"title": "Feedback-kataloger",
|
||||
"unarchive": "Avarkivera",
|
||||
"unarchive_workspace_conflict": "Den här katalogen kan inte avarkiveras eftersom en eller flera tilldelade arbetsytor är arkiverade.",
|
||||
"upgrade_prompt_description": "Organisera feedbackposter i kataloger och dirigera data till rätt arbetsyta. Tillgängligt på Pro- och Scale-planerna.",
|
||||
@@ -3687,7 +3686,7 @@
|
||||
"enum": "enum",
|
||||
"failed_to_load_feedback_records": "Det gick inte att ladda feedbackposter",
|
||||
"feedback_date": "Aktuellt datum",
|
||||
"feedback_directory": "Feedbackkatalog",
|
||||
"feedback_directory": "Feedback-katalog",
|
||||
"feedback_record_created_successfully": "Feedbackposten har skapats",
|
||||
"feedback_record_details": "Feedbackpostdetaljer",
|
||||
"feedback_record_details_description": "Granska och uppdatera fält för feedbackposter.",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Skrivskyddade metadatavärden (icke-sträng)",
|
||||
"metadata_value": "Metadatavärde",
|
||||
"missing_feedback_source_title": "Missing feedback source?",
|
||||
"no_feedback_directory_available": "Ingen feedbackkatalog tilldelad till den här arbetsytan. Skapa eller tilldela en först.",
|
||||
"no_feedback_directory_available": "Ingen feedback-katalog är tilldelad till denna arbetsyta. Skapa eller tilldela en först.",
|
||||
"no_feedback_records": "Inga feedbackposter ännu. Poster visas här när dina connectors börjar skicka data.",
|
||||
"no_source_fields_loaded": "Inga källfält har laddats än",
|
||||
"no_sources_connected": "Inga källor är anslutna än. Lägg till en källa för att komma igång.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Request source integration",
|
||||
"required": "Obligatoriskt",
|
||||
"save_changes": "Spara ändringar",
|
||||
"search_feedback": "Sök feedback",
|
||||
"select_a_survey_to_see_questions": "Välj en enkät för att se dess frågor",
|
||||
"select_a_value": "Välj ett värde...",
|
||||
"select_feedback_directory": "Välj en katalog",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Välj enkät",
|
||||
"select_survey_and_questions": "Välj enkät & frågor",
|
||||
"select_survey_questions_description": "Välj vilka enkätfrågor som ska skapa FeedbackRecords.",
|
||||
"semantic_search_failed": "Misslyckades med att söka i feedback-poster",
|
||||
"semantic_search_input_label": "Sök feedback-poster efter ämne",
|
||||
"semantic_search_missing_text": "Denna feedback-post har ingen text att visa.",
|
||||
"semantic_search_no_directories": "Ingen feedback-katalog är tilldelad till denna arbetsyta ännu. Lägg till en feedback-källa för att börja söka feedback efter mening.",
|
||||
"semantic_search_no_results": "Inga matchande feedback-poster hittades. Prova ett bredare ämne eller en annan fras.",
|
||||
"semantic_search_placeholder": "Sök efter ett ämne, t.ex. klagomål om priser",
|
||||
"semantic_search_relevance": "{score}% relevans",
|
||||
"semantic_search_results_count": "{count, plural, one {# matchande feedback-post} other {# matchande feedback-poster}}",
|
||||
"semantic_search_unavailable": "Semantisk sökning är inte tillgänglig ännu. Konfigurera Hub-inbäddningar för att använda denna förhandsvisning.",
|
||||
"semantic_topics_example_confusing_onboarding": "förvirrande onboarding",
|
||||
"semantic_topics_example_pricing_complaints": "klagomål om priser",
|
||||
"semantic_topics_example_slow_checkout": "långsam utcheckning",
|
||||
"semantic_topics_preview_description": "Ange ett ämne eller en fras för att hitta feedbackposter baserat på betydelse. Detta är en tidig förhandsgranskning av framtida Ämnen & Underämnen.",
|
||||
"semantic_topics_preview_title": "Sök feedback efter ämne",
|
||||
"set_value": "ange värde",
|
||||
"setup_connection": "Ställ in anslutning",
|
||||
"showing_count_loaded": "Visar {count} poster",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Inlämnings-ID",
|
||||
"survey_has_no_questions": "Den här enkäten har inga frågor",
|
||||
"topics_and_subtopics": "Ämnen och delämnen",
|
||||
"try_searching_for": "Prova att söka efter",
|
||||
"unify_feedback": "Samla feedback",
|
||||
"update_mapping_description": "Uppdatera mappningskonfigurationen för den här källan.",
|
||||
"updated_at": "Uppdaterad",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "Bazı dosyalar yüklenemedi",
|
||||
"something_went_wrong": "Bir şeyler ters gitti",
|
||||
"something_went_wrong_please_try_again": "Bir sorun oluştu. Lütfen tekrar deneyin.",
|
||||
"soon": "Yakında",
|
||||
"sort_by": "Sıralama",
|
||||
"start_free_trial": "Ücretsiz denemeyi başlat",
|
||||
"status": "Durum",
|
||||
@@ -2561,12 +2560,12 @@
|
||||
"directory_updated_successfully": "Dizin başarıyla güncellendi",
|
||||
"empty_state": "Geri bildirim dizini bulunamadı. Başlamak için bir tane oluştur.",
|
||||
"error_directory_has_connectors": "Bağlayıcıları bağlı olan bir dizin arşivlenemez. Önce tüm bağlayıcıları kaldır.",
|
||||
"error_directory_name_duplicate": "Bu ada sahip bir geri bildirim dizini zaten mevcut.",
|
||||
"error_directory_name_duplicate": "Bu adda bir geri bildirim dizini zaten mevcut.",
|
||||
"error_directory_name_required": "Dizin adı gereklidir.",
|
||||
"error_directory_workspaces_invalid_org": "Belirtilen çalışma alanlarından bazıları bu organizasyona ait değil.",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "Geri Bildirim Dizinleri",
|
||||
"no_access": "Geri bildirim dizinlerini yönetme izniniz yok.",
|
||||
"no_access": "Geri bildirim dizinlerini yönetme yetkin yok.",
|
||||
"no_connectors": "Bu dizine henüz bağlı bağlayıcı yok.",
|
||||
"pause_connectors_confirmation_description": "Bu bağlayıcıları duraklatırsanız yeni kayıtlar eklenmez.",
|
||||
"pause_connectors_confirmation_title": "Bağlı bağlayıcılar duraklatılsın mı?",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "Salt okunur meta veri değerleri (dize dışı)",
|
||||
"metadata_value": "Meta veri değeri",
|
||||
"missing_feedback_source_title": "Missing feedback source?",
|
||||
"no_feedback_directory_available": "Bu çalışma alanına atanmış bir geri bildirim dizini yok. Önce bir tane oluştur veya ata.",
|
||||
"no_feedback_directory_available": "Bu çalışma alanına atanmış geri bildirim dizini yok. Önce bir tane oluştur veya ata.",
|
||||
"no_feedback_records": "Henüz geri bildirim kaydı yok. Bağlayıcıların veri göndermeye başlamasıyla kayıtlar burada görünecek.",
|
||||
"no_source_fields_loaded": "Henüz kaynak alan yüklenmedi",
|
||||
"no_sources_connected": "Henüz bağlı kaynak yok. Başlamak için bir kaynak ekle.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Request source integration",
|
||||
"required": "Gerekli",
|
||||
"save_changes": "Değişiklikleri kaydet",
|
||||
"search_feedback": "Geri bildirim ara",
|
||||
"select_a_survey_to_see_questions": "Sorularını görmek için bir anket seç",
|
||||
"select_a_value": "Bir değer seç...",
|
||||
"select_feedback_directory": "Bir dizin seç",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "Anket Seç",
|
||||
"select_survey_and_questions": "Anket ve Soruları Seç",
|
||||
"select_survey_questions_description": "Hangi anket sorularının GeriBildirimKayıtları oluşturması gerektiğini seçin.",
|
||||
"semantic_search_failed": "Geri bildirim kayıtları aranamadı",
|
||||
"semantic_search_input_label": "Geri bildirim kayıtlarını konuya göre ara",
|
||||
"semantic_search_missing_text": "Bu geri bildirim kaydında görüntülenecek metin yok.",
|
||||
"semantic_search_no_directories": "Bu çalışma alanına henüz geri bildirim kayıt dizini atanmamış. Anlamsal aramaya başlamak için bir geri bildirim kaynağı ekle.",
|
||||
"semantic_search_no_results": "Eşleşen geri bildirim kaydı bulunamadı. Daha geniş bir konu veya farklı bir ifade dene.",
|
||||
"semantic_search_placeholder": "Bir konu ara, örn. fiyatlandırma şikayetleri",
|
||||
"semantic_search_relevance": "%{score} uygunluk",
|
||||
"semantic_search_results_count": "{count, plural, one {# eşleşen geri bildirim kaydı} other {# eşleşen geri bildirim kaydı}}",
|
||||
"semantic_search_unavailable": "Anlamsal arama henüz kullanıma sunulmadı. Bu önizlemeyi kullanmak için Hub gömme ayarlarını yapılandır.",
|
||||
"semantic_topics_example_confusing_onboarding": "kafa karıştırıcı onboarding",
|
||||
"semantic_topics_example_pricing_complaints": "fiyatlandırma şikayetleri",
|
||||
"semantic_topics_example_slow_checkout": "yavaş ödeme",
|
||||
"semantic_topics_preview_description": "Anlam bazında geri bildirim kayıtlarını ortaya çıkarmak için bir konu veya ifade gir. Bu, gelecekteki Konular ve Alt Konular özelliğinin erken bir ön izlemesidir.",
|
||||
"semantic_topics_preview_title": "Konuya göre geri bildirim ara",
|
||||
"set_value": "değer belirle",
|
||||
"setup_connection": "Bağlantıyı kur",
|
||||
"showing_count_loaded": "{count} kayıt gösteriliyor",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "Gönderim Kimliği",
|
||||
"survey_has_no_questions": "Bu ankette soru yok",
|
||||
"topics_and_subtopics": "Konular ve alt konular",
|
||||
"try_searching_for": "Şunu aramayı dene",
|
||||
"unify_feedback": "Geri Bildirimleri Birleştir",
|
||||
"update_mapping_description": "Bu kaynak için eşleme yapılandırmasını güncelle.",
|
||||
"updated_at": "Güncellenme tarihi",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "某些文件上传失败",
|
||||
"something_went_wrong": "出错了",
|
||||
"something_went_wrong_please_try_again": "出错了 。请 尝试 再次 操作 。",
|
||||
"soon": "即将推出",
|
||||
"sort_by": "排序 依据",
|
||||
"start_free_trial": "开始免费试用",
|
||||
"status": "状态",
|
||||
@@ -1851,8 +1850,8 @@
|
||||
"api_key_updated": "API 密钥已更新",
|
||||
"delete_api_key_confirmation": "使用此密钥的任何应用将无法再访问您的 Formbricks 数据。",
|
||||
"duplicate_access": "不允许重复的工作区访问权限",
|
||||
"duplicate_directory_access": "不允许重复的反馈目录访问",
|
||||
"feedback_directory_access": "反馈目录访问",
|
||||
"duplicate_directory_access": "不允许重复的反馈目录访问权限",
|
||||
"feedback_directory_access": "反馈目录访问权限",
|
||||
"no_api_keys_yet": "您还没有任何 API 密钥",
|
||||
"no_directory_permissions_found": "未找到反馈目录权限",
|
||||
"no_workspace_permissions_found": "未找到工作区权限",
|
||||
@@ -2559,9 +2558,9 @@
|
||||
"directory_settings_title": "{directoryName} 设置",
|
||||
"directory_unarchived_successfully": "目录已成功取消归档",
|
||||
"directory_updated_successfully": "目录已成功更新",
|
||||
"empty_state": "未找到反馈目录。创建一个开始使用吧。",
|
||||
"empty_state": "未找到反馈目录。创建一个以开始使用。",
|
||||
"error_directory_has_connectors": "无法归档已链接连接器的目录。请先移除所有连接器。",
|
||||
"error_directory_name_duplicate": "已存在同名的反馈目录。",
|
||||
"error_directory_name_duplicate": "已存在使用此名称的反馈目录。",
|
||||
"error_directory_name_required": "目录名称为必填项。",
|
||||
"error_directory_workspaces_invalid_org": "某些指定的工作区不属于此组织。",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Request source integration",
|
||||
"required": "必填",
|
||||
"save_changes": "保存更改",
|
||||
"search_feedback": "搜索反馈",
|
||||
"select_a_survey_to_see_questions": "请选择一个调查以查看其问题",
|
||||
"select_a_value": "选择一个值...",
|
||||
"select_feedback_directory": "选择目录",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "选择调查",
|
||||
"select_survey_and_questions": "选择调查和问题",
|
||||
"select_survey_questions_description": "选择哪些调查问题会创建反馈记录。",
|
||||
"semantic_search_failed": "搜索反馈记录失败",
|
||||
"semantic_search_input_label": "按主题搜索反馈记录",
|
||||
"semantic_search_missing_text": "此反馈记录没有可显示的文本。",
|
||||
"semantic_search_no_directories": "此工作区尚未分配反馈记录目录。添加反馈来源以开始按含义搜索反馈。",
|
||||
"semantic_search_no_results": "未找到匹配的反馈记录。尝试使用更宽泛的主题或不同的短语。",
|
||||
"semantic_search_placeholder": "搜索主题,例如:定价投诉",
|
||||
"semantic_search_relevance": "相关度 {score}%",
|
||||
"semantic_search_results_count": "{count, plural, other {# 条匹配的反馈记录}}",
|
||||
"semantic_search_unavailable": "语义搜索暂不可用。配置 Hub 嵌入以使用此预览功能。",
|
||||
"semantic_topics_example_confusing_onboarding": "令人困惑的引导流程",
|
||||
"semantic_topics_example_pricing_complaints": "定价投诉",
|
||||
"semantic_topics_example_slow_checkout": "结账慢",
|
||||
"semantic_topics_preview_description": "输入主题或短语,按含义查找反馈记录。这是未来主题和子主题功能的早期预览。",
|
||||
"semantic_topics_preview_title": "按主题搜索反馈",
|
||||
"set_value": "设置值",
|
||||
"setup_connection": "设置连接",
|
||||
"showing_count_loaded": "显示 {count} 条记录",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "提交ID",
|
||||
"survey_has_no_questions": "该调查没有任何问题",
|
||||
"topics_and_subtopics": "主题和子主题",
|
||||
"try_searching_for": "尝试搜索",
|
||||
"unify_feedback": "统一反馈",
|
||||
"update_mapping_description": "更新此来源的映射配置。",
|
||||
"updated_at": "更新于",
|
||||
|
||||
@@ -434,7 +434,6 @@
|
||||
"some_files_failed_to_upload": "部分檔案上傳失敗",
|
||||
"something_went_wrong": "發生錯誤",
|
||||
"something_went_wrong_please_try_again": "發生錯誤。請再試一次。",
|
||||
"soon": "即將推出",
|
||||
"sort_by": "排序方式",
|
||||
"start_free_trial": "開始免費試用",
|
||||
"status": "狀態",
|
||||
@@ -1770,7 +1769,7 @@
|
||||
"no_data_available": "沒有可用資料",
|
||||
"no_data_returned": "查詢沒有回傳資料",
|
||||
"no_data_returned_for_chart": "此圖表沒有回傳資料",
|
||||
"no_data_source_available": "此工作區尚未指派意見回饋目錄。",
|
||||
"no_data_source_available": "此工作區未指派任何意見回饋目錄。",
|
||||
"no_grouping": "無(僅篩選)",
|
||||
"no_valid_data_to_display": "沒有可顯示的有效資料",
|
||||
"not_contains": "不包含",
|
||||
@@ -2550,7 +2549,7 @@
|
||||
"assign_workspaces_description": "控制哪些工作區可以存取此意見回饋目錄。",
|
||||
"connectors_description": "將意見回饋記錄傳送至此目錄的連接器。",
|
||||
"create_feedback_directory": "建立意見回饋目錄",
|
||||
"description": "管理意見回饋目錄及其工作區配置。",
|
||||
"description": "管理意見回饋目錄及其工作區指派。",
|
||||
"directory_archived_successfully": "目錄已成功封存",
|
||||
"directory_created_successfully": "目錄已成功建立",
|
||||
"directory_id": "目錄 ID",
|
||||
@@ -2559,14 +2558,14 @@
|
||||
"directory_settings_title": "{directoryName} 設定",
|
||||
"directory_unarchived_successfully": "目錄已成功取消封存",
|
||||
"directory_updated_successfully": "目錄已成功更新",
|
||||
"empty_state": "找不到任何意見回饋目錄。建立一個開始使用吧。",
|
||||
"empty_state": "找不到意見回饋目錄。建立一個以開始使用。",
|
||||
"error_directory_has_connectors": "無法封存已連結連接器的目錄。請先移除所有連接器。",
|
||||
"error_directory_name_duplicate": "已存在同名的意見回饋目錄。",
|
||||
"error_directory_name_required": "目錄名稱為必填項目。",
|
||||
"error_directory_workspaces_invalid_org": "部分指定的工作區不屬於此組織。",
|
||||
"error_workspace_already_assigned": "One or more workspaces are already linked to a different active directory. Reassign them first.",
|
||||
"nav_label": "意見回饋目錄",
|
||||
"no_access": "您沒有權限管理意見回饋目錄。",
|
||||
"no_access": "你沒有權限管理意見回饋目錄。",
|
||||
"no_connectors": "此目錄尚未連結任何連接器。",
|
||||
"pause_connectors_confirmation_description": "暫停這些連接器後,將不會再新增新紀錄。",
|
||||
"pause_connectors_confirmation_title": "暫停已連結的連接器?",
|
||||
@@ -3727,7 +3726,7 @@
|
||||
"metadata_read_only_entries": "唯讀元資料值(非字串)",
|
||||
"metadata_value": "元資料值",
|
||||
"missing_feedback_source_title": "Missing feedback source?",
|
||||
"no_feedback_directory_available": "此工作區尚未指派意見回饋目錄。請先建立或指派一個目錄。",
|
||||
"no_feedback_directory_available": "此工作區未指派意見回饋目錄。請先建立或指派一個。",
|
||||
"no_feedback_records": "目前尚無回饋紀錄。當你的連接器開始傳送資料時,紀錄會顯示在這裡。",
|
||||
"no_source_fields_loaded": "尚未載入來源欄位",
|
||||
"no_sources_connected": "尚未連接任何來源。請新增來源以開始使用。",
|
||||
@@ -3739,6 +3738,7 @@
|
||||
"request_feedback_source": "Request source integration",
|
||||
"required": "必填",
|
||||
"save_changes": "儲存變更",
|
||||
"search_feedback": "搜尋意見回饋",
|
||||
"select_a_survey_to_see_questions": "請選擇問卷以查看其問題",
|
||||
"select_a_value": "請選擇一個值...",
|
||||
"select_feedback_directory": "選擇目錄",
|
||||
@@ -3748,6 +3748,20 @@
|
||||
"select_survey": "選擇問卷",
|
||||
"select_survey_and_questions": "選擇問卷與問題",
|
||||
"select_survey_questions_description": "請選擇哪些問卷問題要建立 FeedbackRecords。",
|
||||
"semantic_search_failed": "搜尋意見回饋記錄失敗",
|
||||
"semantic_search_input_label": "依主題搜尋意見回饋記錄",
|
||||
"semantic_search_missing_text": "此意見回饋記錄沒有可顯示的文字。",
|
||||
"semantic_search_no_directories": "此工作區尚未指派意見回饋記錄目錄。新增意見回饋來源以開始依語意搜尋意見回饋。",
|
||||
"semantic_search_no_results": "找不到相符的意見回饋記錄。試試更廣泛的主題或不同的描述方式。",
|
||||
"semantic_search_placeholder": "搜尋主題,例如:價格投訴",
|
||||
"semantic_search_relevance": "相關度 {score}%",
|
||||
"semantic_search_results_count": "{count, plural, other {找到 # 筆相符的意見回饋記錄}}",
|
||||
"semantic_search_unavailable": "語意搜尋尚無法使用。請設定 Hub 嵌入功能以使用此預覽功能。",
|
||||
"semantic_topics_example_confusing_onboarding": "令人困惑的入門流程",
|
||||
"semantic_topics_example_pricing_complaints": "價格投訴",
|
||||
"semantic_topics_example_slow_checkout": "結帳速度慢",
|
||||
"semantic_topics_preview_description": "輸入主題或詞彙,以語意方式找出相關的意見回饋記錄。這是未來主題與子主題功能的早期預覽版本。",
|
||||
"semantic_topics_preview_title": "依主題搜尋意見回饋",
|
||||
"set_value": "設定值",
|
||||
"setup_connection": "設定連線",
|
||||
"showing_count_loaded": "顯示 {count} 筆記錄",
|
||||
@@ -3775,6 +3789,7 @@
|
||||
"submission_id": "提交ID",
|
||||
"survey_has_no_questions": "此問卷沒有任何題目",
|
||||
"topics_and_subtopics": "主題與子主題",
|
||||
"try_searching_for": "試試搜尋",
|
||||
"unify_feedback": "整合回饋",
|
||||
"update_mapping_description": "更新此來源的對應設定。",
|
||||
"updated_at": "更新時間",
|
||||
|
||||
@@ -31,10 +31,10 @@ export const UnifyConfigNavigation = ({
|
||||
label: (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
{t("workspace.unify.topics_and_subtopics")}
|
||||
<Badge text={t("common.soon")} type="gray" size="tiny" />
|
||||
<Badge text={t("common.preview")} type="gray" size="tiny" />
|
||||
</span>
|
||||
),
|
||||
disabled: true,
|
||||
href: `${baseHref}/topics-subtopics`,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import "server-only";
|
||||
import { NextRequest } from "next/server";
|
||||
import { feedbackRecordsEnvoyAuthorizer } from "@/modules/hub/feedback-records-gateway";
|
||||
import {
|
||||
TEnvoyRequestAuthorizer,
|
||||
authenticateEnvoyRequest,
|
||||
buildStatusResponse,
|
||||
parseEnvoyRequestMetadata,
|
||||
TEnvoyRequestAuthorizer,
|
||||
} from "./shared";
|
||||
|
||||
const envoyAuthorizers: TEnvoyRequestAuthorizer[] = [feedbackRecordsEnvoyAuthorizer];
|
||||
@@ -16,9 +16,7 @@ export const authorizeEnvoyRequest = async (request: NextRequest): Promise<Respo
|
||||
return requestMetadata.errorResponse;
|
||||
}
|
||||
|
||||
const authorizer = envoyAuthorizers.find((candidate) =>
|
||||
candidate.matches(requestMetadata.originalRequest)
|
||||
);
|
||||
const authorizer = envoyAuthorizers.find((candidate) => candidate.matches(requestMetadata.originalRequest));
|
||||
if (!authorizer) {
|
||||
return buildStatusResponse(400, "Unsupported Envoy auth route");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { getGatewayAuthServiceTokenPurpose, ZGatewayAuthService } from "./service";
|
||||
import { ZGatewayAuthService, getGatewayAuthServiceTokenPurpose } from "./service";
|
||||
|
||||
describe("gateway auth service registry", () => {
|
||||
test("returns the configured token purpose for feedbackRecords", () => {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { createCacheKey } from "@formbricks/cache";
|
||||
import FormbricksHub from "@formbricks/hub";
|
||||
import {
|
||||
createFeedbackRecord,
|
||||
createFeedbackRecordsBatch,
|
||||
getFeedbackRecordTenant,
|
||||
listFeedbackRecords,
|
||||
retrieveFeedbackRecord,
|
||||
semanticSearchFeedbackRecords,
|
||||
updateFeedbackRecord,
|
||||
} from "./service";
|
||||
import type { FeedbackRecordCreateParams } from "./types";
|
||||
|
||||
vi.mock("@formbricks/logger", () => ({
|
||||
@@ -31,14 +41,6 @@ vi.mock("@/lib/cache", () => ({
|
||||
|
||||
const { getHubClient } = await import("./hub-client");
|
||||
const { cache } = await import("@/lib/cache");
|
||||
const {
|
||||
createFeedbackRecord,
|
||||
createFeedbackRecordsBatch,
|
||||
getFeedbackRecordTenant,
|
||||
listFeedbackRecords,
|
||||
retrieveFeedbackRecord,
|
||||
updateFeedbackRecord,
|
||||
} = await import("./service");
|
||||
|
||||
const sampleInput: FeedbackRecordCreateParams = {
|
||||
field_id: "el-1",
|
||||
@@ -49,6 +51,8 @@ const sampleInput: FeedbackRecordCreateParams = {
|
||||
field_label: "Question?",
|
||||
value_number: 5,
|
||||
collected_at: "2026-02-24T10:00:00.000Z",
|
||||
submission_id: "sub-1",
|
||||
tenant_id: "tenant-1",
|
||||
};
|
||||
|
||||
describe("hub service", () => {
|
||||
@@ -117,7 +121,7 @@ describe("hub service", () => {
|
||||
feedbackRecords: { list: vi.fn().mockResolvedValue(listResponse) },
|
||||
} as any);
|
||||
|
||||
const result = await listFeedbackRecords({ tenant_id: "env-1", limit: 50, offset: 0 });
|
||||
const result = await listFeedbackRecords({ tenant_id: "env-1", limit: 50 });
|
||||
|
||||
expect(result.error).toBeNull();
|
||||
expect(result.data).toEqual(listResponse);
|
||||
@@ -133,7 +137,6 @@ describe("hub service", () => {
|
||||
expect(result.data).toBeNull();
|
||||
expect(result.error).toMatchObject({ status: 0, message: "Network error" });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("retrieveFeedbackRecord", () => {
|
||||
@@ -164,6 +167,89 @@ describe("hub service", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("semanticSearchFeedbackRecords", () => {
|
||||
test("returns error result when getHubClient returns null", async () => {
|
||||
vi.mocked(getHubClient).mockReturnValue(null);
|
||||
|
||||
const result = await semanticSearchFeedbackRecords({
|
||||
tenant_id: "env-1",
|
||||
query: "slow checkout",
|
||||
});
|
||||
|
||||
expect(result.data).toBeNull();
|
||||
expect(result.error).toMatchObject({
|
||||
status: 0,
|
||||
message: "HUB_API_KEY is not set; Hub integration is disabled.",
|
||||
});
|
||||
});
|
||||
|
||||
test("returns data when client.search.performSemanticSearch succeeds", async () => {
|
||||
const searchResponse = {
|
||||
data: [
|
||||
{
|
||||
feedback_record_id: "018e1234-5678-9abc-def0-123456789abc",
|
||||
score: 0.91,
|
||||
field_label: "What can we improve?",
|
||||
value_text: "Checkout feels slow.",
|
||||
},
|
||||
],
|
||||
limit: 10,
|
||||
};
|
||||
const performSemanticSearch = vi.fn().mockResolvedValue(searchResponse);
|
||||
vi.mocked(getHubClient).mockReturnValue({
|
||||
feedbackRecords: { search: { performSemanticSearch } },
|
||||
} as any);
|
||||
|
||||
const input = {
|
||||
tenant_id: "env-1",
|
||||
query: "slow checkout",
|
||||
limit: 10,
|
||||
min_score: 0.7,
|
||||
};
|
||||
const result = await semanticSearchFeedbackRecords(input);
|
||||
|
||||
expect(result.error).toBeNull();
|
||||
expect(result.data).toEqual(searchResponse);
|
||||
expect(performSemanticSearch).toHaveBeenCalledWith(input);
|
||||
});
|
||||
|
||||
test("returns error with status when client.search.performSemanticSearch throws APIError", async () => {
|
||||
const apiError = new (FormbricksHub.APIError as any)("Embeddings are not configured", 503);
|
||||
vi.mocked(getHubClient).mockReturnValue({
|
||||
feedbackRecords: {
|
||||
search: { performSemanticSearch: vi.fn().mockRejectedValue(apiError) },
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await semanticSearchFeedbackRecords({
|
||||
tenant_id: "env-1",
|
||||
query: "slow checkout",
|
||||
});
|
||||
|
||||
expect(result.data).toBeNull();
|
||||
expect(result.error).toMatchObject({
|
||||
status: 503,
|
||||
message: "Embeddings are not configured",
|
||||
});
|
||||
});
|
||||
|
||||
test("returns error result when call throws non-API error", async () => {
|
||||
vi.mocked(getHubClient).mockReturnValue({
|
||||
feedbackRecords: {
|
||||
search: { performSemanticSearch: vi.fn().mockRejectedValue(new Error("Network error")) },
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await semanticSearchFeedbackRecords({
|
||||
tenant_id: "env-1",
|
||||
query: "slow checkout",
|
||||
});
|
||||
|
||||
expect(result.data).toBeNull();
|
||||
expect(result.error).toMatchObject({ status: 0, message: "Network error" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateFeedbackRecord", () => {
|
||||
test("returns error when client is null", async () => {
|
||||
vi.mocked(getHubClient).mockReturnValue(null);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
import FormbricksHub from "@formbricks/hub";
|
||||
import { createCacheKey } from "@formbricks/cache";
|
||||
import FormbricksHub from "@formbricks/hub";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { cache } from "@/lib/cache";
|
||||
import { getHubClient } from "./hub-client";
|
||||
@@ -10,6 +10,8 @@ import type {
|
||||
FeedbackRecordListParams,
|
||||
FeedbackRecordListResponse,
|
||||
FeedbackRecordUpdateParams,
|
||||
SemanticSearchInput,
|
||||
SemanticSearchResponse,
|
||||
} from "./types";
|
||||
|
||||
type HubError = { status: number; message: string; detail: string };
|
||||
@@ -25,9 +27,15 @@ const NO_CONFIG_ERROR = {
|
||||
detail: "HUB_API_KEY is not set; Hub integration is disabled.",
|
||||
} as const;
|
||||
|
||||
const getErrorMessage = (err: unknown): string => {
|
||||
if (err instanceof Error) return err.message;
|
||||
if (typeof err === "string") return err;
|
||||
return "Unknown error";
|
||||
};
|
||||
|
||||
const createResultFromError = (err: unknown): HubFeedbackRecordResult => {
|
||||
const status = err instanceof FormbricksHub.APIError ? err.status : 0;
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
return { data: null, error: { status, message, detail: message } };
|
||||
};
|
||||
|
||||
@@ -95,6 +103,11 @@ export type ListFeedbackRecordsResult = {
|
||||
error: HubError | null;
|
||||
};
|
||||
|
||||
export type SemanticSearchFeedbackRecordsResult = {
|
||||
data: SemanticSearchResponse | null;
|
||||
error: HubError | null;
|
||||
};
|
||||
|
||||
export type FeedbackRecordTenantResult = {
|
||||
data: { tenantId: string } | null;
|
||||
error: { status: number; message: string; detail: string } | null;
|
||||
@@ -116,7 +129,25 @@ export const listFeedbackRecords = async (
|
||||
} catch (err) {
|
||||
logger.warn({ err }, "Hub: listFeedbackRecords failed");
|
||||
const status = err instanceof FormbricksHub.APIError ? err.status : 0;
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
return { data: null, error: { status, message, detail: message } };
|
||||
}
|
||||
};
|
||||
|
||||
export const semanticSearchFeedbackRecords = async (
|
||||
input: SemanticSearchInput
|
||||
): Promise<SemanticSearchFeedbackRecordsResult> => {
|
||||
const client = getHubClient();
|
||||
if (!client) {
|
||||
return { data: null, error: { ...NO_CONFIG_ERROR } };
|
||||
}
|
||||
try {
|
||||
const data = await client.feedbackRecords.search.performSemanticSearch(input);
|
||||
return { data, error: null };
|
||||
} catch (err) {
|
||||
logger.warn({ err, tenantId: input.tenant_id }, "Hub: semanticSearchFeedbackRecords failed");
|
||||
const status = err instanceof FormbricksHub.APIError ? err.status : 0;
|
||||
const message = getErrorMessage(err);
|
||||
return { data: null, error: { status, message, detail: message } };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,3 +5,7 @@ export type FeedbackRecordData = FormbricksHub.FeedbackRecordData;
|
||||
export type FeedbackRecordListParams = FormbricksHub.FeedbackRecordListParams;
|
||||
export type FeedbackRecordListResponse = FormbricksHub.FeedbackRecordListResponse;
|
||||
export type FeedbackRecordUpdateParams = FormbricksHub.FeedbackRecordUpdateParams;
|
||||
|
||||
export type SemanticSearchInput = FormbricksHub.FeedbackRecords.SearchPerformSemanticSearchParams;
|
||||
export type SemanticSearchResponse = FormbricksHub.FeedbackRecords.SearchPerformSemanticSearchResponse;
|
||||
export type SemanticSearchResultItem = FormbricksHub.FeedbackRecords.SearchPerformSemanticSearchResponse.Data;
|
||||
|
||||
@@ -142,9 +142,10 @@ This chart does not deploy Cube.js. XM Suite v5 dashboard and analysis features
|
||||
| hub.enabled | bool | `true` | |
|
||||
| hub.env | object | `{}` | |
|
||||
| hub.existingSecret | string | `""` | |
|
||||
| hub.image.digest | string | `"sha256:14db7b3d285b6e9165b55693f9b83d08beff840a255fd77dd12882ee0a62f5cb"` | When set, takes precedence over tag (immutable pin). |
|
||||
| hub.image.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| hub.image.repository | string | `"ghcr.io/formbricks/hub"` | |
|
||||
| hub.image.tag | string | `"1.0.0"` | |
|
||||
| hub.image.tag | string | `"0.2.0"` | Fallback when digest is empty. |
|
||||
| hub.migration.activeDeadlineSeconds | int | `900` | |
|
||||
| hub.migration.backoffLimit | int | `3` | |
|
||||
| hub.migration.ttlSecondsAfterFinished | int | `300` | |
|
||||
|
||||
@@ -101,6 +101,19 @@ If `namespaceOverride` is provided, it will be used; otherwise, it defaults to `
|
||||
{{- default (include "formbricks.appSecretName" .) .Values.hub.existingSecret -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Hub image reference. Pin by digest in production (hub.image.digest = "sha256:..."); falls back to
|
||||
hub.image.tag for local/dev. All Hub workloads (deployment, init container, migration job, future
|
||||
hub-worker) must use this helper so they cannot drift apart.
|
||||
*/}}
|
||||
{{- define "formbricks.hubImage" -}}
|
||||
{{- if .Values.hub.image.digest -}}
|
||||
{{- printf "%s@%s" .Values.hub.image.repository .Values.hub.image.digest -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s:%s" .Values.hub.image.repository (.Values.hub.image.tag | default "latest") -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{- define "formbricks.postgresAdminPassword" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "formbricks.appSecretName" .)) }}
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
{{- end }}
|
||||
initContainers:
|
||||
- name: hub-migrate
|
||||
image: {{ .Values.hub.image.repository }}:{{ .Values.hub.image.tag | default "latest" }}
|
||||
image: {{ include "formbricks.hubImage" . }}
|
||||
imagePullPolicy: {{ .Values.hub.image.pullPolicy }}
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
@@ -48,7 +48,7 @@ spec:
|
||||
name: {{ include "formbricks.hubSecretName" . }}
|
||||
containers:
|
||||
- name: hub
|
||||
image: {{ .Values.hub.image.repository }}:{{ .Values.hub.image.tag | default "latest" }}
|
||||
image: {{ include "formbricks.hubImage" . }}
|
||||
imagePullPolicy: {{ .Values.hub.image.pullPolicy }}
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
|
||||
@@ -37,7 +37,7 @@ spec:
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: hub-migrate
|
||||
image: {{ .Values.hub.image.repository }}:{{ .Values.hub.image.tag | default "latest" }}
|
||||
image: {{ include "formbricks.hubImage" . }}
|
||||
imagePullPolicy: {{ .Values.hub.image.pullPolicy }}
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
|
||||
@@ -568,8 +568,13 @@ hub:
|
||||
|
||||
image:
|
||||
repository: "ghcr.io/formbricks/hub"
|
||||
# Pin to a semver tag for reproducible deployments; update on each Hub release.
|
||||
tag: "1.0.0"
|
||||
# Pinned by digest for immutable, reproducible deployments. When digest is set it takes
|
||||
# precedence over tag, and deployment, init container, and migration job all resolve to the
|
||||
# same immutable image. Update on each Hub release.
|
||||
# Current digest corresponds to ghcr.io/formbricks/hub:0.2.0.
|
||||
digest: "sha256:14db7b3d285b6e9165b55693f9b83d08beff840a255fd77dd12882ee0a62f5cb"
|
||||
# Tag is a fallback for dev/non-prod when digest is cleared; keep aligned with the digest above.
|
||||
tag: "0.2.0"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# Optional override for the secret Hub reads from.
|
||||
|
||||
@@ -47,8 +47,11 @@ services:
|
||||
- minio-data:/data
|
||||
|
||||
# Run Hub DB migrations (goose + river) before the API starts. Idempotent; runs on every compose up.
|
||||
# Hub image pinned via HUB_IMAGE_TAG so docker does not silently reuse a stale :latest cache.
|
||||
# Keep hub, hub-migrate, and any future hub-worker on the same tag — they share one image and
|
||||
# drift breaks migrations or job processing.
|
||||
hub-migrate:
|
||||
image: ghcr.io/formbricks/hub:latest
|
||||
image: ghcr.io/formbricks/hub:${HUB_IMAGE_TAG:-0.2.0}
|
||||
restart: "no"
|
||||
entrypoint: ["sh", "-c"]
|
||||
command:
|
||||
@@ -63,7 +66,7 @@ services:
|
||||
|
||||
# Formbricks Hub API (ghcr.io/formbricks/hub). Shares the same Postgres database as Formbricks by default.
|
||||
hub:
|
||||
image: ghcr.io/formbricks/hub:latest
|
||||
image: ghcr.io/formbricks/hub:${HUB_IMAGE_TAG:-0.2.0}
|
||||
depends_on:
|
||||
hub-migrate:
|
||||
condition: service_completed_successfully
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ That's it! After running the command and providing the required information, vis
|
||||
The stack includes the [Formbricks Hub](https://github.com/formbricks/hub) API (`ghcr.io/formbricks/hub`) and a bundled Cube.js service for XM Suite v5 analytics. Hub and Cube share the same database as Formbricks by default.
|
||||
|
||||
- **Migrations**: A `hub-migrate` service runs Hub's database migrations (goose + river) before the Hub API starts. It runs on every `docker compose up` and is idempotent.
|
||||
- **Production** (`docker/docker-compose.yml`): Set `HUB_API_KEY` and `CUBEJS_API_SECRET` (required). `HUB_API_URL` defaults to `http://hub:8080` and `CUBEJS_API_URL` defaults to `http://cube:4000` so the Formbricks app can reach both services inside the compose network. Override `HUB_DATABASE_URL` and `CUBEJS_DB_*` only if Hub or Cube should use a separate database.
|
||||
- **Development** (`docker-compose.dev.yml`): Hub and Cube use the same local Postgres database. `HUB_API_KEY` defaults to `dev-api-key`, `CUBEJS_API_URL` defaults to `http://localhost:4000`, and `pnpm dev:setup` generates `CUBEJS_API_SECRET` in the repo root `.env`.
|
||||
- **Production** (`docker/docker-compose.yml`): Set `HUB_API_KEY` and `CUBEJS_API_SECRET` (required). `HUB_API_URL` defaults to `http://hub:8080` and `CUBEJS_API_URL` defaults to `http://cube:4000` so the Formbricks app can reach both services inside the compose network. Override `HUB_DATABASE_URL` and `CUBEJS_DB_*` only if Hub or Cube should use a separate database. The Hub image tracks `:latest` by default so `formbricks.sh update` advances Hub in lockstep with the app. `hub` and `hub-migrate` always resolve to the same image. To pin to an immutable reference, set `HUB_IMAGE_REF` in `docker/.env` to either a tag (e.g. `:0.2.0`) or a digest (e.g. `@sha256:14db7b3d…`).
|
||||
- **Development** (`docker-compose.dev.yml`): Hub and Cube use the same local Postgres database. `HUB_API_KEY` defaults to `dev-api-key`, `CUBEJS_API_URL` defaults to `http://localhost:4000`, and `pnpm dev:setup` generates `CUBEJS_API_SECRET` in the repo root `.env`. The Hub image is pinned to a semver tag (`hub` and `hub-migrate` share the same value); override `HUB_IMAGE_TAG` in the repo root `.env` to test a specific Hub release.
|
||||
|
||||
In development, Hub is exposed locally on port **8080** and Cube on **4000** (with the Cube playground on **4001**). In production Docker Compose, Hub and Cube stay internal to the compose network and are reached via `http://hub:8080` and `http://cube:4000`.
|
||||
|
||||
@@ -262,8 +262,11 @@ services:
|
||||
<<: *environment
|
||||
|
||||
# Run Hub DB migrations (goose + river) before the API starts. Uses same image; migrations are idempotent.
|
||||
# Default tracks :latest so self-host updates (compose pull) advance Hub alongside the app image.
|
||||
# Operators who want an immutable pin can set HUB_IMAGE_REF in docker/.env to either ":<tag>"
|
||||
# (e.g. ":0.2.0") or "@sha256:<digest>". hub and hub-migrate share the same value — no drift.
|
||||
hub-migrate:
|
||||
image: ghcr.io/formbricks/hub:latest
|
||||
image: ghcr.io/formbricks/hub${HUB_IMAGE_REF:-:latest}
|
||||
restart: "no"
|
||||
entrypoint: ["sh", "-c"]
|
||||
command:
|
||||
@@ -279,7 +282,7 @@ services:
|
||||
# Formbricks Hub API (ghcr.io/formbricks/hub). Set HUB_API_KEY. By default shares the Formbricks database; set HUB_DATABASE_URL to use a separate DB.
|
||||
hub:
|
||||
restart: always
|
||||
image: ghcr.io/formbricks/hub:latest
|
||||
image: ghcr.io/formbricks/hub${HUB_IMAGE_REF:-:latest}
|
||||
depends_on:
|
||||
hub-migrate:
|
||||
condition: service_completed_successfully
|
||||
|
||||
@@ -6,22 +6,245 @@ icon: "arrow-right"
|
||||
|
||||
## v5
|
||||
|
||||
**Rate Limit**
|
||||
Formbricks v5 changes the self-hosted runtime contract. If you are upgrading an existing Formbricks 4.x
|
||||
deployment, review this section before starting the new version.
|
||||
|
||||
Formbricks v5 changes how rate limiting is enforced:
|
||||
### What Changes In v5
|
||||
|
||||
- several public and API-key routes are no longer rate-limited inside the application server
|
||||
- those routes are now expected to be protected by Envoy Gateway or an equivalent edge rate limiter
|
||||
- the remaining session-based routes, server actions, and uncovered APIs still use the in-app limiter
|
||||
- **Formbricks Hub is now mandatory** for self-hosted Formbricks v5 deployments.
|
||||
- **Edge rate limiting is now required** for specific public and API-key routes. Those routes are no longer
|
||||
throttled inside the application server.
|
||||
- **AI features are configured at the instance level** via `AI_*` environment variables.
|
||||
- **XM Suite v5 analytics depends on Cube.js**. The Docker and one-click stack bundle it, while Helm
|
||||
deployments still need a separate reachable Cube.js instance and `CUBEJS_API_SECRET`.
|
||||
|
||||
<Warning>
|
||||
If you self-host Formbricks without Envoy or another equivalent edge rate limiter, upgrade planning for v5
|
||||
must include new edge protection for the covered routes. Otherwise those routes will no longer be throttled
|
||||
by the application server after the upgrade.
|
||||
Formbricks v5 removes application-level rate limiting for several routes that are now expected to be
|
||||
protected by Envoy Gateway or an equivalent edge rate limiter. If your self-hosted instance does not
|
||||
already have equivalent edge protection, add it before exposing the v5 stack.
|
||||
</Warning>
|
||||
|
||||
See the [rate-limiting guide](/self-hosting/advanced/rate-limiting) for the exact covered route groups, thresholds,
|
||||
and the remaining app-enforced limits.
|
||||
### Before You Upgrade
|
||||
|
||||
Before you restart your instance on Formbricks v5:
|
||||
|
||||
- back up your database
|
||||
- identify your current deployment type: one-click, manual Docker Compose, or Kubernetes/Helm
|
||||
- confirm Redis/Valkey and your file storage setup are already healthy from your v4 baseline
|
||||
- identify whether file uploads use external S3-compatible storage or a legacy bundled MinIO service
|
||||
- decide whether this instance needs AI features, dashboards/analysis, or only core survey flows
|
||||
- verify whether you already run Envoy Gateway or another equivalent edge rate limiter for the covered routes
|
||||
|
||||
### Required Config And Infrastructure Changes
|
||||
|
||||
#### Formbricks Hub
|
||||
|
||||
Formbricks v5 expects Hub to be part of the self-hosted stack.
|
||||
|
||||
- `HUB_API_KEY` is required
|
||||
- `HUB_API_URL` must point to the Hub service the Formbricks app can reach
|
||||
- `HUB_DATABASE_URL` is optional; when unset, Hub can share the same PostgreSQL database as Formbricks
|
||||
- bundled Docker and Helm assets run Hub database migrations automatically during startup or upgrade, and the
|
||||
migration steps are idempotent
|
||||
|
||||
<Note>
|
||||
Hub-specific source code and standalone deployment assets live in the
|
||||
[Formbricks Hub repository](https://github.com/formbricks/hub). For a normal Formbricks v5 upgrade, use that
|
||||
repository as reference material only; the canonical migration steps stay in these Formbricks docs.
|
||||
</Note>
|
||||
|
||||
#### Edge Rate Limiting
|
||||
|
||||
Formbricks v5 splits rate limiting across two layers:
|
||||
|
||||
- Envoy Gateway, or an equivalent edge rate limiter, for covered public and API-key routes
|
||||
- the application server for the remaining session-authenticated routes, server actions, and uncovered APIs
|
||||
|
||||
Keep Redis/Valkey enabled for the remaining application-enforced limits. For the covered route groups and exact
|
||||
thresholds, use the [rate-limiting guide](/self-hosting/advanced/rate-limiting) as the source of truth.
|
||||
|
||||
#### AI Features
|
||||
|
||||
AI features are optional and only need instance-level configuration if you want to enable the related
|
||||
enterprise functionality.
|
||||
|
||||
- `AI_PROVIDER` and `AI_MODEL` are the base settings
|
||||
- `AI_PROVIDER=aws` requires `AI_AWS_REGION`, `AI_AWS_ACCESS_KEY_ID`, and `AI_AWS_SECRET_ACCESS_KEY`
|
||||
- `AI_PROVIDER=google` requires `AI_GOOGLE_CLOUD_PROJECT`, `AI_GOOGLE_CLOUD_LOCATION`, and either
|
||||
`AI_GOOGLE_CLOUD_CREDENTIALS_JSON` or `AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS`
|
||||
- `AI_PROVIDER=azure` requires `AI_AZURE_API_KEY` and either `AI_AZURE_BASE_URL` or
|
||||
`AI_AZURE_RESOURCE_NAME`
|
||||
|
||||
#### Cube.js Analytics
|
||||
|
||||
XM Suite v5 dashboard and analysis features require Cube.js.
|
||||
|
||||
- the Docker and one-click stack bundle the `cube` service and expect `CUBEJS_API_SECRET`
|
||||
- Helm deployments still need a separate reachable Cube.js instance
|
||||
- the Formbricks app expects `CUBEJS_API_URL` and `CUBEJS_API_SECRET`
|
||||
- if you run Cube yourself, you may also need to override `CUBEJS_DB_*` values for the Cube service
|
||||
|
||||
### Upgrade Steps By Deployment Type
|
||||
|
||||
**1. Back up your database**
|
||||
|
||||
<Tabs>
|
||||
<Tab title="One-Click">
|
||||
From the `formbricks` directory created by the installer:
|
||||
|
||||
```bash
|
||||
cd formbricks
|
||||
docker compose exec postgres pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v5_$(date +%Y%m%d_%H%M%S).dump
|
||||
```
|
||||
|
||||
<Info>
|
||||
If your PostgreSQL service name differs, run <code>docker compose ps</code> first and adjust the command.
|
||||
</Info>
|
||||
</Tab>
|
||||
<Tab title="Docker Compose">
|
||||
From the directory that contains your `docker-compose.yml`:
|
||||
|
||||
```bash
|
||||
docker compose exec postgres pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v5_$(date +%Y%m%d_%H%M%S).dump
|
||||
```
|
||||
|
||||
<Info>
|
||||
If your service is not named <code>postgres</code>, use <code>docker compose ps</code> to find the correct
|
||||
name.
|
||||
</Info>
|
||||
</Tab>
|
||||
<Tab title="Kubernetes">
|
||||
If you are using the in-cluster PostgreSQL released by the Helm chart:
|
||||
|
||||
```bash
|
||||
kubectl exec -n formbricks formbricks-postgresql-0 -- pg_dump -Fc -U formbricks -d formbricks > formbricks_pre_v5_$(date +%Y%m%d_%H%M%S).dump
|
||||
```
|
||||
|
||||
If you use a managed PostgreSQL service, create a provider snapshot or run `pg_dump` directly against the
|
||||
external host before continuing.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
**2. Update your deployment config and restart on v5**
|
||||
|
||||
<Tabs>
|
||||
<Tab title="One-Click">
|
||||
The one-click `./formbricks.sh update` command only pulls new images. It does **not** rewrite your existing
|
||||
`formbricks/docker-compose.yml`, so you must merge the v5 stack changes before the first v5 restart.
|
||||
|
||||
```bash
|
||||
curl -fsSL -o formbricks/docker-compose.v5.yml https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/docker-compose.yml
|
||||
```
|
||||
|
||||
Then compare `formbricks/docker-compose.v5.yml` with your existing `formbricks/docker-compose.yml` and merge
|
||||
the v5 additions:
|
||||
|
||||
- add a non-empty `HUB_API_KEY` and reuse the same value wherever your deployment resolves Hub auth
|
||||
- keep `HUB_API_URL` at `http://hub:8080` unless Hub runs elsewhere
|
||||
- include the bundled `hub-migrate` and `hub` services
|
||||
- if you use the bundled XM Suite v5 analytics stack, sync `formbricks/cube/cube.js` and
|
||||
`formbricks/cube/schema/FeedbackRecords.js` from the current release and ensure
|
||||
`formbricks/.env` contains `CUBEJS_API_SECRET`
|
||||
- if your older setup still uses bundled MinIO for uploads, review that storage path separately before the
|
||||
first v5 restart; newer self-hosting updates move the bundled object-storage path to RustFS, while
|
||||
external S3-compatible storage keeps the same `S3_*` app contract
|
||||
- add any `AI_*` variables you need
|
||||
- if you do not run the bundled Docker analytics path, point `CUBEJS_API_URL` at your external Cube.js
|
||||
instance and provide the matching `CUBEJS_API_SECRET`
|
||||
|
||||
After the compose file is updated and your edge rate limiter is in place:
|
||||
|
||||
```bash
|
||||
./formbricks.sh update
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Docker Compose">
|
||||
Pull the current production compose file and merge the v5 changes into your existing deployment config before
|
||||
only updating images:
|
||||
|
||||
```bash
|
||||
curl -fsSL -o docker-compose.v5.yml https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/docker-compose.yml
|
||||
```
|
||||
|
||||
At minimum, confirm:
|
||||
|
||||
- `HUB_API_KEY` is configured and the same value is available wherever your deployment resolves Hub auth
|
||||
- `HUB_API_URL` points to the Hub service the app can reach
|
||||
- the compose stack includes `hub-migrate` and `hub`
|
||||
- the XM Suite v5 Docker stack also includes `cube`, `cube/cube.js`, and
|
||||
`cube/schema/FeedbackRecords.js`, with `CUBEJS_API_SECRET` available through your `.env` or shell
|
||||
environment
|
||||
- if your legacy Compose file still includes bundled MinIO for uploads, treat that as a separate storage
|
||||
review when comparing files; newer bundled storage guidance uses RustFS, while external S3-compatible
|
||||
storage keeps the same `S3_*` app contract
|
||||
- any `AI_*` variables you need are set
|
||||
- if you override the bundled analytics path, point `CUBEJS_API_URL` at your external Cube.js instance and
|
||||
supply the matching `CUBEJS_API_SECRET`
|
||||
|
||||
Then restart the stack:
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
<Info>
|
||||
The XM Suite v5 Docker Compose stack bundles Hub and Cube.js. Keep the bundled `cube/` config files in
|
||||
sync with `docker-compose.yml` when you update this path.
|
||||
</Info>
|
||||
</Tab>
|
||||
<Tab title="Kubernetes">
|
||||
Upgrade using the current Formbricks chart:
|
||||
|
||||
```bash
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \
|
||||
-n formbricks \
|
||||
-f values.yaml
|
||||
```
|
||||
|
||||
Before running the upgrade, confirm these v5 expectations in your values:
|
||||
|
||||
- keep `hub.enabled=true`; Hub is mandatory in v5
|
||||
- if you want the bundled Envoy path, configure `envoy.enabled=true` and then choose:
|
||||
- `envoy.controller.enabled=true` for the bundled controller mode
|
||||
- `envoy.controller.enabled=false` when the cluster already has a compatible Envoy Gateway controller
|
||||
- if you use bundled Envoy rate limiting, enable a dedicated backend with `envoyRedis.enabled=true`
|
||||
- if you already have an equivalent edge rate limiter outside the chart, keep that protection in place
|
||||
- if the instance needs XM Suite v5 analytics or dashboards, provide `CUBEJS_API_URL` and
|
||||
`CUBEJS_API_SECRET` for the external Cube.js deployment
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Post-Upgrade Verification
|
||||
|
||||
After the upgrade:
|
||||
|
||||
- confirm the Formbricks app starts and `GET /health` returns successfully
|
||||
- confirm the Hub service is healthy and reachable from the Formbricks app
|
||||
- verify any Hub-backed connector or feedback flows you use
|
||||
- verify covered routes are rate-limited at the edge layer
|
||||
- verify AI features only if you configured the required `AI_*` variables
|
||||
- verify dashboards and analysis flows only if your deployment path includes Cube.js or points to an external
|
||||
Cube.js instance
|
||||
|
||||
### Troubleshooting And Rollback
|
||||
|
||||
Common upgrade issues:
|
||||
|
||||
- **Missing `HUB_API_KEY`**: Formbricks cannot authenticate to Hub, and Hub itself may fail to start
|
||||
- **Bad `HUB_API_URL`**: the app starts, but Hub-backed features fail because the service cannot be reached
|
||||
- **No edge rate limiter in front of covered routes**: the v5 deployment runs, but those routes are no longer
|
||||
protected by the legacy in-app limiter
|
||||
- **Missing AI credentials**: AI features remain unavailable until `AI_PROVIDER`, `AI_MODEL`, and the matching
|
||||
provider credentials are set correctly
|
||||
- **Cube not configured**: dashboards or analysis queries fail even though the core Formbricks app is healthy
|
||||
|
||||
If you need to roll back:
|
||||
|
||||
- restore the pre-upgrade database backup if schema or data changes require it
|
||||
- revert your image tags or Helm release values to the last known-good Formbricks 4.x version
|
||||
- revert compose or Helm configuration changes introduced for the v5 rollout
|
||||
|
||||
**Workspaces and Environment IDs**
|
||||
|
||||
@@ -1372,7 +1595,8 @@ For a seamless migration, below is a shell script for your self-hosted instance
|
||||
### Docker & Single Script Setup
|
||||
|
||||
Now that these variables can be defined at runtime, you can append them inside your `x-environment` in the `docker-compose.yml` itself.
|
||||
For a more detailed guide on these environment variables, please refer to the [Important Runtime Variables](/self-hosting/setup/docker#important-run-time-variables) section.
|
||||
For a more detailed guide on these environment variables, please refer to the
|
||||
[Environment Variables](/self-hosting/configuration/environment-variables) page.
|
||||
|
||||
```yaml docker-compose.yml
|
||||
version: "3.3"
|
||||
|
||||
@@ -18,6 +18,11 @@ Starting with Formbricks v5, rate limiting is split across two layers:
|
||||
those routes will no longer be throttled by the application server after upgrading.
|
||||
</Warning>
|
||||
|
||||
<Info>
|
||||
For the full self-hosted upgrade checklist, use this page together with the
|
||||
[v5 migration guide](/self-hosting/advanced/migration#v5).
|
||||
</Info>
|
||||
|
||||
Rate limits are scoped by identifier, depending on the endpoint and enforcement layer:
|
||||
|
||||
- IP hash (for unauthenticated/client-side routes and public actions)
|
||||
@@ -36,7 +41,7 @@ Before upgrading to Formbricks v5:
|
||||
- expect covered routes to emit gateway `429`s instead of the legacy app JSON `429`s
|
||||
|
||||
For the current source of truth on covered routes and thresholds, use this page together with your deployment
|
||||
configuration.
|
||||
configuration and the [v5 migration guide](/self-hosting/advanced/migration#v5).
|
||||
|
||||
## Envoy-Managed Limits
|
||||
|
||||
|
||||
@@ -8,102 +8,108 @@ icon: "code"
|
||||
|
||||
These variables are present inside your machine's docker-compose file. Restart the docker containers if you change any variables for them to take effect.
|
||||
|
||||
<Note>
|
||||
Upgrading from Formbricks 4.x to 5.0? Read the [migration guide](/self-hosting/advanced/migration#v5) first.
|
||||
Formbricks v5 makes Hub part of the standard self-hosted runtime and changes how rate limiting is enforced.
|
||||
</Note>
|
||||
|
||||
For `AI_PROVIDER=google`, use a Gemini model ID such as `gemini-2.5-flash` together with Google Cloud credentials. Formbricks uses Google Cloud naming here, even though the underlying SDK still talks to Vertex AI endpoints for Gemini model access.
|
||||
|
||||
| Variable | Description | Required | Default |
|
||||
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| WEBAPP_URL | Base URL of the site. | required | http://localhost:3000 |
|
||||
| PUBLIC_URL | Base URL for the public domain where surveys and public-facing content are served. If not set, uses WEBAPP_URL. | optional | WEBAPP_URL |
|
||||
| NEXTAUTH_URL | Location of the auth server. This should normally be the same as WEBAPP_URL | required | http://localhost:3000 |
|
||||
| DATABASE_URL | Database URL with credentials. | required | |
|
||||
| NEXTAUTH_SECRET | Secret for NextAuth, used for session signing and encryption. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) |
|
||||
| ENCRYPTION_KEY | Secret used by Formbricks for data encryption and audit log hashing. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) |
|
||||
| CRON_SECRET | API Secret for running cron jobs. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) |
|
||||
| LOG_LEVEL | Minimum log level (debug, info, warn, error, fatal) | optional | info |
|
||||
| S3_ACCESS_KEY | Access key for S3. | optional | (resolved by the AWS SDK) |
|
||||
| S3_SECRET_KEY | Secret key for S3. | optional | (resolved by the AWS SDK) |
|
||||
| S3_REGION | Region for S3. | optional | (resolved by the AWS SDK) |
|
||||
| S3_BUCKET_NAME | S3 bucket name for data storage. Formbricks enables S3 storage when this is set. | optional (required if S3 is enabled) | |
|
||||
| S3_ENDPOINT_URL | Endpoint for S3. | optional | (resolved by the AWS SDK) |
|
||||
| SAML_DATABASE_URL | Database URL for SAML. | optional | postgres://postgres:@localhost:5432/formbricks-saml |
|
||||
| PRIVACY_URL | URL for privacy policy. | optional | |
|
||||
| TERMS_URL | URL for terms of service. | optional | |
|
||||
| IMPRINT_URL | URL for imprint. | optional | |
|
||||
| IMPRINT_ADDRESS | Address for imprint. | optional | |
|
||||
| EMAIL_AUTH_DISABLED | Disables the ability for users to signup or login via email and password if set to 1. | optional | |
|
||||
| PASSWORD_RESET_DISABLED | Disables password reset functionality if set to 1. | optional | |
|
||||
| PASSWORD_RESET_TOKEN_LIFETIME_MINUTES | Configures how long password reset links remain valid in minutes. Accepted values are integers from 5 to 120. | optional | 30 |
|
||||
| EMAIL_VERIFICATION_DISABLED | Disables email verification if set to 1. | optional | |
|
||||
| RATE_LIMITING_DISABLED | Disables rate limiting if set to 1. | optional | |
|
||||
| TELEMETRY_DISABLED | Disables telemetry reporting if set to 1. Ignored when an Enterprise License is active. | optional | |
|
||||
| DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS | Allows webhook URLs to point to internal/private network addresses (e.g. localhost, 192.168.x.x) if set to 1. Useful for self-hosted instances that need to send webhooks to internal services. | optional | |
|
||||
| INVITE_DISABLED | Disables the ability for invited users to create an account if set to 1. | optional | |
|
||||
| MAIL_FROM | Email address to send emails from. | optional (required if email services are to be enabled) | |
|
||||
| MAIL_FROM_NAME | Email name/title to send emails from. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_HOST | Host URL of your SMTP server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_PORT | Host Port of your SMTP server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_USER | Username for your SMTP Server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_PASSWORD | Password for your SMTP Server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_AUTHENTICATED | If set to 0, the server will not require SMTP_USER and SMTP_PASSWORD(default is 1) | optional | |
|
||||
| SMTP_SECURE_ENABLED | SMTP secure connection. For using TLS, set to 1 else to 0. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_REJECT_UNAUTHORIZED_TLS | If set to 0, the server will accept connections without requiring authorization from the list of supplied CAs. | optional | 1 |
|
||||
| TURNSTILE_SITE_KEY | Site key for Turnstile. | optional | |
|
||||
| TURNSTILE_SECRET_KEY | Secret key for Turnstile. | optional | |
|
||||
| RECAPTCHA_SITE_KEY | Site key for survey responses recaptcha bot protection | optional | |
|
||||
| RECAPTCHA_SECRET_KEY | Secret key for recaptcha bot protection. | optional | |
|
||||
| GITHUB_ID | Client ID for GitHub. | optional (required if GitHub auth is enabled) | |
|
||||
| GITHUB_SECRET | Secret for GitHub. | optional (required if GitHub auth is enabled) | |
|
||||
| GOOGLE_CLIENT_ID | Client ID for Google. | optional (required if Google auth is enabled) | |
|
||||
| GOOGLE_CLIENT_SECRET | Secret for Google. | optional (required if Google auth is enabled) | |
|
||||
| AI_PROVIDER | Instance-level AI provider used in the background. Supported values: `aws`, `google`, `azure`. | optional (required if AI is enabled) | |
|
||||
| AI_MODEL | Instance-level AI model or deployment name used by the active provider. | optional (required if `AI_PROVIDER` is set) | |
|
||||
| AI_GOOGLE_CLOUD_PROJECT | Google Cloud project ID for the `google` AI provider. | optional (required if `AI_PROVIDER=google`) | |
|
||||
| AI_GOOGLE_CLOUD_LOCATION | Google Cloud location for `google` AI requests. | optional (required if `AI_PROVIDER=google`) | |
|
||||
| AI_GOOGLE_CLOUD_CREDENTIALS_JSON | Service account credentials JSON for the `google` AI provider. | optional (one of this or `AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS` required if `AI_PROVIDER=google`) | |
|
||||
| AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS | Path to Google Application Default Credentials used by the `google` AI provider. | optional (one of this or `AI_GOOGLE_CLOUD_CREDENTIALS_JSON` required if `AI_PROVIDER=google`) | |
|
||||
| AI_AWS_REGION | AWS region for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | |
|
||||
| AI_AWS_ACCESS_KEY_ID | AWS access key ID for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | |
|
||||
| AI_AWS_SECRET_ACCESS_KEY | AWS secret access key for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | |
|
||||
| AI_AWS_SESSION_TOKEN | AWS session token for Amazon Bedrock temporary credentials. | optional | |
|
||||
| AI_AZURE_BASE_URL | Azure OpenAI / Foundry base URL. When set, this is preferred over `AI_AZURE_RESOURCE_NAME`. | optional | |
|
||||
| AI_AZURE_RESOURCE_NAME | Azure resource name used to assemble the Azure OpenAI URL. | optional | |
|
||||
| AI_AZURE_API_KEY | API key for Azure OpenAI / Foundry. | optional (required if `AI_PROVIDER=azure`) | |
|
||||
| AI_AZURE_API_VERSION | Azure API version for OpenAI-compatible calls. | optional | v1 |
|
||||
| STRIPE_SECRET_KEY | Secret key for Stripe integration. | optional | |
|
||||
| STRIPE_WEBHOOK_SECRET | Webhook secret for Stripe integration. | optional | |
|
||||
| DEFAULT_BRAND_COLOR | Default brand color for your app (Can be overwritten from the UI as well). | optional | #64748b |
|
||||
| DEFAULT_ORGANIZATION_ID | Automatically assign new users to a specific organization when joining | optional | |
|
||||
| OIDC_DISPLAY_NAME | Display name for Custom OpenID Connect Provider | optional | |
|
||||
| OIDC_CLIENT_ID | Client ID for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | |
|
||||
| OIDC_CLIENT_SECRET | Secret for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | |
|
||||
| OIDC_ISSUER | Issuer URL for Custom OpenID Connect Provider (should have .well-known configured at this) | optional (required if OIDC auth is enabled) | |
|
||||
| OIDC_SIGNING_ALGORITHM | Signing Algorithm for Custom OpenID Connect Provider | optional | RS256 |
|
||||
| OTEL_EXPORTER_OTLP_ENDPOINT | Base OTLP HTTP endpoint for traces and metrics export (e.g. http://collector:4318). | optional | |
|
||||
| OTEL_EXPORTER_OTLP_PROTOCOL | OTLP protocol to use for export. | optional | http/protobuf |
|
||||
| OTEL_SERVICE_NAME | Service name reported in OpenTelemetry resource attributes. | optional | formbricks |
|
||||
| OTEL_RESOURCE_ATTRIBUTES | Comma-separated resource attributes in OTel format (`key=value,key2=value2`). | optional | |
|
||||
| OTEL_TRACES_SAMPLER | Trace sampler strategy (`always_on`, `always_off`, `traceidratio`, `parentbased_traceidratio`). | optional | always_on |
|
||||
| OTEL_TRACES_SAMPLER_ARG | Sampling argument used by ratio-based samplers (`0` to `1`). | optional | |
|
||||
| PROMETHEUS_ENABLED | Enables Prometheus metrics if set to 1. | optional | |
|
||||
| PROMETHEUS_EXPORTER_PORT | Port for Prometheus metrics. | optional | 9090 |
|
||||
| DEFAULT_TEAM_ID | Default team ID for new users. | optional | |
|
||||
| SENTRY_DSN | Set this to track errors and monitor performance in Sentry. | optional | |
|
||||
| SENTRY_ENVIRONMENT | Set this to identify the environment in Sentry | optional | |
|
||||
| SENTRY_AUTH_TOKEN | Set this if you want to make errors more readable in Sentry. | optional | |
|
||||
| SESSION_MAX_AGE | Configure the maximum age for the session in seconds. | optional | 86400 (24 hours) |
|
||||
| USER_MANAGEMENT_MINIMUM_ROLE | Set this to control which roles can access user management features. Accepted values: "owner", "manager", "disabled" | optional | manager |
|
||||
| REDIS_URL | Redis URL for caching, rate limiting, and audit logging. Application will not start without this. | required | redis://localhost:6379 |
|
||||
| AUDIT_LOG_ENABLED | Set this to 1 to enable audit logging. Requires Redis to be configured with the REDIS_URL env variable. | optional | 0 |
|
||||
| AUDIT_LOG_GET_USER_IP | Set to 1 to include user IP addresses in audit logs from request headers | optional | 0 |
|
||||
| Variable | Description | Required | Default |
|
||||
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| WEBAPP_URL | Base URL of the site. | required | http://localhost:3000 |
|
||||
| PUBLIC_URL | Base URL for the public domain where surveys and public-facing content are served. If not set, uses WEBAPP_URL. | optional | WEBAPP_URL |
|
||||
| NEXTAUTH_URL | Location of the auth server. This should normally be the same as WEBAPP_URL | required | http://localhost:3000 |
|
||||
| DATABASE_URL | Database URL with credentials. | required | |
|
||||
| NEXTAUTH_SECRET | Secret for NextAuth, used for session signing and encryption. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) |
|
||||
| ENCRYPTION_KEY | Secret used by Formbricks for data encryption and audit log hashing. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) |
|
||||
| CRON_SECRET | API Secret for running cron jobs. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) |
|
||||
| LOG_LEVEL | Minimum log level (debug, info, warn, error, fatal) | optional | info |
|
||||
| S3_ACCESS_KEY | Access key for S3. | optional | (resolved by the AWS SDK) |
|
||||
| S3_SECRET_KEY | Secret key for S3. | optional | (resolved by the AWS SDK) |
|
||||
| S3_REGION | Region for S3. | optional | (resolved by the AWS SDK) |
|
||||
| S3_BUCKET_NAME | S3 bucket name for data storage. Formbricks enables S3 storage when this is set. | optional (required if S3 is enabled) | |
|
||||
| S3_ENDPOINT_URL | Endpoint for S3. | optional | (resolved by the AWS SDK) |
|
||||
| SAML_DATABASE_URL | Database URL for SAML. | optional | postgres://postgres:@localhost:5432/formbricks-saml |
|
||||
| PRIVACY_URL | URL for privacy policy. | optional | |
|
||||
| TERMS_URL | URL for terms of service. | optional | |
|
||||
| IMPRINT_URL | URL for imprint. | optional | |
|
||||
| IMPRINT_ADDRESS | Address for imprint. | optional | |
|
||||
| EMAIL_AUTH_DISABLED | Disables the ability for users to signup or login via email and password if set to 1. | optional | |
|
||||
| PASSWORD_RESET_DISABLED | Disables password reset functionality if set to 1. | optional | |
|
||||
| PASSWORD_RESET_TOKEN_LIFETIME_MINUTES | Configures how long password reset links remain valid in minutes. Accepted values are integers from 5 to 120. | optional | 30 |
|
||||
| EMAIL_VERIFICATION_DISABLED | Disables email verification if set to 1. | optional | |
|
||||
| RATE_LIMITING_DISABLED | Disables only the application-level rate limiter if set to 1. It does not disable Envoy or an equivalent edge rate limiter. | optional | |
|
||||
| TELEMETRY_DISABLED | Disables telemetry reporting if set to 1. Ignored when an Enterprise License is active. | optional | |
|
||||
| DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS | Allows webhook URLs to point to internal/private network addresses (e.g. localhost, 192.168.x.x) if set to 1. Useful for self-hosted instances that need to send webhooks to internal services. | optional | |
|
||||
| INVITE_DISABLED | Disables the ability for invited users to create an account if set to 1. | optional | |
|
||||
| MAIL_FROM | Email address to send emails from. | optional (required if email services are to be enabled) | |
|
||||
| MAIL_FROM_NAME | Email name/title to send emails from. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_HOST | Host URL of your SMTP server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_PORT | Host Port of your SMTP server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_USER | Username for your SMTP Server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_PASSWORD | Password for your SMTP Server. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_AUTHENTICATED | If set to 0, the server will not require SMTP_USER and SMTP_PASSWORD(default is 1) | optional | |
|
||||
| SMTP_SECURE_ENABLED | SMTP secure connection. For using TLS, set to 1 else to 0. | optional (required if email services are to be enabled) | |
|
||||
| SMTP_REJECT_UNAUTHORIZED_TLS | If set to 0, the server will accept connections without requiring authorization from the list of supplied CAs. | optional | 1 |
|
||||
| TURNSTILE_SITE_KEY | Site key for Turnstile. | optional | |
|
||||
| TURNSTILE_SECRET_KEY | Secret key for Turnstile. | optional | |
|
||||
| RECAPTCHA_SITE_KEY | Site key for survey responses recaptcha bot protection | optional | |
|
||||
| RECAPTCHA_SECRET_KEY | Secret key for recaptcha bot protection. | optional | |
|
||||
| GITHUB_ID | Client ID for GitHub. | optional (required if GitHub auth is enabled) | |
|
||||
| GITHUB_SECRET | Secret for GitHub. | optional (required if GitHub auth is enabled) | |
|
||||
| GOOGLE_CLIENT_ID | Client ID for Google. | optional (required if Google auth is enabled) | |
|
||||
| GOOGLE_CLIENT_SECRET | Secret for Google. | optional (required if Google auth is enabled) | |
|
||||
| AI_PROVIDER | Instance-level AI provider used in the background. Supported values: `aws`, `google`, `azure`. | optional (required if AI is enabled) | |
|
||||
| AI_MODEL | Instance-level AI model or deployment name used by the active provider. | optional (required if `AI_PROVIDER` is set) | |
|
||||
| AI_GOOGLE_CLOUD_PROJECT | Google Cloud project ID for the `google` AI provider. | optional (required if `AI_PROVIDER=google`) | |
|
||||
| AI_GOOGLE_CLOUD_LOCATION | Google Cloud location for `google` AI requests. | optional (required if `AI_PROVIDER=google`) | |
|
||||
| AI_GOOGLE_CLOUD_CREDENTIALS_JSON | Service account credentials JSON for the `google` AI provider. | optional (one of this or `AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS` required if `AI_PROVIDER=google`) | |
|
||||
| AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS | Path to Google Application Default Credentials used by the `google` AI provider. | optional (one of this or `AI_GOOGLE_CLOUD_CREDENTIALS_JSON` required if `AI_PROVIDER=google`) | |
|
||||
| AI_AWS_REGION | AWS region for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | |
|
||||
| AI_AWS_ACCESS_KEY_ID | AWS access key ID for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | |
|
||||
| AI_AWS_SECRET_ACCESS_KEY | AWS secret access key for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | |
|
||||
| AI_AWS_SESSION_TOKEN | AWS session token for Amazon Bedrock temporary credentials. | optional | |
|
||||
| AI_AZURE_BASE_URL | Azure OpenAI / Foundry base URL. When set, this is preferred over `AI_AZURE_RESOURCE_NAME`. | optional (one of this or `AI_AZURE_RESOURCE_NAME` required if `AI_PROVIDER=azure`) | |
|
||||
| AI_AZURE_RESOURCE_NAME | Azure resource name used to assemble the Azure OpenAI URL. | optional (one of this or `AI_AZURE_BASE_URL` required if `AI_PROVIDER=azure`) | |
|
||||
| AI_AZURE_API_KEY | API key for Azure OpenAI / Foundry. | optional (required if `AI_PROVIDER=azure`) | |
|
||||
| AI_AZURE_API_VERSION | Azure API version for OpenAI-compatible calls. | optional | v1 |
|
||||
| STRIPE_SECRET_KEY | Secret key for Stripe integration. | optional | |
|
||||
| STRIPE_WEBHOOK_SECRET | Webhook secret for Stripe integration. | optional | |
|
||||
| DEFAULT_BRAND_COLOR | Default brand color for your app (Can be overwritten from the UI as well). | optional | #64748b |
|
||||
| DEFAULT_ORGANIZATION_ID | Automatically assign new users to a specific organization when joining | optional | |
|
||||
| OIDC_DISPLAY_NAME | Display name for Custom OpenID Connect Provider | optional | |
|
||||
| OIDC_CLIENT_ID | Client ID for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | |
|
||||
| OIDC_CLIENT_SECRET | Secret for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | |
|
||||
| OIDC_ISSUER | Issuer URL for Custom OpenID Connect Provider (should have .well-known configured at this) | optional (required if OIDC auth is enabled) | |
|
||||
| OIDC_SIGNING_ALGORITHM | Signing Algorithm for Custom OpenID Connect Provider | optional | RS256 |
|
||||
| OTEL_EXPORTER_OTLP_ENDPOINT | Base OTLP HTTP endpoint for traces and metrics export (e.g. http://collector:4318). | optional | |
|
||||
| OTEL_EXPORTER_OTLP_PROTOCOL | OTLP protocol to use for export. | optional | http/protobuf |
|
||||
| OTEL_SERVICE_NAME | Service name reported in OpenTelemetry resource attributes. | optional | formbricks |
|
||||
| OTEL_RESOURCE_ATTRIBUTES | Comma-separated resource attributes in OTel format (`key=value,key2=value2`). | optional | |
|
||||
| OTEL_TRACES_SAMPLER | Trace sampler strategy (`always_on`, `always_off`, `traceidratio`, `parentbased_traceidratio`). | optional | always_on |
|
||||
| OTEL_TRACES_SAMPLER_ARG | Sampling argument used by ratio-based samplers (`0` to `1`). | optional | |
|
||||
| PROMETHEUS_ENABLED | Enables Prometheus metrics if set to 1. | optional | |
|
||||
| PROMETHEUS_EXPORTER_PORT | Port for Prometheus metrics. | optional | 9090 |
|
||||
| DEFAULT_TEAM_ID | Default team ID for new users. | optional | |
|
||||
| SENTRY_DSN | Set this to track errors and monitor performance in Sentry. | optional | |
|
||||
| SENTRY_ENVIRONMENT | Set this to identify the environment in Sentry | optional | |
|
||||
| SENTRY_AUTH_TOKEN | Set this if you want to make errors more readable in Sentry. | optional | |
|
||||
| SESSION_MAX_AGE | Configure the maximum age for the session in seconds. | optional | 86400 (24 hours) |
|
||||
| USER_MANAGEMENT_MINIMUM_ROLE | Set this to control which roles can access user management features. Accepted values: "owner", "manager", "disabled" | optional | manager |
|
||||
| REDIS_URL | Redis URL for caching, rate limiting, and audit logging. Application will not start without this. | required | redis://localhost:6379 |
|
||||
| AUDIT_LOG_ENABLED | Set this to 1 to enable audit logging. Requires Redis to be configured with the REDIS_URL env variable. | optional | 0 |
|
||||
| AUDIT_LOG_GET_USER_IP | Set to 1 to include user IP addresses in audit logs from request headers | optional | 0 |
|
||||
|
||||
#### Formbricks Hub
|
||||
|
||||
When running the stack with [Formbricks Hub](https://github.com/formbricks/hub) (for example via Docker Compose or Helm), the following variables apply:
|
||||
Starting with Formbricks v5, Hub is part of the standard self-hosted runtime. When you run Formbricks with the
|
||||
bundled Docker Compose or Helm assets, the following variables apply:
|
||||
|
||||
| Variable | Description | Required | Default |
|
||||
| ---------------- | ---------------------------------------------------------------------------------- | -------- | --------------------------------------------------- |
|
||||
| HUB_API_KEY | API key used by the Formbricks Hub API (port 8080). | required | (e.g. `openssl rand -hex 32`) |
|
||||
| HUB_API_URL | Base URL the Formbricks app uses to call Hub. Use `http://localhost:8080` locally. | required | `http://localhost:8080` in local dev |
|
||||
| HUB_API_KEY | API key used by the Formbricks Hub API. Generate a strong secret and use the same value wherever your deployment supplies Hub auth configuration. | required | (e.g. `openssl rand -hex 32`) |
|
||||
| HUB_API_URL | Base URL the Formbricks app uses to call Hub. With the bundled Docker stack, keep this at `http://hub:8080` unless Hub runs elsewhere. | required | `http://hub:8080` (bundled Docker), `http://localhost:8080` (local dev) |
|
||||
| HUB_DATABASE_URL | PostgreSQL connection URL for Hub. Omit to use the same database as Formbricks. | optional | Same as Formbricks `DATABASE_URL` (shared database) |
|
||||
|
||||
#### Cube.js Analytics for XM Suite v5
|
||||
@@ -113,8 +119,8 @@ Cube JWT from `CUBEJS_API_SECRET`, so `CUBEJS_API_TOKEN` is not part of the supp
|
||||
|
||||
| Variable | Description | Required | Default |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------ |
|
||||
| CUBEJS_API_URL | Base URL the Formbricks app uses to call Cube. Use `http://localhost:4000` locally. | required for XM Suite v5 analytics | `http://localhost:4000` in local dev |
|
||||
| CUBEJS_API_SECRET | Shared secret Formbricks uses to sign Cube API JWTs. Generate with `openssl rand -hex 32`. | required for XM Suite v5 analytics | |
|
||||
| CUBEJS_API_URL | Base URL the Formbricks app uses to call Cube. Use `http://localhost:4000` locally. | required for XM Suite v5 analytics | `http://localhost:4000` in local dev |
|
||||
| CUBEJS_API_SECRET | Shared secret Formbricks uses to sign Cube API JWTs. Generate with `openssl rand -hex 32`. | required for XM Suite v5 analytics | |
|
||||
| CUBEJS_DB_HOST | Database host for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment |
|
||||
| CUBEJS_DB_PORT | Database port for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment |
|
||||
| CUBEJS_DB_NAME | Database name for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment |
|
||||
|
||||
@@ -1,197 +1,162 @@
|
||||
---
|
||||
title: "Cluster Setup"
|
||||
description: "How to set up Formbricks in a High-Availability Cluster"
|
||||
description: "Run Formbricks in a high-availability cluster."
|
||||
icon: "circle-nodes"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Running Formbricks as a cluster of multiple instances offers several key advantages:
|
||||
Running Formbricks as a cluster of multiple instances gives you:
|
||||
|
||||
- **High Availability**: Ensure your surveys remain accessible even if some instances fail
|
||||
|
||||
- **Load Distribution**: Handle higher traffic by distributing requests across multiple instances
|
||||
|
||||
- **Scalability**: Easily scale horizontally by adding more instances as your needs grow
|
||||
|
||||
- **Zero-Downtime Updates**: Rolling updates without service interruption
|
||||
- **High Availability**: surveys remain accessible even if one app pod becomes unavailable
|
||||
- **Load Distribution**: traffic can be spread across multiple stateless Formbricks app instances
|
||||
- **Scalability**: you can scale app replicas horizontally as usage grows
|
||||
- **Zero-Downtime Updates**: rolling deployments are possible with the right orchestration setup
|
||||
|
||||
## Requirements
|
||||
|
||||
To run Formbricks in a cluster setup, you'll need:
|
||||
For a Formbricks v5 cluster setup, plan for:
|
||||
|
||||
- Shared PostgreSQL database
|
||||
|
||||
- Shared Redis cache for session management and caching
|
||||
|
||||
- Load balancer to distribute traffic
|
||||
- shared PostgreSQL for Formbricks and Hub, with `pgvector` support if Hub shares the same database
|
||||
- shared Redis/Valkey for caching, rate limiting, and audit-related flows
|
||||
- shared S3-compatible storage if you use file uploads or organization branding assets
|
||||
- a load balancer or ingress layer in front of the app
|
||||
- Formbricks Hub as part of the runtime
|
||||
- Envoy Gateway or an equivalent external edge rate limiter for the v5-covered public and API-key routes
|
||||
|
||||
## Architecture
|
||||
|
||||
The Formbricks cluster setup consists of multiple components working together to provide a scalable and highly available system. Here's a detailed overview of the architecture:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Load Balancer
|
||||
LB[Load Balancer/Ingress]
|
||||
subgraph Edge
|
||||
LB["Load Balancer / Ingress"]
|
||||
RL["Envoy Or Equivalent Edge Rate Limiter"]
|
||||
end
|
||||
|
||||
subgraph Formbricks Cluster
|
||||
FB1[Formbricks Instance 1]
|
||||
FB2[Formbricks Instance 2]
|
||||
FB3[Formbricks Instance n]
|
||||
FB1["Formbricks App 1"]
|
||||
FB2["Formbricks App 2"]
|
||||
FB3["Formbricks App n"]
|
||||
HUB["Formbricks Hub"]
|
||||
end
|
||||
|
||||
subgraph Data Storage
|
||||
subgraph PostgreSQL HA
|
||||
PSQL_P[(PostgreSQL Primary)]
|
||||
PSQL_R[(PostgreSQL Replica)]
|
||||
end
|
||||
|
||||
subgraph Redis Cluster
|
||||
RC_P[(Redis Primary)]
|
||||
RC_R[(Redis Replica)]
|
||||
end
|
||||
|
||||
S3[S3 Compatible Storage]
|
||||
PSQL["PostgreSQL"]
|
||||
REDIS["Redis / Valkey"]
|
||||
S3["S3 Compatible Storage"]
|
||||
end
|
||||
|
||||
%% Connections
|
||||
LB --> FB1
|
||||
LB --> FB2
|
||||
LB --> FB3
|
||||
LB --> RL
|
||||
RL --> FB1
|
||||
RL --> FB2
|
||||
RL --> FB3
|
||||
|
||||
FB1 --> PSQL_P
|
||||
FB2 --> PSQL_P
|
||||
FB3 --> PSQL_P
|
||||
PSQL_P --> PSQL_R
|
||||
FB1 --> PSQL
|
||||
FB2 --> PSQL
|
||||
FB3 --> PSQL
|
||||
HUB --> PSQL
|
||||
|
||||
FB1 --> RC_P
|
||||
FB2 --> RC_P
|
||||
FB3 --> RC_P
|
||||
RC_P --> RC_R
|
||||
FB1 --> REDIS
|
||||
FB2 --> REDIS
|
||||
FB3 --> REDIS
|
||||
|
||||
FB1 --> HUB
|
||||
FB2 --> HUB
|
||||
FB3 --> HUB
|
||||
|
||||
FB1 --> S3
|
||||
FB2 --> S3
|
||||
FB3 --> S3
|
||||
|
||||
style PSQL_P fill:#00C4B8,color:#ffffff
|
||||
style PSQL_R fill:#00C4B8,color:#ffffff
|
||||
style RC_P fill:#FF6B6B,color:#ffffff
|
||||
style RC_R fill:#FF6B6B,color:#ffffff
|
||||
style S3 fill:#FFA94D,color:#ffffff
|
||||
style FB1,FB2,FB3 fill:#0D9373,color:#ffffff
|
||||
style LB fill:#4C6EF5,color:#ffffff
|
||||
```
|
||||
|
||||
### Component Description
|
||||
|
||||
1. **Formbricks Cluster**
|
||||
1. **Formbricks App Replicas**
|
||||
|
||||
- Multiple Formbricks instances (1..n) running in parallel
|
||||
- Each instance is stateless and can handle any incoming request
|
||||
- Automatic failover if any instance becomes unavailable
|
||||
- stateless application instances that serve the UI, APIs, and survey flows
|
||||
- can be scaled horizontally behind a load balancer
|
||||
|
||||
2. **PostgreSQL Database**
|
||||
2. **Formbricks Hub**
|
||||
|
||||
- Primary database storing all survey, response, and contact data
|
||||
- Optional high-availability setup with primary-replica configuration
|
||||
- Handles all persistent data storage needs
|
||||
- required in Formbricks v5
|
||||
- stores and serves Hub-backed feedback record data
|
||||
- can share the same PostgreSQL database as the main app when configured that way
|
||||
|
||||
3. **Redis Cluster**
|
||||
3. **PostgreSQL**
|
||||
|
||||
- Acts as a distributed cache layer
|
||||
- Improves performance by caching frequently accessed data
|
||||
- Can be configured in HA mode with primary-replica setup
|
||||
- Handles session management and real-time features
|
||||
- primary persistent store for the Formbricks app and, by default, Hub
|
||||
- should be backed up and monitored like any other stateful production dependency
|
||||
|
||||
4. **S3 Compatible Storage**
|
||||
4. **Redis / Valkey**
|
||||
|
||||
- Stores file uploads and attachments
|
||||
- Can be any S3-compatible storage service (AWS S3, MinIO, etc.)
|
||||
- Provides reliable and scalable file storage
|
||||
- required for caching, remaining application-enforced rate limits, and audit-related flows
|
||||
- should be shared across all app replicas
|
||||
|
||||
5. **Load Balancer**
|
||||
- Distributes incoming traffic across all Formbricks instances
|
||||
- Performs health checks and removes unhealthy instances
|
||||
- Ensures even load distribution and high availability
|
||||
5. **S3-Compatible Storage**
|
||||
|
||||
- used for file uploads and media-related features
|
||||
- should be shared across all replicas
|
||||
|
||||
6. **Edge Layer**
|
||||
|
||||
- terminates or routes incoming traffic
|
||||
- enforces rate limiting for the route groups that moved out of the application server in v5
|
||||
|
||||
## Redis Configuration
|
||||
|
||||
<Note>
|
||||
Redis is required for Formbricks to function. The application will not start without a Redis URL configured.
|
||||
Redis/Valkey is required for Formbricks to function. The application will not start without `REDIS_URL`.
|
||||
</Note>
|
||||
|
||||
Configure Redis by adding the following **required** environment variable to your instances:
|
||||
|
||||
```sh env
|
||||
REDIS_URL=redis://your-redis-host:6379
|
||||
```
|
||||
|
||||
## S3 Configuration
|
||||
|
||||
Configure S3 storage by adding the following environment variables to your instances:
|
||||
|
||||
```sh env
|
||||
# Required for file uploads in serverless environments
|
||||
S3_ACCESS_KEY=your-access-key
|
||||
S3_SECRET_KEY=your-secret-key
|
||||
S3_REGION=your-region
|
||||
S3_BUCKET_NAME=your-bucket-name
|
||||
|
||||
# For S3-compatible storage (e.g., StorJ, MinIO)
|
||||
# Leave empty for Amazon S3
|
||||
S3_ENDPOINT_URL=https://your-s3-compatible-endpoint
|
||||
|
||||
# Enable for S3-compatible storage that requires path style
|
||||
# 0 for disabled, 1 for enabled
|
||||
S3_FORCE_PATH_STYLE=0
|
||||
```
|
||||
|
||||
When using S3 in a cluster setup, ensure that:
|
||||
When using S3 in a cluster setup, ensure:
|
||||
|
||||
- All Formbricks instances have access to the same S3 bucket
|
||||
- The bucket has appropriate CORS settings configured
|
||||
- IAM roles/users have sufficient permissions for read/write operations
|
||||
- all replicas use the same bucket
|
||||
- the bucket has the required CORS settings
|
||||
- the credentials have read/write access for the assets you expect Formbricks to manage
|
||||
|
||||
## v5 Cluster Notes
|
||||
|
||||
### Hub Is Mandatory
|
||||
|
||||
Formbricks v5 self-hosting requires Hub. Do not plan a cluster upgrade that keeps Hub disabled.
|
||||
|
||||
### Edge Rate Limiting Must Exist Somewhere
|
||||
|
||||
You do not have to use the Formbricks Helm chart's Envoy bundle, but you do need equivalent edge protection for
|
||||
the v5-covered public and API-key routes. Use the
|
||||
[rate-limiting guide](/self-hosting/advanced/rate-limiting) for the exact route coverage.
|
||||
|
||||
### Cube Is Optional
|
||||
|
||||
Cube is only needed for analytics dashboards or other analysis flows that depend on Cube queries. It is not part
|
||||
of the baseline Formbricks v5 cluster runtime.
|
||||
|
||||
## Kubernetes Setup
|
||||
|
||||
Formbricks provides an official Helm chart for deploying the entire cluster stack on Kubernetes. The Helm chart is available in the [Formbricks GitHub repository](https://github.com/formbricks/formbricks/tree/main/helm-chart).
|
||||
|
||||
### Features of the Helm Chart
|
||||
|
||||
The Helm chart provides a complete deployment solution that includes:
|
||||
|
||||
- Formbricks application with configurable replicas
|
||||
- PostgreSQL database (with optional HA configuration)
|
||||
- Redis cluster for caching
|
||||
- Optional Traefik ingress controller for routing and SSL termination
|
||||
- Automatic configuration of dependencies and networking
|
||||
|
||||
### Installation Steps
|
||||
|
||||
1. Add the Formbricks Helm repository:
|
||||
The current Kubernetes deployment path uses the OCI chart published from
|
||||
[`charts/formbricks`](https://github.com/formbricks/formbricks/tree/main/charts/formbricks):
|
||||
|
||||
```sh
|
||||
helm repo add formbricks https://raw.githubusercontent.com/formbricks/formbricks/main/helm-chart
|
||||
helm repo update
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \
|
||||
-n formbricks \
|
||||
--create-namespace \
|
||||
-f values.yaml
|
||||
```
|
||||
|
||||
2. Install the chart:
|
||||
|
||||
```sh
|
||||
helm install formbricks formbricks/formbricks
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
The Helm chart can be customized using a `values.yaml` file to configure:
|
||||
|
||||
- Number of Formbricks replicas
|
||||
- Resource limits and requests
|
||||
- Database configuration
|
||||
- Redis settings
|
||||
- Ingress rules and TLS
|
||||
- Environment variables and secrets
|
||||
|
||||
Refer to the [Helm chart documentation](https://github.com/formbricks/formbricks/tree/main/helm-chart) for detailed configuration options and examples.
|
||||
For the Kubernetes-specific installation flow, mandatory Hub behavior, and Envoy bundle modes, use the dedicated
|
||||
[Kubernetes deployment guide](/self-hosting/setup/kubernetes).
|
||||
|
||||
@@ -15,6 +15,13 @@ Make sure Docker and Docker Compose are installed on your system. These are usua
|
||||
Docker documentation.
|
||||
</Note>
|
||||
|
||||
<Info>
|
||||
Starting with Formbricks v5, the production Docker Compose stack includes Formbricks Hub and the XM Suite v5
|
||||
Cube.js services. Generate `HUB_API_KEY` and `CUBEJS_API_SECRET` during setup, keep `HUB_API_URL` at its
|
||||
internal default unless Hub runs elsewhere, and use the [migration guide](/self-hosting/advanced/migration#v5)
|
||||
when upgrading an existing 4.x instance.
|
||||
</Info>
|
||||
|
||||
## Start
|
||||
|
||||
1. **Create a New Directory for Formbricks**
|
||||
@@ -95,6 +102,29 @@ Make sure Docker and Docker Compose are installed on your system. These are usua
|
||||
sed -i '' "s/CRON_SECRET:.*/CRON_SECRET: $(openssl rand -hex 32)/" docker-compose.yml
|
||||
```
|
||||
|
||||
1. **Generate Hub API Key**
|
||||
|
||||
Formbricks v5 requires a Hub API key for the bundled Hub service.
|
||||
|
||||
For Linux:
|
||||
|
||||
```bash
|
||||
sed -i "/HUB_API_KEY:$/s/HUB_API_KEY:.*/HUB_API_KEY: $(openssl rand -hex 32)/" docker-compose.yml
|
||||
```
|
||||
|
||||
For macOS:
|
||||
|
||||
```bash
|
||||
sed -i '' "s/HUB_API_KEY:.*/HUB_API_KEY: $(openssl rand -hex 32)/" docker-compose.yml
|
||||
```
|
||||
|
||||
<Info>
|
||||
The bundled production stack already sets <code>HUB_API_URL</code> to <code>http://hub:8080</code>. Only
|
||||
change that value if your Formbricks app needs to reach Hub at a different address. If your deployment also
|
||||
resolves Compose variables from a shell environment or <code>.env</code> file, keep the same
|
||||
<code>HUB_API_KEY</code> available there as well.
|
||||
</Info>
|
||||
|
||||
1. **Start the Docker Setup**
|
||||
|
||||
Now, you're ready to run Formbricks with Docker. Use the command below to start Formbricks together with PostgreSQL, Redis, Formbricks Hub, and Cube.js:
|
||||
@@ -118,6 +148,12 @@ Make sure Docker and Docker Compose are installed on your system. These are usua
|
||||
|
||||
Please take a look at our [migration guide](/self-hosting/advanced/migration) for version specific steps to update Formbricks.
|
||||
|
||||
<Info>
|
||||
For a major migration such as Formbricks 4.x to 5.0, update your compose structure and configuration first.
|
||||
Pulling images alone is not enough if your stack does not yet include Hub, `HUB_API_KEY`, the bundled
|
||||
`cube/` config files plus `CUBEJS_API_SECRET`, or the new edge rate-limiting setup.
|
||||
</Info>
|
||||
|
||||
1. Pull the latest Formbricks image
|
||||
|
||||
```bash
|
||||
@@ -163,7 +199,8 @@ The fastest way to test MinIO with Formbricks is to use the included `docker-com
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
This starts PostgreSQL, Valkey (Redis), MinIO, and Mailhog.
|
||||
This starts PostgreSQL, Valkey (Redis), MinIO, Mailhog, Formbricks Hub, and a local Cube instance for
|
||||
analytics testing.
|
||||
|
||||
2. **Access MinIO Console**
|
||||
|
||||
|
||||
@@ -1,211 +1,167 @@
|
||||
---
|
||||
title: "Kubernetes Deployment"
|
||||
description: "Deploy the new Helm chart on a Kubernetes cluster using Helm."
|
||||
description: "Deploy Formbricks on Kubernetes with the current OCI Helm chart."
|
||||
icon: "circle-nodes"
|
||||
---
|
||||
|
||||
Deploy Formbricks on Kubernetes using the current OCI Helm chart published from the `charts/formbricks`
|
||||
directory in the Formbricks repository.
|
||||
|
||||
<Info>
|
||||
Formbricks v5 self-hosting expects Hub to be part of the runtime. The chart handles that by default. Use the
|
||||
[migration guide](/self-hosting/advanced/migration#v5) before upgrading an existing 4.x deployment.
|
||||
</Info>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Ensure you have the following before proceeding:
|
||||
|
||||
- A running Kubernetes cluster (EKS, GKE, AKS, Minikube, etc.)
|
||||
- An **Ingress Controller** (e.g., Traefik, Nginx)
|
||||
- **Helm installed** on your local machine
|
||||
- **Production setup requires managed PostgreSQL and Redis services**
|
||||
- a running Kubernetes cluster
|
||||
- Helm 3 installed locally
|
||||
- a public hostname for `formbricks.webappUrl`
|
||||
- a plan for PostgreSQL and Redis/Valkey, either in-cluster or managed externally
|
||||
- an edge rate-limiting plan for the v5-covered routes: the chart's Envoy bundle or an equivalent external edge
|
||||
solution
|
||||
|
||||
> **Note:** Redis is required for **session handling** when deploying multiple pods.
|
||||
|
||||
---
|
||||
|
||||
## 1. Installation Steps
|
||||
## 1. Install The Chart
|
||||
|
||||
<Steps>
|
||||
<Step title="Install with Default Configuration">
|
||||
```sh
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace
|
||||
```
|
||||
> **Note:** To specify specific version use `--version` flag. E.g., `--version 1.0.0`. Starting from 3.5.0, the chart is available on the GitHub Container Registry (GHCR).
|
||||
<Step title="Create A Minimal values.yaml">
|
||||
|
||||
By default:
|
||||
- PostgreSQL and Redis are deployed within the cluster.
|
||||
- Secrets are dynamically generated and stored as Kubernetes Secrets.
|
||||
```yaml
|
||||
formbricks:
|
||||
webappUrl: https://surveys.example.com
|
||||
```
|
||||
|
||||
Add any additional overrides you need for ingress, external services, secrets, or Enterprise license features.
|
||||
</Step>
|
||||
|
||||
<Step title="Install with an Enterprise License">
|
||||
<Step title="Install Formbricks">
|
||||
|
||||
```sh
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace --set enterprise.licenseKey="YOUR_LICENSE_KEY"
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \
|
||||
-n formbricks \
|
||||
--create-namespace \
|
||||
-f values.yaml
|
||||
```
|
||||
|
||||
By default, the chart deploys:
|
||||
|
||||
- the Formbricks application
|
||||
- Formbricks Hub
|
||||
- PostgreSQL
|
||||
- Redis
|
||||
- generated Kubernetes Secrets
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
## 2. Configure Secrets And External Services
|
||||
|
||||
## 2. Configuring Secrets
|
||||
### Using Generated Secrets
|
||||
|
||||
### Using Kubernetes Secrets (Default)
|
||||
By default, **secrets are stored as Kubernetes Secrets**.
|
||||
The chart automatically generates **random values** for required secrets.
|
||||
The default chart path keeps `secret.enabled: true`, which lets the chart generate the required application
|
||||
secrets for you.
|
||||
|
||||
Modify `values.yaml`:
|
||||
```yaml
|
||||
secret:
|
||||
enabled: true
|
||||
```
|
||||
### Using Managed PostgreSQL And Redis
|
||||
|
||||
---
|
||||
For production workloads, many teams prefer managed services:
|
||||
|
||||
### Using External Secrets (AWS Secrets Manager, Vault, etc.)
|
||||
To use an **external secrets manager**, enable `externalSecret` in `values.yaml`:
|
||||
```yaml
|
||||
secret:
|
||||
enabled: false # Disable default secret generation
|
||||
|
||||
externalSecret:
|
||||
enabled: true
|
||||
secretStore:
|
||||
name: aws-secrets-manager
|
||||
kind: ClusterSecretStore
|
||||
refreshInterval: "1h"
|
||||
files:
|
||||
redis:
|
||||
data:
|
||||
REDIS_PASSWORD:
|
||||
remoteRef:
|
||||
key: "prod/formbricks/secrets"
|
||||
property: REDIS_PASSWORD
|
||||
postgres:
|
||||
data:
|
||||
POSTGRES_ADMIN_PASSWORD:
|
||||
remoteRef:
|
||||
key: "prod/formbricks/secrets"
|
||||
property: POSTGRES_ADMIN_PASSWORD
|
||||
POSTGRES_USER_PASSWORD:
|
||||
remoteRef:
|
||||
key: "prod/formbricks/secrets"
|
||||
property: POSTGRES_USER_PASSWORD
|
||||
app-secrets:
|
||||
data:
|
||||
DATABASE_URL:
|
||||
remoteRef:
|
||||
key: "prod/formbricks/secrets"
|
||||
property: DATABASE_URL
|
||||
REDIS_URL:
|
||||
remoteRef:
|
||||
key: "prod/formbricks/secrets"
|
||||
property: REDIS_URL
|
||||
ENCRYPTION_KEY:
|
||||
remoteRef:
|
||||
key: "prod/formbricks/secrets"
|
||||
property: ENCRYPTION_KEY
|
||||
```
|
||||
**Ensure ExternalSecrets Operator is installed:**
|
||||
[https://external-secrets.io/latest/](https://external-secrets.io/latest/)
|
||||
|
||||
Install with:
|
||||
```sh
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Configuring PostgreSQL and Redis
|
||||
|
||||
### Using Managed PostgreSQL and Redis
|
||||
For production, we recommend using **managed database and cache services**.
|
||||
|
||||
Modify `values.yaml`:
|
||||
```yaml
|
||||
postgresql:
|
||||
enabled: false
|
||||
externalDatabaseUrl: "postgresql://user:password@your-postgres-host:5432/mydb"
|
||||
externalDatabaseUrl: "postgresql://user:password@your-postgres-host:5432/formbricks"
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
externalRedisUrl: "redis://your-redis-host:6379"
|
||||
```
|
||||
Install with:
|
||||
```sh
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
---
|
||||
### Using External Secrets
|
||||
|
||||
### Using In-Cluster PostgreSQL and Redis (Default)
|
||||
By default, PostgreSQL and Redis are **deployed inside the cluster**.
|
||||
To **ensure in-cluster deployment**, use:
|
||||
If your cluster already uses an external secret manager, enable `externalSecret` and point it at your existing
|
||||
SecretStore. Ensure the resulting app secret exposes the values your deployment needs, including `DATABASE_URL`,
|
||||
`REDIS_URL`, and `HUB_API_KEY`.
|
||||
|
||||
## 3. v5-Specific Deployment Notes
|
||||
|
||||
### Hub Is Mandatory
|
||||
|
||||
Formbricks v5 does not support `hub.enabled=false`. Keep the default `hub.enabled=true` behavior in place.
|
||||
|
||||
Use `hub.image.tag`, `hub.resources`, and `hub.existingSecret` only when you need to pin or customize the Hub
|
||||
deployment details.
|
||||
|
||||
### Envoy Bundle Modes
|
||||
|
||||
The chart supports three edge patterns for the v5-covered routes:
|
||||
|
||||
- **Bundled Envoy controller**: set `envoy.enabled=true` and `envoy.controller.enabled=true`
|
||||
- **Existing cluster Envoy controller**: set `envoy.enabled=true` and `envoy.controller.enabled=false`
|
||||
- **Equivalent external edge protection**: keep using your platform's own ingress or gateway layer if it already
|
||||
provides equivalent rate-limiting coverage
|
||||
|
||||
If you use the chart-managed Envoy rate-limiting path, enable a dedicated backend with:
|
||||
|
||||
```yaml
|
||||
postgresql:
|
||||
enabled: true
|
||||
|
||||
redis:
|
||||
envoyRedis:
|
||||
enabled: true
|
||||
```
|
||||
Apply with:
|
||||
|
||||
This keeps Envoy rate-limiting state separate from the application's own Redis traffic.
|
||||
|
||||
### Cube Is Optional
|
||||
|
||||
Cube is only needed for analytics dashboards or other analysis flows that depend on Cube queries.
|
||||
|
||||
- deploy Cube separately when you need it
|
||||
- configure `CUBEJS_API_URL` and `CUBEJS_API_SECRET` for the Formbricks app
|
||||
- do not expect the main Formbricks chart to provision Cube automatically
|
||||
|
||||
## 4. Upgrade The Deployment
|
||||
|
||||
For normal chart upgrades:
|
||||
|
||||
```sh
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \
|
||||
-n formbricks \
|
||||
-f values.yaml
|
||||
```
|
||||
|
||||
---
|
||||
For a Formbricks 4.x to 5.0 migration, confirm the following before running the upgrade:
|
||||
|
||||
## 4. Upgrading the Deployment
|
||||
To apply changes:
|
||||
```sh
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks
|
||||
```
|
||||
- Hub remains enabled
|
||||
- `HUB_API_KEY` is present
|
||||
- your edge rate-limiting plan is in place
|
||||
- any required `AI_*` variables are added
|
||||
- Cube is configured only if this instance needs analytics dashboards or analysis queries
|
||||
|
||||
### Scaling Resources
|
||||
```sh
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --set deployment.resources.limits.cpu=2 --set deployment.resources.limits.memory=4Gi
|
||||
```
|
||||
## 5. Key Values
|
||||
|
||||
### Enabling Autoscaling
|
||||
```sh
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --set autoscaling.enabled=true --set autoscaling.minReplicas=3 --set autoscaling.maxReplicas=10
|
||||
```
|
||||
| Field | Description |
|
||||
| ----------------------------- | ------------------------------------------------------------- |
|
||||
| `formbricks.webappUrl` | Public base URL for the Formbricks app |
|
||||
| `deployment.image.tag` | Formbricks image tag override |
|
||||
| `hub.enabled` | Must stay `true` in Formbricks v5 |
|
||||
| `hub.image.tag` | Hub image tag override |
|
||||
| `envoy.enabled` | Enables chart-managed Envoy Gateway resources |
|
||||
| `envoy.controller.enabled` | Installs the bundled Envoy controller when `true` |
|
||||
| `envoyRedis.enabled` | Deploys a dedicated Redis backend for Envoy rate limiting |
|
||||
| `postgresql.externalDatabaseUrl` | Uses an external PostgreSQL service instead of in-cluster |
|
||||
| `redis.externalRedisUrl` | Uses an external Redis/Valkey service instead of in-cluster |
|
||||
|
||||
---
|
||||
For the complete values surface, refer to the chart README in the repository:
|
||||
[charts/formbricks/README.md](https://github.com/formbricks/formbricks/tree/main/charts/formbricks).
|
||||
|
||||
## 5. Key Configuration Values
|
||||
## 6. Uninstalling The Deployment
|
||||
|
||||
| Field | Description | Default Value |
|
||||
|--------------------------------|--------------------------------------|--------------|
|
||||
| `deployment.replicas` | Number of application replicas | `1` |
|
||||
| `deployment.image.repository` | Docker image repository | `"ghcr.io/formbricks/formbricks"` |
|
||||
| `deployment.image.tag` | Docker image tag | `"latest"` |
|
||||
| `autoscaling.enabled` | Enable autoscaling | `false` |
|
||||
| `postgresql.enabled` | Deploy PostgreSQL in cluster | `true` |
|
||||
| `postgresql.externalDatabaseUrl` | External PostgreSQL URL | `""` |
|
||||
| `redis.enabled` | Deploy Redis in cluster | `true` |
|
||||
| `redis.externalRedisUrl` | External Redis URL | `""` |
|
||||
| `externalSecret.enabled` | Enable external secrets manager | `false` |
|
||||
|
||||
**Refer to the Helm chart repository for full configuration options.**
|
||||
|
||||
---
|
||||
|
||||
## 6. Uninstalling the Deployment
|
||||
To remove the deployment:
|
||||
|
||||
```sh
|
||||
helm uninstall formbricks -n formbricks
|
||||
```
|
||||
|
||||
### Removing Persistent Volumes (PVCs)
|
||||
By default, **PVCs are not deleted** with Helm.
|
||||
To manually remove them:
|
||||
If you also want to remove in-cluster persistent volumes:
|
||||
|
||||
```sh
|
||||
kubectl delete pvc --all -n formbricks
|
||||
```
|
||||
|
||||
To completely delete the namespace:
|
||||
```sh
|
||||
kubectl delete namespace formbricks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Notes
|
||||
- **Ingress Setup:** If using an ingress controller, make sure to configure `ingress.enabled: true` in `values.yaml`.
|
||||
- **Environment Variables:** Pass custom environment variables via `envFrom` in `values.yaml`.
|
||||
- **Backup Strategy:** Ensure you have a backup policy for PostgreSQL if running in-cluster.
|
||||
|
||||
**Your Formbricks deployment is now ready!**
|
||||
|
||||
@@ -32,6 +32,14 @@ Run this command in your terminal:
|
||||
curl -fsSL https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/formbricks.sh -o formbricks.sh && chmod +x formbricks.sh && ./formbricks.sh install
|
||||
```
|
||||
|
||||
<Info>
|
||||
The current v5 one-click stack is based on the production Docker Compose file and includes Formbricks Hub plus
|
||||
the bundled XM Suite v5 Cube.js files under `formbricks/cube/`. Ensure your generated
|
||||
`formbricks/docker-compose.yml` contains a non-empty `HUB_API_KEY` and that `formbricks/.env` contains
|
||||
`CUBEJS_API_SECRET` before treating the v5 stack as ready. If either value is missing after the script
|
||||
finishes, add it manually. `HUB_API_URL` should normally stay at `http://hub:8080`.
|
||||
</Info>
|
||||
|
||||
### Script Prompts
|
||||
|
||||
During installation, the script will prompt you to provide some details:
|
||||
@@ -284,13 +292,22 @@ Y
|
||||
|
||||
## Update
|
||||
|
||||
To update Formbricks, simply run the following command:
|
||||
To update Formbricks for a minor or patch release, run:
|
||||
|
||||
```
|
||||
./formbricks.sh update
|
||||
```
|
||||
|
||||
The script will automatically pull the latest version of Formbricks from GitHub Container Registry and restart the containers.
|
||||
The script pulls the latest images, stops the running containers, and starts the stack again.
|
||||
|
||||
<Warning>
|
||||
`./formbricks.sh update` does **not** rewrite your existing `formbricks/docker-compose.yml`. For a major
|
||||
migration such as Formbricks 4.x to 5.0, follow the [migration guide](/self-hosting/advanced/migration#v5)
|
||||
first, merge the current v5 Compose changes into your existing deployment, confirm `HUB_API_KEY` is set, and
|
||||
only then run the update command. If your older one-click install also uses bundled MinIO for file uploads,
|
||||
review that storage path separately before the first v5 restart; newer self-hosting updates move the bundled
|
||||
object-storage path to RustFS, while external S3-compatible storage keeps the same `S3_*` app contract.
|
||||
</Warning>
|
||||
|
||||
## Stop
|
||||
|
||||
|
||||
Reference in New Issue
Block a user