mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-27 17:00:25 -06:00
Compare commits
19 Commits
feature/fi
...
v3.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c28de7c079 | ||
|
|
05f1068e01 | ||
|
|
7103ec9877 | ||
|
|
9cd7a25343 | ||
|
|
2d028d18e5 | ||
|
|
0164eca206 | ||
|
|
f227c9e97e | ||
|
|
aecedfd082 | ||
|
|
e0f180bf04 | ||
|
|
5d0c435a33 | ||
|
|
daa7e7b56a | ||
|
|
655f319083 | ||
|
|
fcfe5682da | ||
|
|
e1140ac436 | ||
|
|
1529f5d478 | ||
|
|
4870dc8d45 | ||
|
|
a25e5dcfcd | ||
|
|
828e23b5c6 | ||
|
|
1921312445 |
@@ -39,6 +39,7 @@ DATABASE_URL='postgresql://postgres:postgres@localhost:5432/formbricks?schema=pu
|
||||
# See optional configurations below if you want to disable these features.
|
||||
|
||||
MAIL_FROM=noreply@example.com
|
||||
MAIL_FROM_NAME=Formbricks
|
||||
SMTP_HOST=localhost
|
||||
SMTP_PORT=1025
|
||||
# Enable SMTP_SECURE_ENABLED for TLS (port 465)
|
||||
@@ -202,4 +203,9 @@ UNKEY_ROOT_KEY=
|
||||
# AI_AZURE_LLM_DEPLOYMENT_ID=
|
||||
|
||||
# NEXT_PUBLIC_INTERCOM_APP_ID=
|
||||
# INTERCOM_SECRET_KEY=
|
||||
# INTERCOM_SECRET_KEY=
|
||||
|
||||
# Enable Prometheus metrics
|
||||
# PROMETHEUS_ENABLED=
|
||||
# PROMETHEUS_EXPORTER_PORT=
|
||||
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Bug report
|
||||
description: "Found a bug? Please fill out the sections below. \U0001F44D"
|
||||
type: bug
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: issue-summary
|
||||
|
||||
10
.github/workflows/apply-issue-labels-to-pr.yml
vendored
10
.github/workflows/apply-issue-labels-to-pr.yml
vendored
@@ -5,6 +5,9 @@ on:
|
||||
types:
|
||||
- opened
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
label_on_pr:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -15,8 +18,13 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Apply labels from linked issue to PR
|
||||
uses: actions/github-script@v5
|
||||
uses: actions/github-script@211cb3fefb35a799baa5156f9321bb774fe56294 # v5.2.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
||||
7
.github/workflows/build-web.yml
vendored
7
.github/workflows/build-web.yml
vendored
@@ -12,7 +12,12 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Build & Cache Web Binaries
|
||||
|
||||
13
.github/workflows/chromatic.yml
vendored
13
.github/workflows/chromatic.yml
vendored
@@ -11,19 +11,24 @@ jobs:
|
||||
name: Run Chromatic
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
- name: Run Chromatic
|
||||
uses: chromaui/action@latest
|
||||
uses: chromaui/action@c93e0bc3a63aa176e14a75b61a31847cbfdd341c # latest
|
||||
with:
|
||||
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
|
||||
@@ -18,6 +18,11 @@ jobs:
|
||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@v2
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: cURL request
|
||||
if: ${{ env.APP_URL && env.CRON_SECRET }}
|
||||
run: |
|
||||
|
||||
7
.github/workflows/cron-weeklySummary.yml
vendored
7
.github/workflows/cron-weeklySummary.yml
vendored
@@ -7,6 +7,9 @@ on:
|
||||
schedule:
|
||||
# Runs “At 08:00 on Monday.” (see https://crontab.guru)
|
||||
- cron: "0 8 * * 1"
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cron-weeklySummary:
|
||||
permissions:
|
||||
@@ -16,6 +19,10 @@ jobs:
|
||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481
|
||||
with:
|
||||
egress-policy: audit
|
||||
- name: cURL request
|
||||
if: ${{ env.APP_URL && env.CRON_SECRET }}
|
||||
run: |
|
||||
|
||||
27
.github/workflows/dependency-review.yml
vendored
Normal file
27
.github/workflows/dependency-review.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
||||
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
||||
# Once installed, if the workflow run is marked as required,
|
||||
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||
15
.github/workflows/e2e.yml
vendored
15
.github/workflows/e2e.yml
vendored
@@ -43,16 +43,21 @@ jobs:
|
||||
--health-timeout=5s
|
||||
--health-retries=5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
@@ -112,7 +117,7 @@ jobs:
|
||||
|
||||
- name: Azure login
|
||||
if: env.AZURE_ENABLED == 'true'
|
||||
uses: azure/login@v2
|
||||
uses: azure/login@a65d910e8af852a8061c627c456678983e180302 # v2.2.0
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
@@ -130,7 +135,7 @@ jobs:
|
||||
run: |
|
||||
pnpm test:e2e
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
|
||||
10
.github/workflows/labeler.yml
vendored
10
.github/workflows/labeler.yml
vendored
@@ -4,6 +4,9 @@ on:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
name: Pull Request Labeler
|
||||
@@ -12,7 +15,12 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
# https://github.com/actions/labeler/issues/442#issuecomment-1297359481
|
||||
|
||||
5
.github/workflows/lint.yml
vendored
5
.github/workflows/lint.yml
vendored
@@ -12,6 +12,11 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
|
||||
4
.github/workflows/pr.yml
vendored
4
.github/workflows/pr.yml
vendored
@@ -50,6 +50,10 @@ jobs:
|
||||
checks: write
|
||||
statuses: write
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481
|
||||
with:
|
||||
egress-policy: audit
|
||||
- name: fail if conditional jobs failed
|
||||
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') || contains(needs.*.result, 'cancelled')
|
||||
run: exit 1
|
||||
|
||||
5
.github/workflows/prepare-release.yml
vendored
5
.github/workflows/prepare-release.yml
vendored
@@ -18,6 +18,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
13
.github/workflows/release-changesets.yml
vendored
13
.github/workflows/release-changesets.yml
vendored
@@ -26,23 +26,28 @@ jobs:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
|
||||
|
||||
- name: Setup Node.js 18.x
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v2.5.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd # v2.2.4
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
|
||||
with:
|
||||
# This expects you to have a script called release which does a build for your packages and calls changeset publish
|
||||
publish: pnpm release
|
||||
|
||||
@@ -17,6 +17,9 @@ env:
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -28,23 +31,28 @@ jobs:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
|
||||
- name: Set up Depot CLI
|
||||
uses: depot/setup-action@v1
|
||||
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@v3.5.0
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3 # v3.0.0
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -54,7 +62,7 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5 # v5.0.0
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
@@ -62,7 +70,7 @@ jobs:
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: depot/build-push-action@v1
|
||||
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
|
||||
with:
|
||||
project: tw0fqmsx3c
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
|
||||
20
.github/workflows/release-docker-github.yml
vendored
20
.github/workflows/release-docker-github.yml
vendored
@@ -20,6 +20,9 @@ env:
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -31,23 +34,28 @@ jobs:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
|
||||
- name: Set up Depot CLI
|
||||
uses: depot/setup-action@v1
|
||||
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@v3.5.0
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3 # v3.0.0
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -57,7 +65,7 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5 # v5.0.0
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
@@ -65,7 +73,7 @@ jobs:
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: depot/build-push-action@v1
|
||||
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
|
||||
with:
|
||||
project: tw0fqmsx3c
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
|
||||
16
.github/workflows/release-docker.yml
vendored
16
.github/workflows/release-docker.yml
vendored
@@ -5,6 +5,9 @@ on:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release-image-on-dockerhub:
|
||||
name: Release on Dockerhub
|
||||
@@ -16,17 +19,22 @@ jobs:
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2.10.0
|
||||
|
||||
- name: Get Release Tag
|
||||
id: extract_release_tag
|
||||
@@ -36,7 +44,7 @@ jobs:
|
||||
echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@0a97817b6ade9f46837855d676c4cca3a2471fc9 # v4.2.1
|
||||
with:
|
||||
context: .
|
||||
file: ./apps/web/Dockerfile
|
||||
|
||||
7
.github/workflows/scorecard.yml
vendored
7
.github/workflows/scorecard.yml
vendored
@@ -34,6 +34,11 @@ jobs:
|
||||
# actions: read
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
@@ -71,6 +76,6 @@ jobs:
|
||||
# Upload the results to GitHub's code scanning dashboard (optional).
|
||||
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
11
.github/workflows/semantic-pull-requests.yml
vendored
11
.github/workflows/semantic-pull-requests.yml
vendored
@@ -16,7 +16,12 @@ jobs:
|
||||
name: PR title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
|
||||
id: lint_pr_title
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -35,7 +40,7 @@ jobs:
|
||||
revert
|
||||
ossgg
|
||||
|
||||
- uses: marocchino/sticky-pull-request-comment@v2
|
||||
- uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
|
||||
# When the previous steps fails, the workflow would stop. By adding this
|
||||
# condition you can continue the execution with the populated error message.
|
||||
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
||||
@@ -54,7 +59,7 @@ jobs:
|
||||
|
||||
# Delete a previous comment when the issue has been resolved
|
||||
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
|
||||
with:
|
||||
header: pr-title-lint-error
|
||||
message: |
|
||||
|
||||
7
.github/workflows/sonarqube.yml
vendored
7
.github/workflows/sonarqube.yml
vendored
@@ -14,7 +14,12 @@ jobs:
|
||||
name: SonarQube
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
|
||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -1,6 +1,9 @@
|
||||
name: Tests
|
||||
on:
|
||||
workflow_call:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Unit Tests
|
||||
@@ -10,16 +13,21 @@ jobs:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
11
.github/workflows/tolgee-missing-key-check.yml
vendored
11
.github/workflows/tolgee-missing-key-check.yml
vendored
@@ -12,18 +12,23 @@ jobs:
|
||||
check-missing-translations:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
||||
14
.github/workflows/tolgee.yml
vendored
14
.github/workflows/tolgee.yml
vendored
@@ -15,8 +15,13 @@ jobs:
|
||||
if: github.event.pull_request.merged == true
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0 # This ensures we get the full git history
|
||||
|
||||
@@ -36,7 +41,7 @@ jobs:
|
||||
echo "Detected source branch: $SOURCE_BRANCH"
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 18 # Ensure compatibility with your project
|
||||
|
||||
@@ -51,7 +56,6 @@ jobs:
|
||||
--filter-tag "draft:${SOURCE_BRANCH}" \
|
||||
--tag production \
|
||||
--untag "draft:${SOURCE_BRANCH}"
|
||||
--verbose
|
||||
|
||||
- name: Tag unused production keys as Deprecated
|
||||
run: |
|
||||
@@ -59,7 +63,6 @@ jobs:
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-not-extracted --filter-tag production \
|
||||
--tag deprecated --untag production
|
||||
--verbose
|
||||
|
||||
- name: Tag unused draft:current-branch keys as Deprecated
|
||||
run: |
|
||||
@@ -67,7 +70,6 @@ jobs:
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-not-extracted --filter-tag "draft:${SOURCE_BRANCH}" \
|
||||
--tag deprecated --untag "draft:${SOURCE_BRANCH}"
|
||||
--verbose
|
||||
|
||||
- name: Sync with backup
|
||||
run: |
|
||||
@@ -78,7 +80,7 @@ jobs:
|
||||
--yes
|
||||
|
||||
- name: Upload backup as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
with:
|
||||
name: tolgee-backup-${{ github.sha }}
|
||||
path: ./tolgee-backup
|
||||
|
||||
@@ -17,7 +17,12 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
if: github.event.action == 'opened'
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/first-interaction@3c71ce730280171fd1cfb57c00c774f8998586f7 # v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pr-message: |-
|
||||
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -55,3 +55,20 @@ apps/web/public/js
|
||||
packages/database/migrations
|
||||
branch.json
|
||||
.vercel
|
||||
|
||||
# Terraform
|
||||
infra/terraform/.terraform/
|
||||
**/.terraform.lock.hcl
|
||||
**/terraform.tfstate
|
||||
**/terraform.tfstate.*
|
||||
**/crash.log
|
||||
**/override.tf
|
||||
**/override.tf.json
|
||||
**/*.tfvars
|
||||
**/*.tfvars.json
|
||||
**/.terraformrc
|
||||
**/terraform.rc
|
||||
|
||||
# IntelliJ IDEA
|
||||
/.idea/
|
||||
/*.iml
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "8.18.0",
|
||||
"@typescript-eslint/parser": "8.18.0",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"esbuild": "0.25.0",
|
||||
"esbuild": "0.25.1",
|
||||
"eslint-plugin-storybook": "0.11.1",
|
||||
"prop-types": "15.8.1",
|
||||
"storybook": "8.4.7",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:22-alpine3.20 AS base
|
||||
FROM node:22-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS base
|
||||
|
||||
#
|
||||
## step 1: Prune monorepo
|
||||
|
||||
@@ -231,6 +231,7 @@ export const ProjectSettings = ({
|
||||
<p className="text-sm text-slate-400">{t("common.preview")}</p>
|
||||
<div className="z-0 h-3/4 w-3/4">
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={previewSurvey(projectName || "my Product", t)}
|
||||
styling={{ brandColor: { light: brandColor } }}
|
||||
isBrandingEnabled={false}
|
||||
|
||||
@@ -27,7 +27,7 @@ export const EditAlerts = ({
|
||||
return (
|
||||
<>
|
||||
{memberships.map((membership) => (
|
||||
<>
|
||||
<div key={membership.organization.id}>
|
||||
<div className="mb-5 grid grid-cols-6 items-center space-x-3">
|
||||
<div className="col-span-3 flex items-center space-x-3">
|
||||
<UsersIcon className="h-6 w-7 text-slate-600" />
|
||||
@@ -110,7 +110,7 @@ export const EditAlerts = ({
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ export const EditWeeklySummary = ({ memberships, user, environmentId }: EditAler
|
||||
return (
|
||||
<>
|
||||
{memberships.map((membership) => (
|
||||
<>
|
||||
<div key={membership.organization.id}>
|
||||
<div className="mb-5 flex items-center space-x-3 text-sm font-medium">
|
||||
<UsersIcon className="h-6 w-7 text-slate-600" />
|
||||
|
||||
@@ -52,7 +52,7 @@ export const EditWeeklySummary = ({ memberships, user, environmentId }: EditAler
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -9,8 +9,10 @@ import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getOrganizationsWhereUserIsSingleOwner } from "@formbricks/lib/organization/service";
|
||||
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
|
||||
import {
|
||||
getOrganizationByEnvironmentId,
|
||||
getOrganizationsWhereUserIsSingleOwner,
|
||||
} from "@formbricks/lib/organization/service";
|
||||
import { getUser } from "@formbricks/lib/user/service";
|
||||
import { SettingsCard } from "../../components/SettingsCard";
|
||||
import { DeleteAccount } from "./components/DeleteAccount";
|
||||
@@ -71,7 +73,9 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
|
||||
description={t("environments.settings.profile.two_factor_authentication_description")}
|
||||
buttons={[
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: IS_FORMBRICKS_CLOUD
|
||||
? t("common.start_free_trial")
|
||||
: t("common.request_trial_license"),
|
||||
href: IS_FORMBRICKS_CLOUD
|
||||
? `/environments/${params.environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { MobileAppTab } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/MobileAppTab";
|
||||
import { WebAppTab } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/WebAppTab";
|
||||
import { OptionsSwitch } from "@/modules/ui/components/options-switch";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
export const AppTab = ({ environmentId }) => {
|
||||
export const AppTab = () => {
|
||||
const { t } = useTranslate();
|
||||
const [selectedTab, setSelectedTab] = useState("webapp");
|
||||
|
||||
@@ -20,79 +21,7 @@ export const AppTab = ({ environmentId }) => {
|
||||
handleOptionChange={(value) => setSelectedTab(value)}
|
||||
/>
|
||||
|
||||
<div className="mt-4">
|
||||
{selectedTab === "webapp" ? <WebAppTab environmentId={environmentId} /> : <MobileAppTab />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MobileAppTab = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<div>
|
||||
<p className="text-lg font-semibold text-slate-800">
|
||||
{t("environments.surveys.summary.how_to_embed_a_survey_on_your_react_native_app")}
|
||||
</p>
|
||||
<ol className="mt-4 list-decimal space-y-2 pl-5 text-sm text-slate-700">
|
||||
<li>
|
||||
{t("common.follow_these")}{" "}
|
||||
<Link
|
||||
href="https://formbricks.com/docs/developer-docs/react-native-in-app-surveys"
|
||||
target="_blank"
|
||||
className="decoration-brand-dark font-medium underline underline-offset-2">
|
||||
{t("environments.surveys.summary.setup_instructions_for_react_native_apps")}
|
||||
</Link>{" "}
|
||||
{t("environments.surveys.summary.to_connect_your_app_with_formbricks")}
|
||||
</li>
|
||||
</ol>
|
||||
<div className="mt-2 text-sm italic text-slate-700">
|
||||
{t("environments.surveys.summary.were_working_on_sdks_for_flutter_swift_and_kotlin")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const WebAppTab = ({ environmentId }) => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<div>
|
||||
<p className="text-lg font-semibold text-slate-800">
|
||||
{t("environments.surveys.summary.how_to_embed_a_survey_on_your_web_app")}
|
||||
</p>
|
||||
<ol className="mt-4 list-decimal space-y-2 pl-5 text-sm text-slate-700">
|
||||
<li>
|
||||
{t("common.follow_these")}{" "}
|
||||
<Link
|
||||
href={`/environments/${environmentId}/project/app-connection`}
|
||||
target="_blank"
|
||||
className="decoration-brand-dark font-medium underline underline-offset-2">
|
||||
{t("environments.surveys.summary.setup_instructions")}
|
||||
</Link>{" "}
|
||||
{t("environments.surveys.summary.to_connect_your_web_app_with_formbricks")}
|
||||
</li>
|
||||
<li>
|
||||
{t("environments.surveys.summary.learn_how_to")}{" "}
|
||||
<Link
|
||||
href="https://formbricks.com/docs/app-surveys/user-identification"
|
||||
target="_blank"
|
||||
className="decoration-brand-dark font-medium underline underline-offset-2">
|
||||
{t("environments.surveys.summary.identify_users_and_set_attributes")}
|
||||
</Link>{" "}
|
||||
{t("environments.surveys.summary.to_run_highly_targeted_surveys")}.
|
||||
</li>
|
||||
<li>
|
||||
{t("environments.surveys.summary.make_sure_the_survey_type_is_set_to")}{" "}
|
||||
<b>{t("common.app_survey")}</b>
|
||||
</li>
|
||||
<li>{t("environments.surveys.summary.define_when_and_where_the_survey_should_pop_up")}</li>
|
||||
</ol>
|
||||
<div className="mt-4">
|
||||
<video autoPlay loop muted className="w-full rounded-xl border border-slate-200">
|
||||
<source src="/video/tooltips/change-survey-type-app.mp4" type="video/mp4" />
|
||||
{t("environments.surveys.summary.unsupported_video_tag_warning")}
|
||||
</video>
|
||||
</div>
|
||||
<div className="mt-4">{selectedTab === "webapp" ? <WebAppTab /> : <MobileAppTab />}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ export const EmbedView = ({
|
||||
locale={locale}
|
||||
/>
|
||||
) : activeId === "app" ? (
|
||||
<AppTab environmentId={environmentId} />
|
||||
<AppTab />
|
||||
) : null}
|
||||
<div className="mt-2 rounded-md p-3 text-center lg:hidden">
|
||||
{tabs.slice(0, 2).map((tab) => (
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/modules/ui/components/alert";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const MobileAppTab = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<Alert>
|
||||
<AlertTitle>{t("environments.surveys.summary.quickstart_mobile_apps")}</AlertTitle>
|
||||
<AlertDescription>
|
||||
{t("environments.surveys.summary.quickstart_mobile_apps_description")}
|
||||
<Button asChild className="w-fit" size="sm" variant="link">
|
||||
<Link
|
||||
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/framework-guides"
|
||||
target="_blank">
|
||||
{t("common.learn_more")}
|
||||
</Link>
|
||||
</Button>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
@@ -85,8 +85,10 @@ export const PanelInfoView = ({ disableBack, handleInitialPageButton }: PanelInf
|
||||
</p>
|
||||
</div>
|
||||
<Button className="justify-center" asChild>
|
||||
<Link href="https://formbricks.com/docs/link-surveys/market-research-panel" target="_blank">
|
||||
{t("common.get_started")}
|
||||
<Link
|
||||
href="https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/market-research-panel"
|
||||
target="_blank">
|
||||
{t("common.learn_more")}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/modules/ui/components/alert";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const WebAppTab = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<Alert>
|
||||
<AlertTitle>{t("environments.surveys.summary.quickstart_web_apps")}</AlertTitle>
|
||||
<AlertDescription>
|
||||
{t("environments.surveys.summary.quickstart_web_apps_description")}
|
||||
<Button asChild className="w-fit" size="sm" variant="link">
|
||||
<Link
|
||||
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/quickstart"
|
||||
target="_blank">
|
||||
{t("common.learn_more")}
|
||||
</Link>
|
||||
</Button>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
58
apps/web/instrumentation-node.ts
Normal file
58
apps/web/instrumentation-node.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
// instrumentation-node.ts
|
||||
import { PrometheusExporter } from "@opentelemetry/exporter-prometheus";
|
||||
import { HostMetrics } from "@opentelemetry/host-metrics";
|
||||
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
||||
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
||||
import { RuntimeNodeInstrumentation } from "@opentelemetry/instrumentation-runtime-node";
|
||||
import {
|
||||
Resource,
|
||||
detectResourcesSync,
|
||||
envDetector,
|
||||
hostDetector,
|
||||
processDetector,
|
||||
} from "@opentelemetry/resources";
|
||||
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||
import { env } from "@formbricks/lib/env";
|
||||
|
||||
const exporter = new PrometheusExporter({
|
||||
port: env.PROMETHEUS_EXPORTER_PORT ? parseInt(env.PROMETHEUS_EXPORTER_PORT) : 9464,
|
||||
endpoint: "/metrics",
|
||||
host: "0.0.0.0", // Listen on all network interfaces
|
||||
});
|
||||
|
||||
const detectedResources = detectResourcesSync({
|
||||
detectors: [envDetector, processDetector, hostDetector],
|
||||
});
|
||||
|
||||
const customResources = new Resource({});
|
||||
|
||||
const resources = detectedResources.merge(customResources);
|
||||
|
||||
const meterProvider = new MeterProvider({
|
||||
readers: [exporter],
|
||||
resource: resources,
|
||||
});
|
||||
|
||||
const hostMetrics = new HostMetrics({
|
||||
name: `otel-metrics`,
|
||||
meterProvider,
|
||||
});
|
||||
|
||||
registerInstrumentations({
|
||||
meterProvider,
|
||||
instrumentations: [new HttpInstrumentation(), new RuntimeNodeInstrumentation()],
|
||||
});
|
||||
|
||||
hostMetrics.start();
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
try {
|
||||
// Stop collecting metrics or flush them if needed
|
||||
await meterProvider.shutdown();
|
||||
// Possibly close other instrumentation resources
|
||||
} catch (e) {
|
||||
console.error("Error during graceful shutdown:", e);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
@@ -1,25 +1,8 @@
|
||||
import { registerOTel } from "@vercel/otel";
|
||||
import { LangfuseExporter } from "langfuse-vercel";
|
||||
import { env } from "@formbricks/lib/env";
|
||||
|
||||
export async function register() {
|
||||
if (env.LANGFUSE_SECRET_KEY && env.LANGFUSE_PUBLIC_KEY && env.LANGFUSE_BASEURL) {
|
||||
registerOTel({
|
||||
serviceName: "formbricks-cloud-dev",
|
||||
traceExporter: new LangfuseExporter({
|
||||
debug: false,
|
||||
secretKey: env.LANGFUSE_SECRET_KEY,
|
||||
publicKey: env.LANGFUSE_PUBLIC_KEY,
|
||||
baseUrl: env.LANGFUSE_BASEURL,
|
||||
}),
|
||||
});
|
||||
// instrumentation.ts
|
||||
export const register = async () => {
|
||||
if (process.env.NEXT_RUNTIME === "nodejs" && env.PROMETHEUS_ENABLED) {
|
||||
await import("./instrumentation-node");
|
||||
}
|
||||
|
||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||
await import("./sentry.server.config");
|
||||
}
|
||||
|
||||
if (process.env.NEXT_RUNTIME === "edge") {
|
||||
await import("./sentry.edge.config");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
0
apps/web/lib/otelSetup.ts
Normal file
0
apps/web/lib/otelSetup.ts
Normal file
@@ -95,7 +95,7 @@ export const ContactsPage = async ({
|
||||
description={t("environments.contacts.unlock_contacts_description")}
|
||||
buttons={[
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: IS_FORMBRICKS_CLOUD
|
||||
? `/environments/${params.environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
|
||||
@@ -100,7 +100,7 @@ export const SegmentsPage = async ({
|
||||
description={t("environments.segments.unlock_segments_description")}
|
||||
buttons={[
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: IS_FORMBRICKS_CLOUD
|
||||
? `/environments/${params.environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
|
||||
@@ -11,7 +11,7 @@ export const LanguagesLoading = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="languages" loading />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { getMultiLanguagePermission } from "@/modules/ee/license-check/lib/utils";
|
||||
import { EditLanguage } from "@/modules/ee/multi-language-surveys/components/edit-language";
|
||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
|
||||
@@ -12,7 +9,7 @@ import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { notFound } from "next/navigation";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { getOrganization } from "@formbricks/lib/organization/service";
|
||||
@@ -35,11 +32,6 @@ export const LanguagesPage = async (props: { params: Promise<{ environmentId: st
|
||||
}
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
if (!isMultiLanguageAllowed) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
const session = await getServerSession(authOptions);
|
||||
|
||||
@@ -63,18 +55,20 @@ export const LanguagesPage = async (props: { params: Promise<{ environmentId: st
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="languages"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="languages" />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
title={t("environments.project.languages.multi_language_surveys")}
|
||||
description={t("environments.project.languages.multi_language_surveys_description")}>
|
||||
<EditLanguage project={project} locale={user.locale} isReadOnly={isReadOnly} />
|
||||
<EditLanguage
|
||||
project={project}
|
||||
locale={user.locale}
|
||||
isReadOnly={isReadOnly}
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
environmentId={params.environmentId}
|
||||
isFormbricksCloud={IS_FORMBRICKS_CLOUD}
|
||||
/>
|
||||
</SettingsCard>
|
||||
</PageContentWrapper>
|
||||
);
|
||||
|
||||
@@ -4,9 +4,9 @@ import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { Alert, AlertDescription } from "@/modules/ui/components/alert";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { ConfirmationModal } from "@/modules/ui/components/confirmation-modal";
|
||||
import { ModalButton, UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
||||
import { Language } from "@prisma/client";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TFnType, useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
@@ -26,6 +26,9 @@ interface EditLanguageProps {
|
||||
project: TProject;
|
||||
locale: TUserLocale;
|
||||
isReadOnly: boolean;
|
||||
isMultiLanguageAllowed: boolean;
|
||||
environmentId: string;
|
||||
isFormbricksCloud: boolean;
|
||||
}
|
||||
|
||||
const checkIfDuplicateExists = (arr: string[]) => {
|
||||
@@ -57,7 +60,7 @@ const validateLanguages = (languages: Language[], t: TFnType) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the chosen alias matches an ISO identifier of a language that hasn’t been added
|
||||
// Check if the chosen alias matches an ISO identifier of a language that hasn't been added
|
||||
for (const alias of languageAliases) {
|
||||
if (iso639Languages.some((language) => language.alpha2 === alias && !languageCodes.includes(alias))) {
|
||||
toast.error(t("environments.project.languages.conflict_between_selected_alias_and_another_language"), {
|
||||
@@ -70,7 +73,14 @@ const validateLanguages = (languages: Language[], t: TFnType) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export function EditLanguage({ project, locale, isReadOnly }: EditLanguageProps) {
|
||||
export function EditLanguage({
|
||||
project,
|
||||
locale,
|
||||
isReadOnly,
|
||||
isMultiLanguageAllowed,
|
||||
environmentId,
|
||||
isFormbricksCloud,
|
||||
}: EditLanguageProps) {
|
||||
const { t } = useTranslate();
|
||||
const [languages, setLanguages] = useState<Language[]>(project.languages);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
@@ -150,6 +160,21 @@ export function EditLanguage({ project, locale, isReadOnly }: EditLanguageProps)
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
const buttons: [ModalButton, ModalButton] = [
|
||||
{
|
||||
text: isFormbricksCloud ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: isFormbricksCloud
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
},
|
||||
{
|
||||
text: t("common.learn_more"),
|
||||
href: isFormbricksCloud
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/learn-more-self-hosting-license",
|
||||
},
|
||||
];
|
||||
|
||||
const handleSaveChanges = async () => {
|
||||
if (!validateLanguages(languages, t)) return;
|
||||
await Promise.all(
|
||||
@@ -179,63 +204,75 @@ export function EditLanguage({ project, locale, isReadOnly }: EditLanguageProps)
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="space-y-4">
|
||||
{languages.length > 0 ? (
|
||||
<>
|
||||
<LanguageLabels />
|
||||
{languages.map((language, index) => (
|
||||
<LanguageRow
|
||||
index={index}
|
||||
isEditing={isEditing}
|
||||
key={language.id}
|
||||
language={language}
|
||||
locale={locale}
|
||||
onDelete={() => handleDeleteLanguage(language.id)}
|
||||
onLanguageChange={(newLanguage: Language) => {
|
||||
const updatedLanguages = [...languages];
|
||||
updatedLanguages[index] = newLanguage;
|
||||
setLanguages(updatedLanguages);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<p className="text-sm italic text-slate-500">
|
||||
{t("environments.project.languages.no_language_found")}
|
||||
</p>
|
||||
)}
|
||||
<AddLanguageButton onClick={handleAddLanguage} />
|
||||
</div>
|
||||
<EditSaveButtons
|
||||
isEditing={isEditing}
|
||||
onCancel={handleCancelChanges}
|
||||
disabled={isReadOnly}
|
||||
onEdit={() => {
|
||||
setIsEditing(true);
|
||||
}}
|
||||
onSave={handleSaveChanges}
|
||||
t={t}
|
||||
/>
|
||||
{isReadOnly && (
|
||||
<Alert variant="warning" className="mt-4">
|
||||
<AlertDescription>
|
||||
{t("common.only_owners_managers_and_manage_access_members_can_perform_this_action")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<>
|
||||
{isMultiLanguageAllowed ? (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="space-y-4">
|
||||
{languages.length > 0 ? (
|
||||
<>
|
||||
<LanguageLabels />
|
||||
{languages.map((language, index) => (
|
||||
<LanguageRow
|
||||
index={index}
|
||||
isEditing={isEditing}
|
||||
key={language.id}
|
||||
language={language}
|
||||
locale={locale}
|
||||
onDelete={() => handleDeleteLanguage(language.id)}
|
||||
onLanguageChange={(newLanguage: Language) => {
|
||||
const updatedLanguages = [...languages];
|
||||
updatedLanguages[index] = newLanguage;
|
||||
setLanguages(updatedLanguages);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<p className="text-sm italic text-slate-500">
|
||||
{t("environments.project.languages.no_language_found")}
|
||||
</p>
|
||||
)}
|
||||
<AddLanguageButton onClick={handleAddLanguage} />
|
||||
</div>
|
||||
<EditSaveButtons
|
||||
isEditing={isEditing}
|
||||
onCancel={handleCancelChanges}
|
||||
disabled={isReadOnly}
|
||||
onEdit={() => {
|
||||
setIsEditing(true);
|
||||
}}
|
||||
onSave={handleSaveChanges}
|
||||
t={t}
|
||||
/>
|
||||
{isReadOnly && (
|
||||
<Alert variant="warning" className="mt-4">
|
||||
<AlertDescription>
|
||||
{t("common.only_owners_managers_and_manage_access_members_can_perform_this_action")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<ConfirmationModal
|
||||
buttonText={t("environments.project.languages.remove_language")}
|
||||
isButtonDisabled={confirmationModal.isButtonDisabled}
|
||||
onConfirm={() => performLanguageDeletion(confirmationModal.languageId)}
|
||||
open={confirmationModal.isOpen}
|
||||
setOpen={() => {
|
||||
setConfirmationModal((prev) => ({ ...prev, isOpen: !prev.isOpen }));
|
||||
}}
|
||||
text={confirmationModal.text}
|
||||
title={t("environments.project.languages.remove_language")}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<UpgradePrompt
|
||||
title={t("environments.settings.general.use_multi_language_surveys_with_a_higher_plan")}
|
||||
description={t(
|
||||
"environments.settings.general.use_multi_language_surveys_with_a_higher_plan_description"
|
||||
)}
|
||||
buttons={buttons}
|
||||
/>
|
||||
)}
|
||||
<ConfirmationModal
|
||||
buttonText={t("environments.project.languages.remove_language")}
|
||||
isButtonDisabled={confirmationModal.isButtonDisabled}
|
||||
onConfirm={() => performLanguageDeletion(confirmationModal.languageId)}
|
||||
open={confirmationModal.isOpen}
|
||||
setOpen={() => {
|
||||
setConfirmationModal((prev) => ({ ...prev, isOpen: !prev.isOpen }));
|
||||
}}
|
||||
text={confirmationModal.text}
|
||||
title={t("environments.project.languages.remove_language")}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,9 @@ export const MultiLanguageCard: FC<MultiLanguageCardProps> = ({
|
||||
description={t("environments.surveys.edit.upgrade_notice_description")}
|
||||
buttons={[
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: isFormbricksCloud
|
||||
? t("common.start_free_trial")
|
||||
: t("common.request_trial_license"),
|
||||
href: isFormbricksCloud
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/docs/self-hosting/license#30-day-trial-license-request",
|
||||
|
||||
36
apps/web/modules/ee/teams/project-teams/loading.tsx
Normal file
36
apps/web/modules/ee/teams/project-teams/loading.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
|
||||
export const TeamsLoading = () => {
|
||||
const { t } = useTranslate();
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="teams" loading />
|
||||
</PageHeader>
|
||||
<div className="p-4">
|
||||
<div className="mb-4">
|
||||
<div className="h-6 w-1/3 animate-pulse rounded bg-slate-200" />
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{[...Array(3)].map((_, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="flex animate-pulse items-center space-x-4 rounded border border-slate-200 p-4">
|
||||
<div className="h-10 w-10 rounded-full bg-slate-300" />
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="h-4 w-3/4 rounded bg-slate-200" />
|
||||
<div className="h-4 w-1/2 rounded bg-slate-200" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</PageContentWrapper>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,4 @@
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { AccessView } from "@/modules/ee/teams/project-teams/components/access-view";
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
@@ -37,9 +33,6 @@ export const ProjectTeams = async (props: { params: Promise<{ environmentId: str
|
||||
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
|
||||
const { isOwner, isManager } = getAccessFlags(currentUserMembership?.role);
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
const teams = await getTeamsByProjectId(project.id);
|
||||
|
||||
if (!teams) {
|
||||
@@ -50,13 +43,8 @@ export const ProjectTeams = async (props: { params: Promise<{ environmentId: str
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="teams"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="teams" />
|
||||
</PageHeader>
|
||||
<AccessView environmentId={params.environmentId} teams={teams} isOwnerOrManager={isOwnerOrManager} />
|
||||
</PageContentWrapper>
|
||||
|
||||
@@ -37,7 +37,7 @@ export const TeamsView = async ({
|
||||
|
||||
const buttons: [ModalButton, ModalButton] = [
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: IS_FORMBRICKS_CLOUD
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/docs/self-hosting/license#30-day-trial-license-request",
|
||||
|
||||
@@ -162,7 +162,7 @@ export const EmailCustomizationSettings = ({
|
||||
|
||||
const buttons: [ModalButton, ModalButton] = [
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: isFormbricksCloud ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: isFormbricksCloud
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
|
||||
@@ -23,7 +23,7 @@ export const BrandingSettingsCard = async ({
|
||||
|
||||
const buttons: [ModalButton, ModalButton] = [
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: IS_FORMBRICKS_CLOUD
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
|
||||
@@ -6,6 +6,7 @@ import type SMTPTransport from "nodemailer/lib/smtp-transport";
|
||||
import {
|
||||
DEBUG,
|
||||
MAIL_FROM,
|
||||
MAIL_FROM_NAME,
|
||||
SMTP_AUTHENTICATED,
|
||||
SMTP_HOST,
|
||||
SMTP_PASSWORD,
|
||||
@@ -69,7 +70,7 @@ export const sendEmail = async (emailData: SendEmailDataProps): Promise<boolean>
|
||||
} as SMTPTransport.Options);
|
||||
|
||||
const emailDefaults = {
|
||||
from: `Formbricks <${MAIL_FROM ?? "noreply@formbricks.com"}>`,
|
||||
from: `${MAIL_FROM_NAME ?? "Formbricks"} <${MAIL_FROM ?? "noreply@formbricks.com"}>`,
|
||||
};
|
||||
await transporter.sendMail({ ...emailDefaults, ...emailData });
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export const AppConnectionLoading = () => {
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="app-connection" loading />
|
||||
</PageHeader>
|
||||
<div className="mt-4 flex max-w-4xl animate-pulse items-center space-y-4 rounded-lg border bg-blue-50 p-6 text-sm text-blue-900 shadow-sm md:space-y-0 md:text-base"></div>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { WidgetStatusIndicator } from "@/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator";
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { EnvironmentIdField } from "@/modules/projects/settings/(setup)/components/environment-id-field";
|
||||
import { SetupInstructions } from "@/modules/projects/settings/(setup)/components/setup-instructions";
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
@@ -31,18 +27,10 @@ export const AppConnectionPage = async (props) => {
|
||||
throw new Error(t("common.organization_not_found"));
|
||||
}
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="app-connection"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="app-connection" />
|
||||
</PageHeader>
|
||||
<div className="space-y-4">
|
||||
<EnvironmentNotice environmentId={params.environmentId} subPageUrl="/project/app-connection" />
|
||||
|
||||
@@ -42,7 +42,7 @@ export const APIKeysLoading = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="api-keys" loading />
|
||||
</PageHeader>
|
||||
<div className="mt-4 flex max-w-4xl animate-pulse items-center space-y-4 rounded-lg border bg-blue-50 p-6 text-sm text-blue-900 shadow-sm md:space-y-0 md:text-base"></div>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
@@ -53,18 +49,10 @@ export const APIKeysPage = async (props) => {
|
||||
|
||||
const isReadOnly = isMember && !hasManageAccess;
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="api-keys"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="api-keys" />
|
||||
</PageHeader>
|
||||
<EnvironmentNotice environmentId={environment.id} subPageUrl="/project/api-keys" />
|
||||
{environment.type === "development" ? (
|
||||
|
||||
@@ -8,17 +8,13 @@ import { usePathname } from "next/navigation";
|
||||
interface ProjectConfigNavigationProps {
|
||||
activeId: string;
|
||||
environmentId?: string;
|
||||
isMultiLanguageAllowed?: boolean;
|
||||
loading?: boolean;
|
||||
canDoRoleManagement?: boolean;
|
||||
}
|
||||
|
||||
export const ProjectConfigNavigation = ({
|
||||
activeId,
|
||||
environmentId,
|
||||
isMultiLanguageAllowed,
|
||||
loading,
|
||||
canDoRoleManagement,
|
||||
}: ProjectConfigNavigationProps) => {
|
||||
const { t } = useTranslate();
|
||||
const pathname = usePathname();
|
||||
@@ -43,7 +39,6 @@ export const ProjectConfigNavigation = ({
|
||||
label: t("common.survey_languages"),
|
||||
icon: <LanguagesIcon className="h-5 w-5" />,
|
||||
href: `/environments/${environmentId}/project/languages`,
|
||||
hidden: !isMultiLanguageAllowed,
|
||||
current: pathname?.includes("/languages"),
|
||||
},
|
||||
{
|
||||
@@ -70,8 +65,8 @@ export const ProjectConfigNavigation = ({
|
||||
{
|
||||
id: "teams",
|
||||
label: t("common.team_access"),
|
||||
icon: <UsersIcon className="h-5 w-5" />,
|
||||
href: `/environments/${environmentId}/project/teams`,
|
||||
hidden: !canDoRoleManagement,
|
||||
current: pathname?.includes("/teams"),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -28,7 +28,7 @@ export const GeneralSettingsLoading = () => {
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="general" loading />
|
||||
</PageHeader>
|
||||
{cards.map((card, index) => (
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
@@ -51,21 +47,12 @@ export const GeneralSettingsPage = async (props: { params: Promise<{ environment
|
||||
|
||||
const isReadOnly = isMember && !hasManageAccess;
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
const isOwnerOrManager = isOwner || isManager;
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
{/* </PageHeader><PageHeader pageTitle={t("common.configuration")}> */}
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="general"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="general" />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
title={t("common.project_name")}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/ser
|
||||
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Config",
|
||||
title: "Configuration",
|
||||
};
|
||||
|
||||
export const ProjectSettingsLayout = async (props) => {
|
||||
|
||||
@@ -24,7 +24,7 @@ export const ProjectLookSettingsLoading = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="look" loading />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
getWhiteLabelPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { getWhiteLabelPermission } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
|
||||
import { BrandingSettingsCard } from "@/modules/ee/whitelabel/remove-branding/components/branding-settings-card";
|
||||
@@ -51,18 +47,10 @@ export const ProjectLookSettingsPage = async (props: { params: Promise<{ environ
|
||||
|
||||
const isReadOnly = isMember && !hasManageAccess;
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="look"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="look" />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
title={t("environments.project.look.theme")}
|
||||
|
||||
@@ -10,7 +10,7 @@ export const TagsLoading = () => {
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation activeId="tags" />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import {
|
||||
getMultiLanguagePermission,
|
||||
getRoleManagementPermission,
|
||||
} from "@/modules/ee/license-check/lib/utils";
|
||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
@@ -59,18 +55,10 @@ export const TagsPage = async (props) => {
|
||||
|
||||
const isReadOnly = isMember && !hasManageAccess;
|
||||
|
||||
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
|
||||
const canDoRoleManagement = await getRoleManagementPermission(organization.billing.plan);
|
||||
|
||||
return (
|
||||
<PageContentWrapper>
|
||||
<PageHeader pageTitle={t("common.configuration")}>
|
||||
<ProjectConfigNavigation
|
||||
environmentId={params.environmentId}
|
||||
activeId="tags"
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
/>
|
||||
<PageHeader pageTitle={t("common.project_configuration")}>
|
||||
<ProjectConfigNavigation environmentId={params.environmentId} activeId="tags" />
|
||||
</PageHeader>
|
||||
<SettingsCard
|
||||
title={t("environments.project.tags.manage_tags")}
|
||||
|
||||
@@ -243,7 +243,6 @@ export const SurveyEditor = ({
|
||||
environment={environment}
|
||||
previewType={localSurvey.type === "app" ? "modal" : "fullwidth"}
|
||||
languageCode={selectedLanguageCode}
|
||||
onFileUpload={async (file) => file.name}
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ export const TargetingLockedCard = ({ isFormbricksCloud, environmentId }: Target
|
||||
description={t("environments.surveys.edit.unlock_targeting_description")}
|
||||
buttons={[
|
||||
{
|
||||
text: t("common.start_free_trial"),
|
||||
text: isFormbricksCloud ? t("common.start_free_trial") : t("common.request_trial_license"),
|
||||
href: isFormbricksCloud
|
||||
? `/environments/${environmentId}/settings/billing`
|
||||
: "https://formbricks.com/upgrade-self-hosting-license",
|
||||
|
||||
@@ -21,7 +21,7 @@ export const LegalFooter = ({
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-0 z-[1500] h-10 w-full" role="contentinfo">
|
||||
<div className="mx-auto flex h-full max-w-lg items-center justify-center p-2 text-center text-xs text-slate-500">
|
||||
<div className="mx-auto flex h-full max-w-2xl items-center justify-center p-2 text-center text-xs text-slate-500">
|
||||
{IMPRINT_URL && (
|
||||
<Link href={IMPRINT_URL} target="_blank" className="hover:underline" tabIndex={-1}>
|
||||
{t("common.imprint")}
|
||||
|
||||
@@ -80,7 +80,7 @@ export const LinkSurveyWrapper = ({
|
||||
onBackgroundLoaded={handleBackgroundLoaded}>
|
||||
<div className="flex max-h-dvh min-h-dvh items-center justify-center overflow-clip">
|
||||
{!styling.isLogoHidden && project.logo?.url && <ClientLogo projectLogo={project.logo} />}
|
||||
<div className="h-full w-full max-w-lg space-y-6 px-1.5">
|
||||
<div className="h-full w-full max-w-4xl space-y-6 px-1.5">
|
||||
{isPreview && (
|
||||
<div className="fixed left-0 top-0 flex w-full items-center justify-between bg-slate-600 p-2 px-4 text-center text-sm text-white shadow-sm">
|
||||
<div />
|
||||
|
||||
@@ -170,14 +170,14 @@ export const LinkSurvey = ({
|
||||
PRIVACY_URL={PRIVACY_URL}
|
||||
isBrandingEnabled={project.linkSurveyBranding}>
|
||||
<SurveyInline
|
||||
apiHost={!isPreview ? webAppUrl : undefined}
|
||||
environmentId={!isPreview ? survey.environmentId : undefined}
|
||||
apiHost={webAppUrl}
|
||||
environmentId={survey.environmentId}
|
||||
isPreviewMode={isPreview}
|
||||
survey={survey}
|
||||
styling={determineStyling()}
|
||||
languageCode={languageCode}
|
||||
isBrandingEnabled={project.linkSurveyBranding}
|
||||
shouldResetQuestionId={false}
|
||||
onFileUpload={isPreview ? async (file) => `https://formbricks.com/${file.name}` : undefined}
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus -- need it as focus behaviour is different in normal surveys and survey preview
|
||||
autoFocus={autoFocus}
|
||||
prefillResponseData={prefillValue}
|
||||
|
||||
@@ -84,7 +84,6 @@ export const TemplateContainerWithPreview = ({
|
||||
project={project}
|
||||
environment={environment}
|
||||
languageCode={"default"}
|
||||
onFileUpload={async (file) => file.name}
|
||||
/>
|
||||
)}
|
||||
</aside>
|
||||
|
||||
@@ -8,8 +8,8 @@ export const SlackIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...props}>
|
||||
<rect width="3" height="8" x="13" y="2" rx="1.5" />
|
||||
<path d="M19 8.5V10h1.5A1.5 1.5 0 1 0 19 8.5" />
|
||||
|
||||
@@ -166,7 +166,7 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
|
||||
return (
|
||||
<div
|
||||
ref={ContentRef}
|
||||
className={`relative h-[90%] max-h-[40rem] w-[22rem] overflow-hidden rounded-[3rem] border-[6px] border-slate-400 ${getFilterStyle()}`}>
|
||||
className={`relative h-[90%] max-h-[42rem] w-[22rem] overflow-hidden rounded-[3rem] border-[6px] border-slate-400 ${getFilterStyle()}`}>
|
||||
{/* below element is use to create notch for the mobile device mockup */}
|
||||
<div className="absolute left-1/2 right-1/2 top-2 z-20 h-4 w-1/3 -translate-x-1/2 transform rounded-full bg-slate-400"></div>
|
||||
{surveyType === "link" && renderBackground()}
|
||||
|
||||
@@ -9,9 +9,7 @@ import { useTranslate } from "@tolgee/react";
|
||||
import { Variants, motion } from "framer-motion";
|
||||
import { ExpandIcon, MonitorIcon, ShrinkIcon, SmartphoneIcon } from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { TJsFileUploadParams } from "@formbricks/types/js";
|
||||
import { TProjectStyling } from "@formbricks/types/project";
|
||||
import { TUploadFileConfig } from "@formbricks/types/storage";
|
||||
import { TSurvey, TSurveyQuestionId, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { Modal } from "./components/modal";
|
||||
import { TabOption } from "./components/tab-option";
|
||||
@@ -25,7 +23,6 @@ interface PreviewSurveyProps {
|
||||
project: Project;
|
||||
environment: Pick<Environment, "id" | "appSetupCompleted">;
|
||||
languageCode: string;
|
||||
onFileUpload: (file: TJsFileUploadParams["file"], config?: TUploadFileConfig) => Promise<string>;
|
||||
}
|
||||
|
||||
let surveyNameTemp: string;
|
||||
@@ -66,7 +63,6 @@ export const PreviewSurvey = ({
|
||||
project,
|
||||
environment,
|
||||
languageCode,
|
||||
onFileUpload,
|
||||
}: PreviewSurveyProps) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(true);
|
||||
const [isFullScreenPreview, setIsFullScreenPreview] = useState(false);
|
||||
@@ -265,11 +261,11 @@ export const PreviewSurvey = ({
|
||||
borderRadius={styling?.roundness ?? 8}
|
||||
background={styling?.cardBackgroundColor?.light}>
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={survey}
|
||||
isBrandingEnabled={project.inAppSurveyBranding}
|
||||
isRedirectDisabled={true}
|
||||
languageCode={languageCode}
|
||||
onFileUpload={onFileUpload}
|
||||
styling={styling}
|
||||
isCardBorderVisible={!styling.highlightBorderColor?.light}
|
||||
onClose={handlePreviewModalClose}
|
||||
@@ -288,9 +284,9 @@ export const PreviewSurvey = ({
|
||||
</div>
|
||||
<div className="z-10 w-full max-w-md rounded-lg border border-transparent">
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={{ ...survey, type: "link" }}
|
||||
isBrandingEnabled={project.linkSurveyBranding}
|
||||
onFileUpload={onFileUpload}
|
||||
languageCode={languageCode}
|
||||
responseCount={42}
|
||||
styling={styling}
|
||||
@@ -367,11 +363,11 @@ export const PreviewSurvey = ({
|
||||
borderRadius={styling.roundness ?? 8}
|
||||
background={styling.cardBackgroundColor?.light}>
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={survey}
|
||||
isBrandingEnabled={project.inAppSurveyBranding}
|
||||
isRedirectDisabled={true}
|
||||
languageCode={languageCode}
|
||||
onFileUpload={onFileUpload}
|
||||
styling={styling}
|
||||
isCardBorderVisible={!styling.highlightBorderColor?.light}
|
||||
onClose={handlePreviewModalClose}
|
||||
@@ -392,12 +388,12 @@ export const PreviewSurvey = ({
|
||||
<ClientLogo environmentId={environment.id} projectLogo={project.logo} previewSurvey />
|
||||
)}
|
||||
</div>
|
||||
<div className="z-0 w-full max-w-lg rounded-lg border-transparent">
|
||||
<div className="z-0 w-full max-w-4xl rounded-lg border-transparent">
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={{ ...survey, type: "link" }}
|
||||
isBrandingEnabled={project.linkSurveyBranding}
|
||||
isRedirectDisabled={true}
|
||||
onFileUpload={onFileUpload}
|
||||
languageCode={languageCode}
|
||||
responseCount={42}
|
||||
styling={styling}
|
||||
|
||||
@@ -16,77 +16,72 @@ interface SecondaryNavbarProps {
|
||||
export const SecondaryNavigation = ({ navigation, activeId, loading, ...props }: SecondaryNavbarProps) => {
|
||||
return (
|
||||
<div {...props}>
|
||||
<div className="grid h-10 w-full grid-cols-[auto,1fr]">
|
||||
<nav className="flex h-full min-w-full items-center space-x-4" aria-label="Tabs">
|
||||
{loading
|
||||
? navigation.map((navElem) => (
|
||||
<div className="group flex h-full flex-col" key={navElem.id}>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
className={cn(
|
||||
navElem.id === activeId ? "font-semibold text-slate-900" : "text-slate-500",
|
||||
"flex h-full items-center px-3 text-sm font-medium",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
aria-current={navElem.id === activeId ? "page" : undefined}>
|
||||
{navElem.label}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"bottom-0 mt-auto h-[2px] w-full rounded-t-lg transition-all duration-150 ease-in-out",
|
||||
navElem.id === activeId ? "bg-slate-300" : "bg-transparent group-hover:bg-slate-300",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
/>
|
||||
<nav className="flex h-10 w-full items-center space-x-4" aria-label="Tabs">
|
||||
{loading
|
||||
? navigation.map((navElem) => (
|
||||
<div className="group flex h-full flex-col truncate" key={navElem.id}>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
className={cn(
|
||||
navElem.id === activeId ? "font-semibold text-slate-900" : "text-slate-500",
|
||||
"flex h-full items-center truncate px-3 text-sm font-medium",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
aria-current={navElem.id === activeId ? "page" : undefined}>
|
||||
{navElem.label}
|
||||
</div>
|
||||
))
|
||||
: navigation.map(
|
||||
(navElem) =>
|
||||
!navElem.hidden && (
|
||||
<div className="group flex h-full flex-col" key={navElem.id}>
|
||||
{navElem.href ? (
|
||||
<Link
|
||||
href={navElem.href}
|
||||
{...(navElem.onClick ? { onClick: navElem.onClick } : {})}
|
||||
className={cn(
|
||||
navElem.id === activeId
|
||||
? "font-semibold text-slate-900"
|
||||
: "text-slate-500 hover:text-slate-700",
|
||||
"flex h-full items-center px-3 text-sm font-medium",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
aria-current={navElem.id === activeId ? "page" : undefined}>
|
||||
{navElem.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
{...(navElem.onClick ? { onClick: navElem.onClick } : {})}
|
||||
className={cn(
|
||||
navElem.id === activeId
|
||||
? "font-semibold text-slate-900"
|
||||
: "text-slate-500 hover:text-slate-700",
|
||||
"grow items-center px-3 text-sm font-medium transition-all duration-150 ease-in-out",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
aria-current={navElem.id === activeId ? "page" : undefined}>
|
||||
{navElem.label}
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
<div
|
||||
className={cn(
|
||||
"bottom-0 mt-auto h-[2px] w-full rounded-t-lg transition-all duration-150 ease-in-out",
|
||||
navElem.id === activeId ? "bg-slate-300" : "bg-transparent group-hover:bg-slate-300",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
: navigation.map(
|
||||
(navElem) =>
|
||||
!navElem.hidden && (
|
||||
<div className="group flex h-full flex-col truncate" key={navElem.id}>
|
||||
{navElem.href ? (
|
||||
<Link
|
||||
href={navElem.href}
|
||||
{...(navElem.onClick ? { onClick: navElem.onClick } : {})}
|
||||
className={cn(
|
||||
"bottom-0 mt-auto h-[2px] w-full rounded-t-lg transition-all duration-150 ease-in-out",
|
||||
navElem.id === activeId
|
||||
? "bg-brand-dark"
|
||||
: "bg-transparent group-hover:bg-slate-300",
|
||||
? "font-semibold text-slate-900"
|
||||
: "text-slate-500 hover:text-slate-700",
|
||||
"flex h-full items-center px-3 text-sm font-medium",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
<div className="justify-self-end"></div>
|
||||
</div>
|
||||
aria-current={navElem.id === activeId ? "page" : undefined}>
|
||||
{navElem.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
{...(navElem.onClick ? { onClick: navElem.onClick } : {})}
|
||||
className={cn(
|
||||
navElem.id === activeId
|
||||
? "font-semibold text-slate-900"
|
||||
: "text-slate-500 hover:text-slate-700",
|
||||
"grow items-center px-3 text-sm font-medium transition-all duration-150 ease-in-out",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
aria-current={navElem.id === activeId ? "page" : undefined}>
|
||||
{navElem.label}
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"bottom-0 mt-auto h-[2px] w-full rounded-t-lg transition-all duration-150 ease-in-out",
|
||||
navElem.id === activeId ? "bg-brand-dark" : "bg-transparent group-hover:bg-slate-300",
|
||||
navElem.hidden && "hidden"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -162,6 +162,7 @@ export const ThemeStylingPreviewSurvey = ({
|
||||
borderRadius={project.styling.roundness ?? 8}>
|
||||
<Fragment key={surveyFormKey}>
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={{ ...survey, type: "app" }}
|
||||
isBrandingEnabled={project.inAppSurveyBranding}
|
||||
isRedirectDisabled={true}
|
||||
@@ -187,6 +188,7 @@ export const ThemeStylingPreviewSurvey = ({
|
||||
key={surveyFormKey}
|
||||
className={`${project.logo?.url && !project.styling.isLogoHidden && !isFullScreenPreview ? "mt-12" : ""} z-0 w-full max-w-md rounded-lg p-4`}>
|
||||
<SurveyInline
|
||||
isPreviewMode={true}
|
||||
survey={{ ...survey, type: "link" }}
|
||||
isBrandingEnabled={project.linkSurveyBranding}
|
||||
isRedirectDisabled={true}
|
||||
|
||||
@@ -27,7 +27,10 @@ const nextConfig = {
|
||||
localeDetection: false,
|
||||
defaultLocale: "en-US",
|
||||
},
|
||||
experimental: {},
|
||||
experimental: {
|
||||
instrumentationHook: true,
|
||||
serverComponentsExternalPackages: ["@opentelemetry/instrumentation"],
|
||||
},
|
||||
transpilePackages: ["@formbricks/database", "@formbricks/lib"],
|
||||
images: {
|
||||
remotePatterns: [
|
||||
@@ -108,6 +111,10 @@ const nextConfig = {
|
||||
},
|
||||
],
|
||||
});
|
||||
config.resolve.fallback = {
|
||||
http: false, // Prevents Next.js from trying to bundle 'http'
|
||||
https: false,
|
||||
};
|
||||
return config;
|
||||
},
|
||||
async headers() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@formbricks/web",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rimraf .turbo node_modules .next",
|
||||
@@ -41,8 +41,13 @@
|
||||
"@lexical/rich-text": "0.21.0",
|
||||
"@lexical/table": "0.21.0",
|
||||
"@opentelemetry/api-logs": "0.56.0",
|
||||
"@opentelemetry/exporter-prometheus": "0.57.2",
|
||||
"@opentelemetry/host-metrics": "0.35.5",
|
||||
"@opentelemetry/instrumentation": "0.56.0",
|
||||
"@opentelemetry/instrumentation-http": "0.57.2",
|
||||
"@opentelemetry/instrumentation-runtime-node": "0.12.2",
|
||||
"@opentelemetry/sdk-logs": "0.56.0",
|
||||
"@opentelemetry/sdk-metrics": "1.30.1",
|
||||
"@paralleldrive/cuid2": "2.2.2",
|
||||
"@prisma/client": "6.0.1",
|
||||
"@radix-ui/react-accordion": "1.2.2",
|
||||
@@ -104,11 +109,12 @@
|
||||
"next-safe-action": "7.10.2",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.9.16",
|
||||
"opentelemetry": "0.1.0",
|
||||
"optional": "0.1.4",
|
||||
"otplib": "12.0.1",
|
||||
"papaparse": "5.4.1",
|
||||
"posthog-js": "1.200.2",
|
||||
"prismjs": "1.29.0",
|
||||
"prismjs": "1.30.0",
|
||||
"react": "19.0.0",
|
||||
"react-colorful": "5.6.1",
|
||||
"react-confetti": "6.1.0",
|
||||
|
||||
5
apps/web/prometheus.yml
Normal file
5
apps/web/prometheus.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
scrape_configs:
|
||||
- job_name: "nodejs-app"
|
||||
scrape_interval: 5s
|
||||
static_configs:
|
||||
- targets: ["host.docker.internal:9464"]
|
||||
@@ -36,6 +36,7 @@ x-environment: &environment
|
||||
|
||||
# Email Configuration
|
||||
# MAIL_FROM:
|
||||
# MAIL_FROM_NAME:
|
||||
# SMTP_HOST:
|
||||
# SMTP_PORT:
|
||||
# SMTP_USER:
|
||||
|
||||
@@ -224,6 +224,9 @@ EOT
|
||||
echo -n "Enter your SMTP configured Email ID: "
|
||||
read mail_from
|
||||
|
||||
echo -n "Enter your SMTP configured Email Name: "
|
||||
read mail_from_name
|
||||
|
||||
echo -n "Enter your SMTP Host URL: "
|
||||
read smtp_host
|
||||
|
||||
@@ -244,6 +247,7 @@ EOT
|
||||
|
||||
else
|
||||
mail_from=""
|
||||
mail_from_name=""
|
||||
smtp_host=""
|
||||
smtp_port=""
|
||||
smtp_user=""
|
||||
@@ -270,6 +274,7 @@ EOT
|
||||
|
||||
if [[ -n $mail_from ]]; then
|
||||
sed -i "s|# MAIL_FROM:|MAIL_FROM: \"$mail_from\"|" docker-compose.yml
|
||||
sed -i "s|# MAIL_FROM_NAME:|MAIL_FROM_NAME: \"$mail_from_name\"|" docker-compose.yml
|
||||
sed -i "s|# SMTP_HOST:|SMTP_HOST: \"$smtp_host\"|" docker-compose.yml
|
||||
sed -i "s|# SMTP_PORT:|SMTP_PORT: \"$smtp_port\"|" docker-compose.yml
|
||||
sed -i "s|# SMTP_SECURE_ENABLED:|SMTP_SECURE_ENABLED: $smtp_secure_enabled|" docker-compose.yml
|
||||
|
||||
@@ -1039,6 +1039,7 @@ x-environment: &environment
|
||||
|
||||
# Email Configuration
|
||||
MAIL_FROM:
|
||||
MAIL_FROM_NAME:
|
||||
SMTP_HOST:
|
||||
SMTP_PORT:
|
||||
SMTP_SECURE_ENABLED:
|
||||
|
||||
@@ -33,6 +33,7 @@ These variables are present inside your machine’s docker-compose file. Restart
|
||||
| RATE_LIMITING_DISABLED | Disables rate limiting if set to 1. | 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) | |
|
||||
@@ -60,5 +61,7 @@ These variables are present inside your machine’s docker-compose file. Restart
|
||||
| OPENTELEMETRY_LISTENER_URL | URL for OpenTelemetry listener inside Formbricks. | optional | |
|
||||
| UNKEY_ROOT_KEY | Key for the [Unkey](https://www.unkey.com/) service. This is used for Rate Limiting for management API. | optional | |
|
||||
| CUSTOM_CACHE_DISABLED | Disables custom cache handler if set to 1 (required for deployment on Vercel) | optional | |
|
||||
| PROMETHEUS_ENABLED | Enables Prometheus metrics if set to 1. | optional | |
|
||||
| PROMETHEUS_EXPORTER_PORT | Port for Prometheus metrics. | optional | 9090 | | optional | |
|
||||
|
||||
Note: If you want to configure something that is not possible via above, please open an issue on our GitHub repo here or reach out to us on Github Discussions and we’ll try our best to work out a solution with you.
|
||||
|
||||
@@ -33,6 +33,7 @@ To enable email functionality, configure the following environment variables:
|
||||
```bash
|
||||
# Basic SMTP Configuration
|
||||
MAIL_FROM=noreply@yourdomain.com
|
||||
MAIL_FROM_NAME=Formbricks
|
||||
SMTP_HOST=smtp.yourprovider.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your_username
|
||||
@@ -75,6 +76,7 @@ If you're using the one-click setup with Docker Compose, you can either:
|
||||
environment:
|
||||
# Email Configuration
|
||||
MAIL_FROM: noreply@yourdomain.com
|
||||
MAIL_FROM_NAME: Formbricks
|
||||
SMTP_HOST: smtp.yourprovider.com
|
||||
SMTP_PORT: 587
|
||||
SMTP_USER: your_username
|
||||
@@ -95,6 +97,7 @@ environment:
|
||||
|
||||
```bash
|
||||
MAIL_FROM=noreply@yourdomain.com
|
||||
MAIL_FROM_NAME=Formbricks
|
||||
SMTP_HOST=smtp.sendgrid.net
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=apikey
|
||||
@@ -105,6 +108,7 @@ SMTP_PASSWORD=your_sendgrid_api_key
|
||||
|
||||
```bash
|
||||
MAIL_FROM=noreply@yourdomain.com
|
||||
MAIL_FROM_NAME=Formbricks
|
||||
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your_ses_access_key
|
||||
@@ -115,6 +119,7 @@ SMTP_PASSWORD=your_ses_secret_key
|
||||
|
||||
```bash
|
||||
MAIL_FROM=your_email@gmail.com
|
||||
MAIL_FROM_NAME=Formbricks
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your_email@gmail.com
|
||||
|
||||
@@ -1,154 +1,217 @@
|
||||
---
|
||||
title: "Kubernetes Deployment"
|
||||
description: "Deploy Formbricks on a Kubernetes cluster using Helm."
|
||||
description: "Deploy the new Helm chart on a Kubernetes cluster using Helm."
|
||||
icon: "circle-nodes"
|
||||
---
|
||||
|
||||
This guide explains how to deploy Formbricks on a Kubernetes cluster using Helm. The primary focus is on deploying Formbricks pods in a production-ready environment with external database services.
|
||||
# **🚀 Kubernetes Deployment Guide**
|
||||
|
||||
## Prerequisites
|
||||
This guide explains how to deploy the **Formbricks Helm Chart** on a Kubernetes cluster using Helm. It provides configuration options for **internal** and **external** databases, caching services, and secrets management.
|
||||
|
||||
Before you begin, ensure that:
|
||||
---
|
||||
|
||||
- You have a running Kubernetes cluster (AWS EKS, GCP GKE, Azure AKS, Minikube, etc.)
|
||||
- An Ingress controller (e.g., Traefik, Nginx) is configured
|
||||
- You have Helm installed on your local machine
|
||||
- For production environments, you have access to external PostgreSQL and Redis services
|
||||
## **📌 Prerequisites**
|
||||
Ensure you have the following before proceeding:
|
||||
|
||||
> **Important:** Running multiple Formbricks pods in a cluster setup requires a Formbricks Enterprise license. With the Community Edition, only a single Formbricks pod is supported. Redis is required when deploying multiple Formbricks pods for proper session handling and caching.
|
||||
- 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**
|
||||
|
||||
## Basic Installation
|
||||
> **Note:** Redis is required for **session handling** when deploying multiple pods.
|
||||
|
||||
### Step 1: Clone the Formbricks Helm Chart
|
||||
---
|
||||
|
||||
## **1️⃣ Installation Steps**
|
||||
|
||||
### **🔹 Step 1: Clone the Helm Chart**
|
||||
```sh
|
||||
git clone https://github.com/formbricks/formbricks.git
|
||||
cd formbricks/helm-chart
|
||||
git clone https://github.com/formbricks/formbricks
|
||||
cd helm-chart
|
||||
```
|
||||
|
||||
### Step 2: Deploy Formbricks
|
||||
|
||||
For a basic deployment with a single pod (Community Edition) and PostgreSQL running in the cluster:
|
||||
|
||||
### **🔹 Step 2: Install with Default Configuration**
|
||||
```sh
|
||||
helm install my-formbricks ./ \
|
||||
--namespace formbricks \
|
||||
--set redis.enabled=false \
|
||||
--create-namespace
|
||||
helm install formbricks ./ -n formbricks --create-namespace
|
||||
```
|
||||
By default:
|
||||
- PostgreSQL and Redis **are deployed within the cluster**.
|
||||
- Secrets **are dynamically generated** and stored as Kubernetes Secrets.
|
||||
|
||||
## Production Deployment
|
||||
|
||||
For production environments, we recommend using managed database and cache services like AWS RDS for PostgreSQL and AWS ElastiCache for Redis:
|
||||
|
||||
### **🔹 Step 3: Install with an Enterprise License**
|
||||
```sh
|
||||
helm install my-formbricks ./ \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set replicaCount=3 \
|
||||
--set postgresql.enabled=false \
|
||||
--set postgresql.externalUrl="postgresql://user:password@your-postgres-host:5432/formbricks" \
|
||||
--set redis.enabled=false \
|
||||
--set redis.externalUrl="redis://your-redis-host:6379"
|
||||
helm install formbricks ./ -n formbricks --create-namespace --set enterprise.licenseKey="YOUR_LICENSE_KEY"
|
||||
```
|
||||
|
||||
> **Note:** The above multi-pod configuration requires a Formbricks Enterprise license. Redis is enabled and configured to support multiple Formbricks pods.
|
||||
---
|
||||
|
||||
## Verify Installation
|
||||
## **2️⃣ Configuring Secrets**
|
||||
|
||||
### Check Running Services
|
||||
### **🔹 Using Kubernetes Secrets (Default)**
|
||||
By default, **secrets are stored as Kubernetes Secrets**.
|
||||
The chart automatically generates **random values** for required secrets.
|
||||
|
||||
Modify `values.yaml`:
|
||||
```yaml
|
||||
secret:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **🔹 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
|
||||
kubectl get pods -n formbricks
|
||||
kubectl get svc -n formbricks
|
||||
kubectl get ingress -n formbricks
|
||||
helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
> **Note:** The Formbricks application pod may take some time to reach a stable state as it runs database migrations during startup.
|
||||
---
|
||||
|
||||
### Access Formbricks
|
||||
## **3️⃣ Configuring PostgreSQL and Redis**
|
||||
|
||||
- If running locally with Minikube:
|
||||
```sh
|
||||
minikube service my-formbricks -n formbricks
|
||||
```
|
||||
- If deployed on a cloud cluster, make sure to set up your ingress controller properly and visit the domain or IP address associated with your ingress.
|
||||
### **🔹 Using Managed PostgreSQL and Redis**
|
||||
For production, we recommend using **managed database and cache services**.
|
||||
|
||||
## Upgrading Formbricks
|
||||
Modify `values.yaml`:
|
||||
```yaml
|
||||
postgresql:
|
||||
enabled: false
|
||||
externalDatabaseUrl: "postgresql://user:password@your-postgres-host:5432/mydb"
|
||||
|
||||
To upgrade your Formbricks deployment, use:
|
||||
|
||||
```bash
|
||||
# From the helm-chart directory
|
||||
helm upgrade my-formbricks ./ --namespace formbricks
|
||||
redis:
|
||||
enabled: false
|
||||
externalRedisUrl: "redis://your-redis-host:6379"
|
||||
```
|
||||
Install with:
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
### Common Upgrade Scenarios
|
||||
---
|
||||
|
||||
#### 1. Updating Environment Variables
|
||||
### **🔹 Using In-Cluster PostgreSQL and Redis (Default)**
|
||||
By default, PostgreSQL and Redis are **deployed inside the cluster**.
|
||||
To **ensure in-cluster deployment**, use:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks ./ --namespace formbricks \
|
||||
--set env.SMTP_HOST=new-smtp.example.com \
|
||||
--set env.SMTP_PORT=587 \
|
||||
--set env.NEW_CUSTOM_VAR=newvalue
|
||||
```yaml
|
||||
postgresql:
|
||||
enabled: true
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
```
|
||||
Apply with:
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
#### 2. Scaling Resources
|
||||
---
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks ./ --namespace formbricks \
|
||||
--set resources.limits.cpu=1 \
|
||||
--set resources.limits.memory=2Gi \
|
||||
--set resources.requests.cpu=500m \
|
||||
--set resources.requests.memory=1Gi
|
||||
## **4️⃣ Upgrading the Deployment**
|
||||
To apply changes:
|
||||
```sh
|
||||
helm upgrade formbricks ./ -n formbricks
|
||||
```
|
||||
|
||||
#### 3. Updating Autoscaling Configuration
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks ./ --namespace formbricks \
|
||||
--set autoscaling.enabled=true \
|
||||
--set autoscaling.minReplicas=3 \
|
||||
--set autoscaling.maxReplicas=10 \
|
||||
--set autoscaling.metrics[0].resource.target.averageUtilization=75
|
||||
### **🔹 Scaling Resources**
|
||||
```sh
|
||||
helm upgrade formbricks ./ -n formbricks --set deployment.resources.limits.cpu=2 --set deployment.resources.limits.memory=4Gi
|
||||
```
|
||||
|
||||
> **Note:** Enabling autoscaling requires a Formbricks Enterprise license and proper Redis configuration.
|
||||
|
||||
#### 4. Changing Database Connection
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks ./ --namespace formbricks \
|
||||
--set postgresql.enabled=false \
|
||||
--set postgresql.externalUrl="postgresql://newuser:newpassword@external-postgres-host:5432/newdatabase"
|
||||
### **🔹 Enabling Autoscaling**
|
||||
```sh
|
||||
helm upgrade formbricks ./ -n formbricks --set autoscaling.enabled=true --set autoscaling.minReplicas=3 --set autoscaling.maxReplicas=10
|
||||
```
|
||||
|
||||
## Advanced Configuration Options
|
||||
---
|
||||
|
||||
For advanced configurations including:
|
||||
## **5️⃣ Key Configuration Values**
|
||||
|
||||
- Deploying PostgreSQL and Redis within your Kubernetes cluster
|
||||
- Configuring Traefik ingress controller
|
||||
- Setting up high availability
|
||||
- Customizing autoscaling behavior
|
||||
| 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` |
|
||||
|
||||
Please refer to the complete Helm chart documentation at:
|
||||
[https://github.com/formbricks/formbricks/tree/main/helm-chart](https://github.com/formbricks/formbricks/tree/main/helm-chart)
|
||||
📌 **Refer to the Helm chart repository for full configuration options.**
|
||||
|
||||
## Key Configuration Values
|
||||
---
|
||||
|
||||
| Field | Description | Default |
|
||||
| ------------------------- | ----------------------------- | ------------------------------- |
|
||||
| `replicaCount` | Number of Formbricks replicas | `1` |
|
||||
| `image.repository` | Docker image repository | `ghcr.io/formbricks/formbricks` |
|
||||
| `image.tag` | Docker image tag | `"2.6.0"` |
|
||||
| `resources.limits.cpu` | CPU resource limit | `500m` |
|
||||
| `resources.limits.memory` | Memory resource limit | `1Gi` |
|
||||
| `autoscaling.enabled` | Enable autoscaling | `false` |
|
||||
| `postgresql.enabled` | Deploy PostgreSQL in cluster | `true` |
|
||||
| `postgresql.externalUrl` | External PostgreSQL URL | `""` |
|
||||
| `redis.enabled` | Deploy Redis in cluster | `false` |
|
||||
| `redis.externalUrl` | External Redis URL | `""` |
|
||||
## **6️⃣ Uninstalling the Deployment**
|
||||
To remove the deployment:
|
||||
```sh
|
||||
helm uninstall formbricks -n formbricks
|
||||
```
|
||||
|
||||
For the complete list of configuration options, please refer to the Formbricks Helm chart repository.
|
||||
### **Removing Persistent Volumes (PVCs)**
|
||||
By default, **PVCs are not deleted** with Helm.
|
||||
To manually remove them:
|
||||
```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!** 🚀
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
---
|
||||
title: "Market Research Panel"
|
||||
title: "Market Research Panel with Prolific"
|
||||
sidebarTitle: "Market Research Panel"
|
||||
description:
|
||||
"Formbricks surveys can be integrated with Prolifics participant panel easily. This tutorial walks you through the steps on how to access a pool of over 200.000 participants for your research."
|
||||
icon: "users"
|
||||
---
|
||||
|
||||
# Creating a Research Panel with Prolific
|
||||
|
||||
You need a lot of research participants that match your target audience fast?
|
||||
|
||||
Formbricks integrates well with Prolific. Prolific provides a pool of over 200.000 research participants you can choose from. Run market research with Formbricks within hours, not days.
|
||||
|
||||
<Note>
|
||||
Prolific is a paid service. You need to fund your account to access the pool
|
||||
of participants. The cost depends on the number of participants you want to
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 15.5.36
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: 16.4.16
|
||||
- name: redis
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 20.1.5
|
||||
- name: traefik
|
||||
repository: https://helm.traefik.io/traefik
|
||||
version: 32.0.0
|
||||
digest: sha256:21923a92e214351c3f96348fe0c479cc6e98e7828d75d41edc1ab73839dd39ce
|
||||
generated: "2024-09-27T13:48:24.815107+03:00"
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: 20.11.2
|
||||
digest: sha256:6233567e6d133fd87585de7cb11f835125ab649fc7979eac7b17d4b2881f54dc
|
||||
generated: "2025-03-06T15:48:20.190945+05:30"
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
apiVersion: v2
|
||||
name: formbricks
|
||||
description: A Helm chart for Formbricks with PostgreSQL, Redis, Traefik, and cert-manager
|
||||
description: A Helm chart for Formbricks with PostgreSQL, Redis
|
||||
|
||||
type: application
|
||||
version: 0.1.2
|
||||
appVersion: "1.0.0"
|
||||
|
||||
# Helm chart Version
|
||||
version: 3.4.0
|
||||
appVersion: v3.4.0
|
||||
|
||||
keywords:
|
||||
- formbricks
|
||||
- postgresql
|
||||
- redis
|
||||
|
||||
home: https://formbricks.com/docs/self-hosting/setup/kubernetes
|
||||
maintainers:
|
||||
- name: Formbricks
|
||||
email: info@formbricks.com
|
||||
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 15.5.36
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: "16.4.16"
|
||||
repository: "oci://registry-1.docker.io/bitnamicharts"
|
||||
condition: postgresql.enabled
|
||||
- name: redis
|
||||
version: 20.1.5
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 20.11.2
|
||||
repository: "oci://registry-1.docker.io/bitnamicharts"
|
||||
condition: redis.enabled
|
||||
- name: traefik
|
||||
version: 32.0.0
|
||||
repository: https://helm.traefik.io/traefik
|
||||
condition: traefik.enabled
|
||||
|
||||
@@ -1,706 +1,156 @@
|
||||
<div id="top"></div>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<a href="https://formbricks.com">
|
||||
|
||||
<img width="120" alt="Open Source Privacy First Experience Management Solution Qualtrics Alternative Logo" src="https://github.com/formbricks/formbricks/assets/72809645/0086704f-bee7-4d38-9cc8-fa42ee59e004">
|
||||
|
||||
</a>
|
||||
|
||||
<h3 align="center">Formbricks</h3>
|
||||
|
||||
<p align="center">
|
||||
Harvest user-insights, build irresistible experiences.
|
||||
<br />
|
||||
<a href="https://formbricks.com/">Website</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
# Formbricks Helm Chart: Comprehensive Documentation
|
||||
|
||||
- [Formbricks Helm Chart: Comprehensive Documentation](#formbricks-helm-chart-comprehensive-documentation)
|
||||
- [Introduction](#introduction)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Chart Components](#chart-components)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Scaling PostgreSQL and Redis](#scaling-postgresql-and-redis)
|
||||
- [Configuration](#configuration)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [Scaling](#scaling)
|
||||
- [With Auto Scaling (Kubernetes Metrics Server Requirement)](#with-auto-scaling-kubernetes-metrics-server-requirement)
|
||||
- [Customizing Autoscaling](#customizing-autoscaling)
|
||||
- [Kubernetes Metrics Server Requirement](#kubernetes-metrics-server-requirement)
|
||||
- [Advanced Autoscaling Configuration](#advanced-autoscaling-configuration)
|
||||
- [Upgrading Formbricks](#upgrading-formbricks)
|
||||
- [Upgrade Process](#upgrade-process)
|
||||
- [Common Upgrade Scenarios](#common-upgrade-scenarios)
|
||||
- [1. Updating Environment Variables](#1-updating-environment-variables)
|
||||
- [2. Enabling or Disabling Features](#2-enabling-or-disabling-features)
|
||||
- [3. Scaling Resources](#3-scaling-resources)
|
||||
- [4. Updating Autoscaling Configuration](#4-updating-autoscaling-configuration)
|
||||
- [5. Changing Database Credentials](#5-changing-database-credentials)
|
||||
- [Using a Values File for Complex Upgrades](#using-a-values-file-for-complex-upgrades)
|
||||
- [Support](#support)
|
||||
- [Full Values Documentation](#full-values-documentation)
|
||||
- [✍️ Contribution](#️-contribution)
|
||||
- [MicroK8s Installation and Formbricks Deployment](#microk8s-installation-and-formbricks-deployment)
|
||||
- [MicroK8s Quick Setup](#microk8s-quick-setup)
|
||||
- [Deploying Formbricks on MicroK8s](#deploying-formbricks-on-microk8s)
|
||||
|
||||
## Introduction
|
||||
|
||||
This Helm chart deploys Formbricks, an advanced open-source form builder and survey tool, along with its required dependencies (PostgreSQL and Redis) on a Kubernetes cluster. It also includes an optional Traefik ingress controller for easy access and SSL termination.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before installing the Formbricks Helm chart, ensure you have the following:
|
||||
|
||||
- Kubernetes cluster version 1.24 or later
|
||||
- Helm version 3.2.0 or later
|
||||
- Dynamic volume provisioning support in the underlying infrastructure (for PostgreSQL persistence)
|
||||
- Familiarity with Kubernetes concepts and Helm charts
|
||||
|
||||
## Chart Components
|
||||
|
||||
This Helm chart deploys the following components:
|
||||
|
||||
1. **Formbricks Application**: The core Formbricks service.
|
||||
2. **PostgreSQL Database**: (Optional) A relational database for Formbricks data.
|
||||
3. **Redis**: (Optional) An in-memory data structure store for caching.
|
||||
4. **Traefik Ingress Controller**: (Optional) A modern HTTP reverse proxy and load balancer.
|
||||
|
||||
## Installation
|
||||
|
||||
### Quick Start
|
||||
|
||||
To quickly deploy Formbricks with default settings:
|
||||
|
||||
1. clone the formbricks repository and navigate to the helm-chart directory:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/formbricks/formbricks.git
|
||||
cd formbricks/helm-chart
|
||||
```
|
||||
|
||||
2. Deploy Formbricks
|
||||
|
||||
```bash
|
||||
helm install my-formbricks ./ \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set replicaCount=2
|
||||
```
|
||||
|
||||
This will deploy Formbricks with default settings, including a new PostgreSQL instance, Redis and Traefik disabled.
|
||||
|
||||
### Verify and Access Formbricks
|
||||
|
||||
After deploying Formbricks, you can verify the installation and access the application:
|
||||
|
||||
1. Check the Running Services:
|
||||
|
||||
```bash
|
||||
kubectl get pods -n formbricks
|
||||
kubectl get svc -n formbricks
|
||||
kubectl get ingress -n formbricks
|
||||
```
|
||||
|
||||
> **Note:** The Formbricks application pod may take some time to reach a stable state as it runs database migrations during startup.
|
||||
|
||||
2. Access Formbricks:
|
||||
- If running locally with **Minikube**:
|
||||
```bash
|
||||
minikube service my-formbricks -n formbricks
|
||||
```
|
||||
- If deployed on a **cloud cluster**, visit:
|
||||
```
|
||||
https://formbricks.example.com
|
||||
```
|
||||
(Replace with your configured hostname)
|
||||
|
||||
### Usage Examples
|
||||
|
||||
Here are various examples of how to install and configure the Formbricks Helm chart:
|
||||
|
||||
1. **Default Installation with Traefik enabled and Custom Hostname**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Option 1: Installation without SSL (Not recommended for production)</summary>
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set traefik.enabled=true \
|
||||
--set hostname=forms.example.com
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
This command enables Traefik and sets a custom hostname. Replace `forms.example.com` with your actual domain name.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Option 2: Installation with SSL (Recommended for production)</summary>
|
||||
|
||||
1. First, download the values file:
|
||||
|
||||
```bash
|
||||
helm show values formbricks/formbricks > values.yaml
|
||||
```
|
||||
|
||||
2. Open the `values.yaml` file in a text editor and make the following changes:
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
enabled: true
|
||||
additionalArguments:
|
||||
- "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com"
|
||||
```
|
||||
|
||||
Replace `your-email@example.com` with a valid email address where you want to receive Let's Encrypt notifications.
|
||||
|
||||
3. Install Formbricks with the updated values file:
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
-f values.yaml \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set hostname=forms.example.com
|
||||
```
|
||||
|
||||
This command enables Traefik, sets a custom hostname, and uses the configured email address for Let's Encrypt. Remember to replace `forms.example.com` with your actual domain name.
|
||||
|
||||
</details>
|
||||
|
||||
These installation options provide flexibility in setting up Formbricks with Traefik. The SSL option is recommended for production environments to ensure secure communications.
|
||||
|
||||
2. **Community Advanced:**
|
||||
Provision a whole community setup with Formbricks, Postgres, Custom Domain with SSL
|
||||
|
||||
```bash
|
||||
helm install formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set postgres.enabled=true \
|
||||
--set traefik.enabled=true \
|
||||
--set hostname=forms.example.com \
|
||||
--set email=your-mail@example.com
|
||||
```
|
||||
|
||||
3. **Cluster Advanced:**
|
||||
Provision a ready to use cluster for enterprise customers with Formbricks (3 pods), Postgres, Redis and Custom Domain with SSL
|
||||
|
||||
```bash
|
||||
helm install formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set replicaCount=3
|
||||
--set redis.enabled=true \
|
||||
--set traefik.enabled=true \
|
||||
--set hostname=forms.example.com \
|
||||
--set email=your-mail@example.com
|
||||
```
|
||||
|
||||
4. **Installation with Redis and PostgreSQL Disabled, Using External Services**:
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set postgresql.enabled=false \
|
||||
--set postgresql.externalUrl=postgresql://user:password@your-postgres-url:5432/dbname \
|
||||
--set redis.enabled=false \
|
||||
--set redis.externalUrl=redis://your-redis-url:6379
|
||||
```
|
||||
|
||||
5. **High Availability Setup**:
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set replicaCount=3
|
||||
```
|
||||
|
||||
This command:
|
||||
|
||||
1. Deploys the Formbricks application with 3 replicas.
|
||||
2. Enables PostgreSQL and Redis with default settings.
|
||||
|
||||
#### Scaling PostgreSQL and Redis
|
||||
|
||||
For advanced configuration and scaling of PostgreSQL and Redis, refer to their respective Helm chart documentation:
|
||||
|
||||
- PostgreSQL Helm Chart: https://github.com/bitnami/charts/tree/master/bitnami/postgresql
|
||||
- Redis Helm Chart: https://github.com/bitnami/charts/tree/master/bitnami/redis
|
||||
|
||||
These documents provide detailed information on scaling and configuring high availability for each component.
|
||||
|
||||
4. **Custom Configuration with Environment Variables**:
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set env.SMTP_HOST=smtp.example.com \
|
||||
--set env.SMTP_PORT=587 \
|
||||
--set env.SMTP_USER=user@example.com \
|
||||
--set env.SMTP_PASSWORD=password123 \
|
||||
--set env.SMTP_AUTHENTICATED=1
|
||||
```
|
||||
|
||||
5. **Installation with Custom Resource Limits**:
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--namespace formbricks \
|
||||
--create-namespace \
|
||||
--set resources.limits.cpu=1 \
|
||||
--set resources.limits.memory=1Gi \
|
||||
--set resources.requests.cpu=500m \
|
||||
--set resources.requests.memory=512Mi
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
For detailed configuration options, please refer to the [Full Values Documentation](#full-values-documentation) section at the end of this document.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Formbricks supports various environment variables for configuration. Here are some key variables:
|
||||
|
||||
| Variable | Description | Required | Default |
|
||||
| ----------------- | -------------------------------- | -------- | ----------------------- |
|
||||
| `WEBAPP_URL` | Base URL of the site | Yes | `http://localhost:3000` |
|
||||
| `NEXTAUTH_URL` | Location of the auth server | Yes | `http://localhost:3000` |
|
||||
| `DATABASE_URL` | Database URL with credentials | Yes | - |
|
||||
| `NEXTAUTH_SECRET` | Secret for NextAuth | Yes | (Generated) |
|
||||
| `ENCRYPTION_KEY` | Secret for data encryption | Yes | (Generated) |
|
||||
| `CRON_SECRET` | API Secret for running cron jobs | Yes | (Generated) |
|
||||
| `...` | ... | ... | ... |
|
||||
|
||||
For a comprehensive list of supported environment variables, refer to the [Formbricks Configuration Documentation](https://formbricks.com/docs/self-hosting/configuration).
|
||||
|
||||
## Scaling
|
||||
|
||||
```bash
|
||||
kubectl scale deployment my-formbricks --replicas=5 -n formbricks
|
||||
```
|
||||
|
||||
This command scales the Formbricks deployment to 5 replicas. Replace `my-formbricks` with your actual deployment name if different.
|
||||
|
||||
### With Auto Scaling (Kubernetes Metrics Server Requirement)
|
||||
|
||||
The Formbricks Helm chart includes support for Horizontal Pod Autoscaling (HPA) to automatically adjust the number of pods based on CPU utilization. This feature is enabled by default and can be customized to suit your specific needs.
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks --namespace formbricks --create-namespace \
|
||||
--set autoscaling.enabled=true
|
||||
```
|
||||
|
||||
This configuration sets up autoscaling with a minimum of 2 replicas and a maximum of 5 replicas, targeting an average CPU utilization of 80%
|
||||
|
||||
### Customizing Autoscaling
|
||||
|
||||
To adjust the autoscaling settings, you can modify the values in your `values.yaml` file or use the `--set` flag when installing or upgrading the chart. Here are some common customizations:
|
||||
|
||||
1. Change the minimum and maximum number of replicas:
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--set autoscaling.enabled=true \
|
||||
--set autoscaling.minReplicas=3 \
|
||||
--set autoscaling.maxReplicas=10
|
||||
```
|
||||
|
||||
2. Adjust the target CPU utilization:
|
||||
|
||||
```bash
|
||||
helm install my-formbricks formbricks/formbricks \
|
||||
--set autoscaling.enabled=true \
|
||||
--set autoscaling.metrics[0].resource.target.averageUtilization=70
|
||||
```
|
||||
|
||||
3. Disable autoscaling:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks formbricks/formbricks \
|
||||
--set autoscaling.enabled=false
|
||||
```
|
||||
|
||||
### Kubernetes Metrics Server Requirement
|
||||
|
||||
For autoscaling to function properly, the Kubernetes Metrics Server must be installed in your cluster. The Metrics Server collects resource metrics from Kubelets and exposes them in the Kubernetes API server through the Metrics API.
|
||||
|
||||
If you don't have the Metrics Server installed, you can typically add it using the following command:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||
```
|
||||
|
||||
For more detailed information on installing and configuring the Metrics Server, please refer to the [official Kubernetes Metrics Server documentation](https://github.com/kubernetes-sigs/metrics-server).
|
||||
|
||||
### Advanced Autoscaling Configuration
|
||||
|
||||
The Formbricks Helm chart uses Kubernetes HPA v2, which allows for more advanced scaling behaviors. You can customize the `behavior` section in the `values.yaml` file to fine-tune how your application scales up and down. For more information on advanced HPA configurations, refer to the [Kubernetes HPA documentation](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/).
|
||||
|
||||
## Upgrading Formbricks
|
||||
|
||||
This section provides guidance on how to upgrade your Formbricks deployment using Helm, including examples of common upgrade scenarios.
|
||||
|
||||
### Upgrade Process
|
||||
|
||||
To upgrade your Formbricks deployment, use the `helm upgrade` command. Always ensure you have the latest version of the Formbricks Helm chart by running `helm repo update` before upgrading.
|
||||
|
||||
```bash
|
||||
helm repo update
|
||||
helm upgrade my-formbricks formbricks/formbricks --namespace formbricks
|
||||
```
|
||||
|
||||
### Common Upgrade Scenarios
|
||||
|
||||
#### 1. Updating Environment Variables
|
||||
|
||||
To update or add new environment variables, use the `--set` flag with the `env` prefix:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks formbricks/formbricks \
|
||||
--set env.SMTP_HOST=new-smtp.example.com \
|
||||
--set env.SMTP_PORT=587 \
|
||||
--set env.NEW_CUSTOM_VAR=newvalue
|
||||
```
|
||||
|
||||
This command updates the SMTP host and port, and adds a new custom environment variable.
|
||||
|
||||
#### 2. Enabling or Disabling Features
|
||||
|
||||
You can enable or disable features by updating their respective values:
|
||||
|
||||
```bash
|
||||
# Disable Redis
|
||||
helm upgrade my-formbricks formbricks/formbricks --set redis.enabled=false
|
||||
|
||||
# Enable Redis
|
||||
helm upgrade my-formbricks formbricks/formbricks --set redis.enabled=true
|
||||
```
|
||||
|
||||
#### 3. Scaling Resources
|
||||
|
||||
To adjust resource allocation:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks formbricks/formbricks \
|
||||
--set resources.limits.cpu=1 \
|
||||
--set resources.limits.memory=2Gi \
|
||||
--set resources.requests.cpu=500m \
|
||||
--set resources.requests.memory=1Gi
|
||||
```
|
||||
|
||||
#### 4. Updating Autoscaling Configuration
|
||||
|
||||
To modify autoscaling settings:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks formbricks/formbricks \
|
||||
--set autoscaling.minReplicas=3 \
|
||||
--set autoscaling.maxReplicas=10 \
|
||||
--set autoscaling.metrics[0].resource.target.averageUtilization=75
|
||||
```
|
||||
|
||||
#### 5. Changing Database Credentials
|
||||
|
||||
To update PostgreSQL database credentials:
|
||||
To switch from the built-in PostgreSQL to an external database or update the external database credentials:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks formbricks/formbricks \
|
||||
--set postgresql.enabled=false \
|
||||
--set postgresql.externalUrl="postgresql://newuser:newpassword@external-postgres-host:5432/newdatabase"
|
||||
```
|
||||
|
||||
This command disables the built-in PostgreSQL and configures Formbricks to use an external PostgreSQL database. Make sure your external database is set up and accessible before making this change.
|
||||
|
||||
### Using a Values File for Complex Upgrades
|
||||
|
||||
For more complex upgrades or when you need to change multiple values, it's recommended to use a values file:
|
||||
|
||||
1. Create a file named `upgrade-values.yaml` with your desired changes:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
SMTP_HOST: new-smtp.example.com
|
||||
SMTP_PORT: "587"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1
|
||||
memory: 2Gi
|
||||
autoscaling:
|
||||
minReplicas: 3
|
||||
maxReplicas: 10
|
||||
traefik:
|
||||
enabled: true
|
||||
postgresql:
|
||||
auth:
|
||||
username: newuser
|
||||
password: newpassword
|
||||
database: newdatabase
|
||||
```
|
||||
|
||||
2. Apply the upgrade using the values file:
|
||||
|
||||
```bash
|
||||
helm upgrade my-formbricks formbricks/formbricks -f upgrade-values.yaml
|
||||
```
|
||||
|
||||
Remember to always backup your data before performing upgrades, especially when modifying database-related settings.
|
||||
|
||||
## Support
|
||||
|
||||
For support with the Formbricks Helm chart:
|
||||
|
||||
- Open an issue on the [Formbricks GitHub repository](https://github.com/formbricks/formbricks)
|
||||
- Get help on [Github Discussions](https://github.com/formbricks/formbricks/discussions)
|
||||
- For enterprise support, contact us at hola@formbricks.com
|
||||
|
||||
## Full Values Documentation
|
||||
|
||||
Below is a comprehensive list of all configurable values in the Formbricks Helm chart:
|
||||
|
||||
| Field | Description | Default |
|
||||
| ----------------------------------------------------------- | ------------------------------------------ | ------------------------------- |
|
||||
| `image.repository` | Docker image repository for Formbricks | `ghcr.io/formbricks/formbricks` |
|
||||
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
|
||||
| `image.tag` | Docker image tag | `"2.6.0"` |
|
||||
| `service.type` | Kubernetes service type | `ClusterIP` |
|
||||
| `service.port` | Kubernetes service port | `80` |
|
||||
| `service.targetPort` | Container port to expose | `3000` |
|
||||
| `resources.limits.cpu` | CPU resource limit | `500m` |
|
||||
| `resources.limits.memory` | Memory resource limit | `1Gi` |
|
||||
| `resources.requests.cpu` | Memory resource request | `null` |
|
||||
| `resources.requests.memory` | Memory resource request | `null` |
|
||||
| `autoscaling.enabled` | Enable autoscaling | `false` |
|
||||
| `autoscaling.minReplicas` | Minimum number of replicas | `1` |
|
||||
| `autoscaling.maxReplicas` | Maximum number of replicas | `5` |
|
||||
| `autoscaling.metrics[0].type` | Type of metric for autoscaling | `Resource` |
|
||||
| `autoscaling.metrics[0].resource.name` | Resource name for autoscaling metric | `cpu` |
|
||||
| `autoscaling.metrics[0].resource.target.type` | Target type for autoscaling | `Utilization` |
|
||||
| `autoscaling.metrics[0].resource.target.averageUtilization` | Average utilization target for autoscaling | `80` |
|
||||
| `autoscaling.behavior.scaleDown.stabilizationWindowSeconds` | Stabilization window for scaling down | `300` |
|
||||
| `autoscaling.behavior.scaleUp.stabilizationWindowSeconds` | Stabilization window for scaling up | `0` |
|
||||
| `replicaCount` | Number of replicas | `1` |
|
||||
| `formbricksConfig.nextAuthSecret` | NextAuth secret | `""` |
|
||||
| `formbricksConfig.encryptionKey` | Encryption key | `""` |
|
||||
| `formbricksConfig.cronSecret` | Cron secret | `""` |
|
||||
| `env` | Additional environment variables | `{}` |
|
||||
| `hostname` | Hostname for Formbricks | `""` |
|
||||
| `traefik.enabled` | Enable Traefik ingress | `false` |
|
||||
| `traefik.ingressRoute.dashboard.enabled` | Enable Traefik dashboard | `false` |
|
||||
| `traefik.additionalArguments` | Additional arguments for Traefik | [See values.yaml] |
|
||||
| `traefik.tls.enabled` | Enable TLS for Traefik | `true` |
|
||||
| `traefik.tls.certResolver` | Cert resolver for Traefik | `letsencrypt` |
|
||||
| `traefik.ports.web.port` | HTTP port for Traefik | `80` |
|
||||
| `traefik.ports.websecure.port` | HTTPS port for Traefik | `443` |
|
||||
| `traefik.persistence.enabled` | Enable persistence for Traefik | `true` |
|
||||
| `traefik.persistence.size` | Size of persistent volume for Traefik | `128Mi` |
|
||||
| `traefik.podSecurityContext.fsGroup` | fsGroup for Traefik pods | `0` |
|
||||
| `traefik.hostNetwork` | Use host network for Traefik | `true` |
|
||||
| `traefik.securityContext` | Security context for Traefik | [See values.yaml] |
|
||||
| `redis.enabled` | Enable Redis | `true` |
|
||||
| `redis.externalUrl` | External Redis URL | `""` |
|
||||
| `redis.architecture` | Redis architecture | `standalone` |
|
||||
| `redis.auth.enabled` | Enable Redis authentication | `true` |
|
||||
| `redis.auth.password` | Redis password | `redispassword` |
|
||||
| `redis.master.persistence.enabled` | Enable persistence for Redis master | `false` |
|
||||
| `redis.replica.replicaCount` | Number of Redis replicas | `0` |
|
||||
| `postgresql.enabled` | Enable PostgreSQL | `true` |
|
||||
| `postgresql.externalUrl` | External PostgreSQL URL | `""` |
|
||||
| `postgresql.auth.username` | PostgreSQL username | `formbricks` |
|
||||
| `postgresql.auth.password` | PostgreSQL password | `formbrickspassword` |
|
||||
| `postgresql.auth.database` | PostgreSQL database name | `formbricks` |
|
||||
| `postgresql.primary.persistence.enabled` | Enable persistence for PostgreSQL | `true` |
|
||||
| `postgresql.primary.persistence.size` | Size of persistent volume for PostgreSQL | `10Gi` |
|
||||
|
||||
This table provides a comprehensive overview of all configurable fields in the Formbricks Helm chart, along with their descriptions and default values. Users can refer to this table to understand what each field does and how they can customize their Formbricks deployment.
|
||||
|
||||
## Full Values Documentation
|
||||
|
||||
Below is a comprehensive list of all configurable values in the Formbricks Helm chart:
|
||||
|
||||
```yaml
|
||||
image:
|
||||
repository: ghcr.io/formbricks/formbricks
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "2.6.0"
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 3000
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 2
|
||||
maxReplicas: 5
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 100
|
||||
periodSeconds: 15
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 0
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 100
|
||||
periodSeconds: 15
|
||||
- type: Pods
|
||||
value: 4
|
||||
periodSeconds: 15
|
||||
selectPolicy: Max
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
formbricksConfig:
|
||||
nextAuthSecret: ""
|
||||
encryptionKey: ""
|
||||
cronSecret: ""
|
||||
|
||||
env: {}
|
||||
|
||||
hostname: ""
|
||||
|
||||
traefik:
|
||||
enabled: false
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: false
|
||||
additionalArguments:
|
||||
- "--providers.file.filename=/config/traefik.toml"
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: letsencrypt
|
||||
ports:
|
||||
web:
|
||||
port: 80
|
||||
websecure:
|
||||
port: 443
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: letsencrypt
|
||||
persistence:
|
||||
enabled: true
|
||||
name: traefik-acme
|
||||
accessMode: ReadWriteOnce
|
||||
size: 128Mi
|
||||
path: /data
|
||||
podSecurityContext:
|
||||
fsGroup: 0
|
||||
hostNetwork: true
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- NET_BIND_SERVICE
|
||||
- NET_BROADCAST
|
||||
- NET_RAW
|
||||
runAsUser: 0
|
||||
runAsGroup: 0
|
||||
runAsNonRoot: false
|
||||
readOnlyRootFilesystem: true
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
externalUrl: ""
|
||||
architecture: standalone
|
||||
auth:
|
||||
enabled: true
|
||||
password: redispassword
|
||||
master:
|
||||
persistence:
|
||||
enabled: false
|
||||
replica:
|
||||
replicaCount: 0
|
||||
|
||||
postgresql:
|
||||
enabled: false
|
||||
externalUrl: ""
|
||||
auth:
|
||||
username: formbricks
|
||||
password: formbrickspassword
|
||||
database: formbricks
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 10Gi
|
||||
```
|
||||
|
||||
You can customize these values by creating a `values.yaml` file or by using the `--set` flag when running `helm install` or `helm upgrade`.
|
||||
|
||||
## ✍️ Contribution
|
||||
|
||||
We are very happy if you are interested in contributing to Formbricks 🤗
|
||||
|
||||
Here are a few options:
|
||||
|
||||
- Star this repo.
|
||||
|
||||
- Create issues every time you feel something is missing or goes wrong.
|
||||
|
||||
- Upvote issues with 👍 reaction so we know what the demand for a particular issue is to prioritize it within the roadmap.
|
||||
|
||||
Please check out [our contribution guide](https://formbricks.com/docs/developer-docs/contributing/get-started) and our [list of open issues](https://github.com/formbricks/formbricks/issues) for more information.
|
||||
|
||||
## MicroK8s Installation and Formbricks Deployment
|
||||
|
||||
### MicroK8s Quick Setup
|
||||
|
||||
1. Install MicroK8s:
|
||||
|
||||
```bash
|
||||
sudo snap install microk8s --classic
|
||||
```
|
||||
|
||||
2. Enable necessary add-ons:
|
||||
```bash
|
||||
microk8s enable dns storage ingress helm3
|
||||
```
|
||||
|
||||
### Deploying Formbricks on MicroK8s
|
||||
|
||||
1. Add the Formbricks Helm repository:
|
||||
|
||||
```bash
|
||||
microk8s helm3 repo add formbricks https://charts.formbricks.com
|
||||
microk8s helm3 repo update
|
||||
```
|
||||
|
||||
2. Install Formbricks:
|
||||
```bash
|
||||
microk8s helm3 install my-formbricks formbricks/formbricks --namespace formbricks --create-namespace
|
||||
```
|
||||
|
||||
For more detailed information on MicroK8s, including advanced configuration and usage, please refer to the [official MicroK8s documentation](https://microk8s.io/docs).
|
||||
|
||||
For Formbricks Helm chart configuration options, see the [Configuration](#configuration) and [Full Values Documentation](#full-values-documentation) sections of this document.
|
||||
```
|
||||
````
|
||||
# formbricks
|
||||
|
||||
  
|
||||
|
||||
A Helm chart for Formbricks with PostgreSQL, Redis
|
||||
|
||||
**Homepage:** <https://formbricks.com/docs/self-hosting/setup/kubernetes>
|
||||
|
||||
## Maintainers
|
||||
|
||||
| Name | Email | Url |
|
||||
| ---- | ------ | --- |
|
||||
| Formbricks | <info@formbricks.com> | |
|
||||
|
||||
## Requirements
|
||||
|
||||
| Repository | Name | Version |
|
||||
|------------|------|---------|
|
||||
| oci://registry-1.docker.io/bitnamicharts | postgresql | 16.4.16 |
|
||||
| oci://registry-1.docker.io/bitnamicharts | redis | 20.11.2 |
|
||||
|
||||
## Values
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| autoscaling.additionalLabels | object | `{}` | |
|
||||
| autoscaling.annotations | object | `{}` | |
|
||||
| autoscaling.enabled | bool | `true` | |
|
||||
| autoscaling.maxReplicas | int | `10` | |
|
||||
| autoscaling.metrics[0].resource.name | string | `"cpu"` | |
|
||||
| autoscaling.metrics[0].resource.target.averageUtilization | int | `60` | |
|
||||
| autoscaling.metrics[0].resource.target.type | string | `"Utilization"` | |
|
||||
| autoscaling.metrics[0].type | string | `"Resource"` | |
|
||||
| autoscaling.metrics[1].resource.name | string | `"memory"` | |
|
||||
| autoscaling.metrics[1].resource.target.averageUtilization | int | `60` | |
|
||||
| autoscaling.metrics[1].resource.target.type | string | `"Utilization"` | |
|
||||
| autoscaling.metrics[1].type | string | `"Resource"` | |
|
||||
| autoscaling.minReplicas | int | `1` | |
|
||||
| componentOverride | string | `""` | |
|
||||
| cronJob.enabled | bool | `false` | |
|
||||
| cronJob.jobs | object | `{}` | |
|
||||
| deployment.additionalLabels | object | `{}` | |
|
||||
| deployment.additionalPodAnnotations | object | `{}` | |
|
||||
| deployment.additionalPodLabels | object | `{}` | |
|
||||
| deployment.affinity | object | `{}` | |
|
||||
| deployment.annotations | object | `{}` | |
|
||||
| deployment.args | list | `[]` | |
|
||||
| deployment.command | list | `[]` | |
|
||||
| deployment.containerSecurityContext.readOnlyRootFilesystem | bool | `true` | |
|
||||
| deployment.containerSecurityContext.runAsNonRoot | bool | `true` | |
|
||||
| deployment.env.EMAIL_VERIFICATION_DISABLED.value | string | `"1"` | |
|
||||
| deployment.env.PASSWORD_RESET_DISABLED.value | string | `"1"` | |
|
||||
| deployment.envFrom | string | `nil` | |
|
||||
| deployment.image.digest | string | `""` | |
|
||||
| deployment.image.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| deployment.image.repository | string | `"ghcr.io/formbricks/formbricks"` | |
|
||||
| deployment.imagePullSecrets | string | `""` | |
|
||||
| deployment.nodeSelector | object | `{}` | |
|
||||
| deployment.ports.http.containerPort | int | `3000` | |
|
||||
| deployment.ports.http.exposed | bool | `true` | |
|
||||
| deployment.ports.http.protocol | string | `"TCP"` | |
|
||||
| deployment.ports.metrics.containerPort | int | `9464` | |
|
||||
| deployment.ports.metrics.exposed | bool | `true` | |
|
||||
| deployment.ports.metrics.protocol | string | `"TCP"` | |
|
||||
| deployment.probes.livenessProbe.failureThreshold | int | `5` | |
|
||||
| deployment.probes.livenessProbe.httpGet.path | string | `"/health"` | |
|
||||
| deployment.probes.livenessProbe.httpGet.port | int | `3000` | |
|
||||
| deployment.probes.livenessProbe.initialDelaySeconds | int | `10` | |
|
||||
| deployment.probes.livenessProbe.periodSeconds | int | `10` | |
|
||||
| deployment.probes.livenessProbe.successThreshold | int | `1` | |
|
||||
| deployment.probes.livenessProbe.timeoutSeconds | int | `5` | |
|
||||
| deployment.probes.readinessProbe.failureThreshold | int | `5` | |
|
||||
| deployment.probes.readinessProbe.httpGet.path | string | `"/health"` | |
|
||||
| deployment.probes.readinessProbe.httpGet.port | int | `3000` | |
|
||||
| deployment.probes.readinessProbe.initialDelaySeconds | int | `10` | |
|
||||
| deployment.probes.readinessProbe.periodSeconds | int | `10` | |
|
||||
| deployment.probes.readinessProbe.successThreshold | int | `1` | |
|
||||
| deployment.probes.readinessProbe.timeoutSeconds | int | `5` | |
|
||||
| deployment.probes.startupProbe.failureThreshold | int | `30` | |
|
||||
| deployment.probes.startupProbe.periodSeconds | int | `10` | |
|
||||
| deployment.probes.startupProbe.tcpSocket.port | int | `3000` | |
|
||||
| deployment.reloadOnChange | bool | `false` | |
|
||||
| deployment.replicas | int | `1` | |
|
||||
| deployment.resources.limits.memory | string | `"2Gi"` | |
|
||||
| deployment.resources.requests.cpu | string | `"1"` | |
|
||||
| deployment.resources.requests.memory | string | `"1Gi"` | |
|
||||
| deployment.revisionHistoryLimit | int | `2` | |
|
||||
| deployment.securityContext | object | `{}` | |
|
||||
| deployment.strategy.type | string | `"RollingUpdate"` | |
|
||||
| deployment.tolerations | list | `[]` | |
|
||||
| deployment.topologySpreadConstraints | list | `[]` | |
|
||||
| enterprise.enabled | bool | `false` | |
|
||||
| enterprise.licenseKey | string | `""` | |
|
||||
| externalSecret.enabled | bool | `false` | |
|
||||
| externalSecret.files | object | `{}` | |
|
||||
| externalSecret.refreshInterval | string | `"1h"` | |
|
||||
| externalSecret.secretStore.kind | string | `"ClusterSecretStore"` | |
|
||||
| externalSecret.secretStore.name | string | `"aws-secrets-manager"` | |
|
||||
| ingress.annotations | object | `{}` | |
|
||||
| ingress.enabled | bool | `false` | |
|
||||
| ingress.hosts[0].host | string | `"k8s.formbricks.com"` | |
|
||||
| ingress.hosts[0].paths[0].path | string | `"/"` | |
|
||||
| ingress.hosts[0].paths[0].pathType | string | `"Prefix"` | |
|
||||
| ingress.hosts[0].paths[0].serviceName | string | `"formbricks"` | |
|
||||
| ingress.ingressClassName | string | `"alb"` | |
|
||||
| nameOverride | string | `""` | |
|
||||
| partOfOverride | string | `""` | |
|
||||
| postgresql.auth.database | string | `"formbricks"` | |
|
||||
| postgresql.auth.existingSecret | string | `"formbricks-app-secrets"` | |
|
||||
| postgresql.auth.secretKeys.adminPasswordKey | string | `"POSTGRES_ADMIN_PASSWORD"` | |
|
||||
| postgresql.auth.secretKeys.userPasswordKey | string | `"POSTGRES_USER_PASSWORD"` | |
|
||||
| postgresql.auth.username | string | `"formbricks"` | |
|
||||
| postgresql.enabled | bool | `true` | |
|
||||
| postgresql.externalDatabaseUrl | string | `""` | |
|
||||
| postgresql.fullnameOverride | string | `"formbricks-postgresql"` | |
|
||||
| postgresql.global.security.allowInsecureImages | bool | `true` | |
|
||||
| postgresql.image.repository | string | `"pgvector/pgvector"` | |
|
||||
| postgresql.image.tag | string | `"0.8.0-pg17"` | |
|
||||
| postgresql.primary.containerSecurityContext.enabled | bool | `true` | |
|
||||
| postgresql.primary.containerSecurityContext.readOnlyRootFilesystem | bool | `false` | |
|
||||
| postgresql.primary.containerSecurityContext.runAsUser | int | `1001` | |
|
||||
| postgresql.primary.networkPolicy.enabled | bool | `false` | |
|
||||
| postgresql.primary.persistence.enabled | bool | `true` | |
|
||||
| postgresql.primary.persistence.size | string | `"10Gi"` | |
|
||||
| postgresql.primary.podSecurityContext.enabled | bool | `true` | |
|
||||
| postgresql.primary.podSecurityContext.fsGroup | int | `1001` | |
|
||||
| postgresql.primary.podSecurityContext.runAsUser | int | `1001` | |
|
||||
| rbac.enabled | bool | `false` | |
|
||||
| rbac.serviceAccount.additionalLabels | object | `{}` | |
|
||||
| rbac.serviceAccount.annotations | object | `{}` | |
|
||||
| rbac.serviceAccount.enabled | bool | `false` | |
|
||||
| rbac.serviceAccount.name | string | `""` | |
|
||||
| redis.architecture | string | `"standalone"` | |
|
||||
| redis.auth.enabled | bool | `true` | |
|
||||
| redis.auth.existingSecret | string | `"formbricks-app-secrets"` | |
|
||||
| redis.auth.existingSecretPasswordKey | string | `"REDIS_PASSWORD"` | |
|
||||
| redis.enabled | bool | `true` | |
|
||||
| redis.externalRedisUrl | string | `""` | |
|
||||
| redis.fullnameOverride | string | `"formbricks-redis"` | |
|
||||
| redis.master.persistence.enabled | bool | `true` | |
|
||||
| redis.networkPolicy.enabled | bool | `false` | |
|
||||
| secret.enabled | bool | `true` | |
|
||||
| service.additionalLabels | object | `{}` | |
|
||||
| service.annotations | object | `{}` | |
|
||||
| service.enabled | bool | `true` | |
|
||||
| service.ports | list | `[]` | |
|
||||
| service.type | string | `"ClusterIP"` | |
|
||||
| serviceMonitor.additionalLabels | string | `nil` | |
|
||||
| serviceMonitor.annotations | string | `nil` | |
|
||||
| serviceMonitor.enabled | bool | `true` | |
|
||||
| serviceMonitor.endpoints[0].interval | string | `"5s"` | |
|
||||
| serviceMonitor.endpoints[0].path | string | `"/metrics"` | |
|
||||
| serviceMonitor.endpoints[0].port | string | `"metrics"` | |
|
||||
|
||||
----------------------------------------------
|
||||
Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2)
|
||||
|
||||
Binary file not shown.
BIN
helm-chart/charts/postgresql-16.4.16.tgz
Normal file
BIN
helm-chart/charts/postgresql-16.4.16.tgz
Normal file
Binary file not shown.
Binary file not shown.
BIN
helm-chart/charts/redis-20.11.2.tgz
Normal file
BIN
helm-chart/charts/redis-20.11.2.tgz
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,97 +1,161 @@
|
||||
NOTES:
|
||||
Thank you for installing Formbricks! Here's how you can access and manage your deployment:
|
||||
|
||||
1. Accessing Your Application:
|
||||
{{- if .Values.traefik.enabled }}
|
||||
Traefik is enabled for ingress routing.
|
||||
Your application should be available at: https://{{ .Values.hostname }}
|
||||
Note: Ensure your DNS is properly configured to point to your cluster's IP.
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
Your application is running inside the cluster with ClusterIP service type.
|
||||
To access it locally, run the following command:
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "formbricks.fullname" . }} 8080:{{ .Values.service.port }}
|
||||
Then visit http://localhost:8080 in your browser.
|
||||
|
||||
{{ .Release.Name | camelcase }} with {{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag }} has been deployed successfully on {{ template "formbricks.namespace" .}} namespace !
|
||||
|
||||
Here's how you can access and manage your deployment:
|
||||
---
|
||||
|
||||
Accessing Your Application:
|
||||
|
||||
{{- if .Values.ingress.enabled }}
|
||||
Your application is accessible via the configured Ingress.
|
||||
```sh
|
||||
kubectl get ingress {{ include "formbricks.name" . }} -n {{ .Release.Namespace }} -o jsonpath='{.items[*].spec.rules[*].host}' | tr ' ' '\n'
|
||||
```
|
||||
Ensure that your DNS points to the cluster's Ingress Controller.
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
Your application is exposed via a LoadBalancer.
|
||||
Run the following to get the external IP:
|
||||
```sh
|
||||
kubectl get svc {{ include "formbricks.name" . }} -n {{ .Release.Namespace }}
|
||||
```
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
Your application is accessible via NodePort.
|
||||
Run the following to get the assigned port:
|
||||
```sh
|
||||
kubectl get svc {{ include "formbricks.name" . }} -n {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}"
|
||||
```
|
||||
Then access the service at: `http://<NodeIP>:<NodePort>`
|
||||
{{- else }}
|
||||
Your application is running inside the cluster (ClusterIP service type).
|
||||
To access it locally, run:
|
||||
```sh
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "formbricks.name" . }} 3000
|
||||
```
|
||||
Then visit: **http://localhost:3000**
|
||||
{{- end }}
|
||||
|
||||
2. Database (PostgreSQL) Access:
|
||||
---
|
||||
|
||||
Database (PostgreSQL) Access:
|
||||
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
PostgreSQL is deployed within your cluster.
|
||||
To get the PostgreSQL password, run:
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ .Release.Name }}-postgresql -o jsonpath="{.data.postgres-password}" | base64 --decode
|
||||
|
||||
Database connection details:
|
||||
- Host: {{ .Release.Name }}-postgresql
|
||||
- Port: 5432
|
||||
- Database: {{ .Values.postgresql.auth.database }}
|
||||
- Username: {{ .Values.postgresql.auth.username }}
|
||||
{{- else if .Values.postgresql.externalUrl }}
|
||||
Retrieve the password using:
|
||||
```sh
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.name" . }}-app-secrets -o jsonpath="{.data.POSTGRES_USER_PASSWORD}" | base64 --decode
|
||||
```
|
||||
Connection details:
|
||||
- **Host**: `{{ include "formbricks.name" . }}-postgresql`
|
||||
- **Port**: `5432`
|
||||
- **Database**: `{{ .Values.postgresql.auth.database }}`
|
||||
- **Username**: `{{ .Values.postgresql.auth.username }}`
|
||||
{{- else if .Values.postgresql.externalDatabaseUrl }}
|
||||
You're using an external PostgreSQL database.
|
||||
Connection URL: {{ .Values.postgresql.externalUrl }}
|
||||
Connection URL:
|
||||
```sh
|
||||
echo "{{ .Values.postgresql.externalDatabaseUrl }}"
|
||||
```
|
||||
{{- end }}
|
||||
|
||||
3. Redis Access:
|
||||
---
|
||||
|
||||
Redis Access:
|
||||
|
||||
{{- if .Values.redis.enabled }}
|
||||
Redis is deployed within your cluster.
|
||||
To get the Redis password, run:
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ .Release.Name }}-redis -o jsonpath="{.data.redis-password}" | base64 --decode
|
||||
|
||||
Redis connection details:
|
||||
- Host: {{ .Release.Name }}-redis-master
|
||||
- Port: 6379
|
||||
{{- else if .Values.redis.externalUrl }}
|
||||
Retrieve the password using:
|
||||
```sh
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.name" . }}-app-secrets -o jsonpath="{.data.REDIS_PASSWORD}" | base64 --decode
|
||||
```
|
||||
Connection details:
|
||||
- **Host**: `{{ include "formbricks.name" . }}-redis-master`
|
||||
- **Port**: `6379`
|
||||
{{- else if .Values.redis.externalRedisUrl }}
|
||||
You're using an external Redis instance.
|
||||
Connection URL: {{ .Values.redis.externalUrl }}
|
||||
Connection URL:
|
||||
```sh
|
||||
echo "{{ .Values.redis.externalRedisUrl }}"
|
||||
```
|
||||
{{- else }}
|
||||
Redis is not enabled in your current configuration.
|
||||
Redis is not enabled in this deployment.
|
||||
{{- end }}
|
||||
|
||||
4. Environment Variables:
|
||||
The following environment variables have been automatically generated:
|
||||
- NEXTAUTH_SECRET: A random 32-character string
|
||||
- ENCRYPTION_KEY: A random 32-character string
|
||||
- CRON_SECRET: A random 32-character string
|
||||
---
|
||||
|
||||
To view these secrets, run:
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.fullname" . }}-secrets -o jsonpath="{.data.NEXTAUTH_SECRET}" | base64 --decode
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.fullname" . }}-secrets -o jsonpath="{.data.ENCRYPTION_KEY}" | base64 --decode
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.fullname" . }}-secrets -o jsonpath="{.data.CRON_SECRET}" | base64 --decode
|
||||
Environment Variables:
|
||||
The following environment variables have been automatically generated:
|
||||
|
||||
- `NEXTAUTH_SECRET`: A random 32-character string
|
||||
- `ENCRYPTION_KEY`: A random 32-character string
|
||||
- `CRON_SECRET`: A random 32-character string
|
||||
- 'EMAIL_VERIFICATION_DISABLED': 1 # By Default email verification is disabled, configure SMTP settings to enable(https://formbricks.com/docs/self-hosting/configuration/smtp)
|
||||
- 'PASSWORD_RESET_DISABLED': 1 # By Default password reset is disabled, configure SMTP settings to enable(https://formbricks.com/docs/self-hosting/configuration/smtp)
|
||||
|
||||
Retrieve them using:
|
||||
```sh
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.name" . }}-app-secrets -o jsonpath="{.data.NEXTAUTH_SECRET}" | base64 --decode
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.name" . }}-app-secrets -o jsonpath="{.data.ENCRYPTION_KEY}" | base64 --decode
|
||||
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "formbricks.name" . }}-app-secrets -o jsonpath="{.data.CRON_SECRET}" | base64 --decode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Scaling:
|
||||
|
||||
5. Scaling:
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
Horizontal Pod Autoscaling is enabled.
|
||||
- Minimum replicas: {{ .Values.autoscaling.minReplicas }}
|
||||
- Maximum replicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
- Target CPU utilization: {{ .Values.autoscaling.metrics.averageUtilization }}%
|
||||
|
||||
To check the current status of the HPA, run:
|
||||
kubectl get hpa -n {{ .Release.Namespace }} {{ include "formbricks.fullname" . }}
|
||||
Horizontal Pod Autoscaling (HPA) is enabled.
|
||||
- **Min Replicas**: `{{ .Values.autoscaling.minReplicas }}`
|
||||
- **Max Replicas**: `{{ .Values.autoscaling.maxReplicas }}`
|
||||
|
||||
Check HPA status:
|
||||
```sh
|
||||
kubectl get hpa -n {{ .Release.Namespace }} {{ include "formbricks.name" . }}
|
||||
```
|
||||
{{- else }}
|
||||
Horizontal Pod Autoscaling is not enabled. Your deployment has a fixed number of {{ .Values.replicaCount }} replicas.
|
||||
To scale manually, use:
|
||||
kubectl scale deployment -n {{ .Release.Namespace }} {{ include "formbricks.fullname" . }} --replicas=<desired_number>
|
||||
HPA is **not enabled**. Your deployment has a fixed number of `{{ .Values.replicaCount }}` replicas.
|
||||
Manually scale using:
|
||||
```sh
|
||||
kubectl scale deployment -n {{ .Release.Namespace }} {{ include "formbricks.name" . }} --replicas=<desired_number>
|
||||
```
|
||||
{{- end }}
|
||||
|
||||
6. Persistence:
|
||||
- PostgreSQL data is persisted with a {{ .Values.postgresql.primary.persistence.size }} storage.
|
||||
---
|
||||
|
||||
External Secrets:
|
||||
{{- if .Values.externalSecret.enabled }}
|
||||
External secrets are enabled.
|
||||
Ensure that your `SecretStore` or `ClusterSecretStore` is configured properly.
|
||||
- Secret Store Name: `{{ .Values.externalSecret.secretStore.name }}`
|
||||
- Refresh Interval: `{{ .Values.externalSecret.refreshInterval }}`
|
||||
|
||||
Verify the external secret:
|
||||
```sh
|
||||
kubectl get externalsecrets -n {{ .Release.Namespace }}
|
||||
```
|
||||
{{- else }}
|
||||
External secrets are **not enabled**.
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
||||
Persistence:
|
||||
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
PostgreSQL data is persisted with `{{ .Values.postgresql.primary.persistence.size }}` storage.
|
||||
{{- end }}
|
||||
{{- if .Values.redis.enabled }}
|
||||
- Redis data is not persisted (persistence is disabled).
|
||||
Redis data is persisted with `{{ .Values.redis.master.persistence.size }}` storage..
|
||||
{{- end }}
|
||||
|
||||
7. Traefik Configuration:
|
||||
{{- if .Values.traefik.enabled }}
|
||||
Traefik is configured with the following settings:
|
||||
- TLS enabled with Let's Encrypt
|
||||
- HTTP to HTTPS redirect enabled
|
||||
- ACME challenge type: HTTP
|
||||
- Entrypoints: web (80) and websecure (443)
|
||||
{{- else }}
|
||||
Traefik is not enabled in your current configuration.
|
||||
{{- end }}
|
||||
---
|
||||
|
||||
8. Formbricks Documentation and Support:
|
||||
For more information, advanced configuration options, and support, please visit:
|
||||
- Official Formbricks website: https://formbricks.com
|
||||
- Documentation: https://formbricks.com/docs
|
||||
Formbricks Documentation and Support:
|
||||
|
||||
If you encounter any issues or have questions, please refer to the Formbricks documentation
|
||||
or reach out to the Formbricks community for support.
|
||||
For more information, advanced configurations, and support, visit:
|
||||
- **Official Website**: https://formbricks.com
|
||||
- **Documentation**: https://formbricks.com/docs
|
||||
|
||||
For troubleshooting, refer to the documentation or community support.
|
||||
|
||||
---
|
||||
|
||||
@@ -1,51 +1,141 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
This function ensures that the chart name is either taken from `nameOverride` or defaults to `.Chart.Name`.
|
||||
It also truncates the name to a maximum of 63 characters and removes trailing hyphens.
|
||||
*/}}
|
||||
{{- define "formbricks.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "formbricks.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
Define the application version to be used in labels.
|
||||
The version is taken from `.Values.deployment.image.tag` if provided, otherwise it defaults to `.Chart.Version`.
|
||||
It ensures the version only contains alphanumeric characters, underscores, dots, or hyphens, replacing any invalid characters with a hyphen.
|
||||
*/}}
|
||||
{{- define "formbricks.version" -}}
|
||||
{{- $appVersion := default .Chart.Version .Values.deployment.image.tag -}}
|
||||
{{- regexReplaceAll "[^a-zA-Z0-9_\\.\\-]" $appVersion "-" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Generate a chart name and version string to be used in Helm chart labels.
|
||||
This follows the format: `<ChartName>-<ChartVersion>`, replacing `+` with `_` and truncating to 63 characters.
|
||||
*/}}
|
||||
{{- define "formbricks.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
Common labels applied to Kubernetes resources.
|
||||
These labels help identify and manage the application.
|
||||
*/}}
|
||||
{{- define "formbricks.labels" -}}
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
|
||||
# Selector labels
|
||||
{{ include "formbricks.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
|
||||
# Application version label
|
||||
{{- with include "formbricks.version" . }}
|
||||
app.kubernetes.io/version: {{ . | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Managed by Helm
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
|
||||
# Part of label, defaults to the chart name if `partOfOverride` is not provided.
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
Selector labels used for identifying workloads in Kubernetes.
|
||||
These labels ensure that selectors correctly map to the deployed resources.
|
||||
*/}}
|
||||
{{- define "formbricks.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "formbricks.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: {{ .Values.componentOverride | default (include "formbricks.name" .) }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Renders a value that contains a Helm template.
|
||||
Usage:
|
||||
{{ include "formbricks.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }}
|
||||
This function allows rendering values dynamically.
|
||||
*/}}
|
||||
{{- define "formbricks.tplvalues.render" -}}
|
||||
{{- if typeIs "string" .value }}
|
||||
{{- tpl .value .context }}
|
||||
{{- else }}
|
||||
{{- tpl (.value | toYaml) .context }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Allow the release namespace to be overridden.
|
||||
If `namespaceOverride` is provided, it will be used; otherwise, it defaults to `.Release.Namespace`.
|
||||
*/}}
|
||||
{{- define "formbricks.namespace" -}}
|
||||
{{- default .Release.Namespace .Values.namespaceOverride -}}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
{{- define "formbricks.postgresAdminPassword" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-app-secrets" (include "formbricks.name" .))) }}
|
||||
{{- if and $secret (index $secret.data "POSTGRES_ADMIN_PASSWORD") }}
|
||||
{{- index $secret.data "POSTGRES_ADMIN_PASSWORD" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 16 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.postgresUserPassword" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-app-secrets" (include "formbricks.name" .))) }}
|
||||
{{- if and $secret (index $secret.data "POSTGRES_USER_PASSWORD") }}
|
||||
{{- index $secret.data "POSTGRES_USER_PASSWORD" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 16 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.redisPassword" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-app-secrets" (include "formbricks.name" .))) }}
|
||||
{{- if and $secret (index $secret.data "REDIS_PASSWORD") }}
|
||||
{{- index $secret.data "REDIS_PASSWORD" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 16 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.cronSecret" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-app-secrets" (include "formbricks.name" .))) }}
|
||||
{{- if $secret }}
|
||||
{{- index $secret.data "CRON_SECRET" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 32 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.encryptionKey" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-app-secrets" (include "formbricks.name" .))) }}
|
||||
{{- if $secret }}
|
||||
{{- index $secret.data "ENCRYPTION_KEY" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 32 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.nextAuthSecret" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-app-secrets" (include "formbricks.name" .))) }}
|
||||
{{- if $secret }}
|
||||
{{- index $secret.data "NEXTAUTH_SECRET" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 32 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
102
helm-chart/templates/cronjob.yaml
Normal file
102
helm-chart/templates/cronjob.yaml
Normal file
@@ -0,0 +1,102 @@
|
||||
{{- if (.Values.cronJob).enabled }}
|
||||
{{- range $name, $job := .Values.cronJob.jobs }}
|
||||
---
|
||||
apiVersion: {{ if $.Capabilities.APIVersions.Has "batch/v1/CronJob" }}batch/v1{{ else }}batch/v1beta1{{ end }}
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
labels:
|
||||
# Standard labels for tracking CronJobs
|
||||
{{- include "formbricks.labels" $ | nindent 4 }}
|
||||
|
||||
# Additional labels if specified
|
||||
{{- if $job.additionalLabels }}
|
||||
{{- toYaml $job.additionalLabels | indent 4 }}
|
||||
{{- end }}
|
||||
|
||||
# Additional annotations if specified
|
||||
{{- if $job.annotations }}
|
||||
annotations:
|
||||
{{- toYaml $job.annotations | indent 4 }}
|
||||
{{- end }}
|
||||
|
||||
spec:
|
||||
# Define the execution schedule for the job
|
||||
schedule: {{ $job.schedule | quote }}
|
||||
|
||||
# Kubernetes 1.27+ supports time zones for CronJobs
|
||||
{{- if ge (int $.Capabilities.KubeVersion.Minor) 27 }}
|
||||
{{- if $job.timeZone }}
|
||||
timeZone: {{ $job.timeZone }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
# Define job retention policies
|
||||
{{- if $job.successfulJobsHistoryLimit }}
|
||||
successfulJobsHistoryLimit: {{ $job.successfulJobsHistoryLimit }}
|
||||
{{- end }}
|
||||
{{- if $job.failedJobsHistoryLimit }}
|
||||
failedJobsHistoryLimit: {{ $job.failedJobsHistoryLimit }}
|
||||
{{- end }}
|
||||
|
||||
# Define concurrency policy
|
||||
{{- if $job.concurrencyPolicy }}
|
||||
concurrencyPolicy: {{ $job.concurrencyPolicy }}
|
||||
{{- end }}
|
||||
|
||||
jobTemplate:
|
||||
spec:
|
||||
{{- with $job.activeDeadlineSeconds }}
|
||||
activeDeadlineSeconds: {{ . }}
|
||||
{{- end }}
|
||||
{{- if not (kindIs "invalid" $job.backoffLimit) }}
|
||||
backoffLimit: {{ $job.backoffLimit }}
|
||||
{{- end }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "formbricks.labels" $ | nindent 12 }}
|
||||
|
||||
# Additional pod-level labels
|
||||
{{- with $job.additionalPodLabels }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
# Additional annotations
|
||||
{{- with $job.additionalPodAnnotations }}
|
||||
annotations: {{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
spec:
|
||||
# Define the service account if RBAC is enabled
|
||||
{{- if $.Values.rbac.enabled }}
|
||||
serviceAccountName: {{ template "formbricks.name" $ }}
|
||||
{{- end }}
|
||||
|
||||
# Define the job container
|
||||
containers:
|
||||
- name: {{ $name }}
|
||||
image: "{{ required "Image repository is undefined" $job.image.repository }}:{{ $job.image.tag | default "latest" }}"
|
||||
imagePullPolicy: {{ $job.image.imagePullPolicy | default "IfNotPresent" }}
|
||||
|
||||
# Environment variables from values
|
||||
{{- with $job.env }}
|
||||
env:
|
||||
{{- range $key, $value := $job.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
# Define command and arguments if specified
|
||||
{{- with $job.command }}
|
||||
command: {{- toYaml . | indent 14 }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $job.args }}
|
||||
args: {{- toYaml . | indent 14 }}
|
||||
{{- end }}
|
||||
|
||||
restartPolicy: {{ $job.restartPolicy | default "OnFailure" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,34 +1,182 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "formbricks.fullname" . }}
|
||||
name: {{ include "formbricks.name" . }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" . | nindent 4 }}
|
||||
{{- if .Values.deployment.additionalLabels }}
|
||||
{{- toYaml .Values.deployment.additionalLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.deployment.annotations .Values.deployment.reloadOnChange }}
|
||||
annotations:
|
||||
{{- if .Values.deployment.annotations }}
|
||||
{{- toYaml .Values.deployment.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- if .Values.deployment.replicas }}
|
||||
replicas: {{ .Values.deployment.replicas }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "formbricks.selectorLabels" . | nindent 6 }}
|
||||
{{- if .Values.deployment.strategy }}
|
||||
strategy:
|
||||
{{- toYaml .Values.deployment.strategy | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if not (kindIs "invalid" .Values.deployment.revisionHistoryLimit) }}
|
||||
revisionHistoryLimit: {{ .Values.deployment.revisionHistoryLimit }}
|
||||
{{- end }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "formbricks.selectorLabels" . | nindent 8 }}
|
||||
{{- if .Values.deployment.additionalPodLabels }}
|
||||
{{- toYaml .Values.deployment.additionalPodLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.disableIstioInject }}
|
||||
sidecar.istio.io/inject: "false"
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.additionalPodAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml .Values.deployment.additionalPodAnnotations | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.deployment.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml .Values.deployment.nodeSelector | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml .Values.deployment.tolerations | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.affinity }}
|
||||
affinity:
|
||||
{{- toYaml .Values.deployment.affinity | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{- toYaml .Values.deployment.topologySpreadConstraints | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml .Values.deployment.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.hostNetwork }}
|
||||
hostNetwork: true
|
||||
{{- end }}
|
||||
{{- if .Values.rbac.serviceAccount.enabled }}
|
||||
serviceAccountName: {{ .Values.rbac.serviceAccount.name | default (include "formbricks.name" .) }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.securityContext }}
|
||||
securityContext:
|
||||
{{ toYaml .Values.deployment.securityContext | indent 8 }}
|
||||
{{- end }}
|
||||
terminationGracePeriodSeconds: {{ .Values.deployment.terminationGracePeriodSeconds | default 30 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
- name: {{ template "formbricks.name" . }}
|
||||
image: "{{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.deployment.image.pullPolicy }}
|
||||
{{- if .Values.deployment.command }}
|
||||
command:
|
||||
{{- toYaml .Values.deployment.command | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.args }}
|
||||
args:
|
||||
{{- toYaml .Values.deployment.args | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.ports }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
protocol: TCP
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: {{ include "formbricks.fullname" . }}-secrets
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- range $name, $config := .Values.deployment.ports }}
|
||||
- name: {{ $name | quote }}
|
||||
containerPort: {{ $config.containerPort | default $config.port }}
|
||||
protocol: {{ $config.protocol | default "TCP" | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.envFrom }}
|
||||
envFrom:
|
||||
{{- range $value := .Values.deployment.envFrom }}
|
||||
{{- if (eq .type "configmap") }}
|
||||
- configMapRef:
|
||||
{{- if .name }}
|
||||
name: {{ include "formbricks.tplvalues.render" ( dict "value" $value.name "context" $ ) }}
|
||||
{{- else if .nameSuffix }}
|
||||
name: {{ template "formbricks.name" $ }}-{{ include "formbricks.tplvalues.render" ( dict "value" $value.nameSuffix "context" $ ) }}
|
||||
{{- else }}
|
||||
name: {{ template "formbricks.name" $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if (eq .type "secret") }}
|
||||
- secretRef:
|
||||
{{- if .name }}
|
||||
name: {{ include "formbricks.tplvalues.render" ( dict "value" $value.name "context" $ ) }}
|
||||
{{- else if .nameSuffix }}
|
||||
name: {{ template "formbricks.name" $ }}-{{ include "formbricks.tplvalues.render" ( dict "value" $value.nameSuffix "context" $ ) }}
|
||||
{{- else }}
|
||||
name: {{ template "formbricks.name" $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- if and (.Values.enterprise.enabled) (ne .Values.enterprise.licenseKey "") }}
|
||||
- name: ENTERPRISE_LICENSE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: ENTERPRISE_LICENSE_KEY
|
||||
{{- else if and (.Values.enterprise.enabled) (eq .Values.enterprise.licenseKey "") }}
|
||||
- name: ENTERPRISE_LICENSE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: ENTERPRISE_LICENSE_KEY
|
||||
{{- end }}
|
||||
- name: REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: REDIS_URL
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: DATABASE_URL
|
||||
- name: CRON_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: CRON_SECRET
|
||||
- name: ENCRYPTION_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: ENCRYPTION_KEY
|
||||
- name: NEXTAUTH_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
key: NEXTAUTH_SECRET
|
||||
{{- range $key, $value := .Values.deployment.env }}
|
||||
- name: {{ include "formbricks.tplvalues.render" ( dict "value" $key "context" $ ) }}
|
||||
{{ include "formbricks.tplvalues.render" ( dict "value" $value "context" $ ) | indent 10 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.resources }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- toYaml .Values.deployment.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.deployment.probes }}
|
||||
{{- if .livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml .livenessProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml .readinessProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .startupProbe }}
|
||||
startupProbe:
|
||||
{{- toYaml .startupProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
52
helm-chart/templates/externalsecrets.yaml
Normal file
52
helm-chart/templates/externalsecrets.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
{{- if (.Values.externalSecret).enabled }}
|
||||
{{- range $nameSuffix, $data := .Values.externalSecret.files }}
|
||||
---
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: {{ template "formbricks.name" $ }}-{{ $nameSuffix }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" $ | nindent 4 }}
|
||||
spec:
|
||||
refreshInterval: {{ $.Values.externalSecret.refreshInterval }}
|
||||
{{- if and (not $data.data) (not $data.dataFrom) }}
|
||||
{{- fail "Data or datafrom not specified for secret {{ template 'formbricks.name' $ }}-{{ $nameSuffix }} " }}
|
||||
{{- end }}
|
||||
{{- if $data.data }}
|
||||
data:
|
||||
{{- range $secretKey, $remoteRef := $data.data}}
|
||||
- secretKey: {{ $secretKey }}
|
||||
{{ toYaml $remoteRef | indent 6}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $data.dataFrom }}
|
||||
dataFrom:
|
||||
- extract:
|
||||
{{ toYaml $data.dataFrom | indent 6 }}
|
||||
{{- end }}
|
||||
{{- if $data.secretStore }}
|
||||
secretStoreRef:
|
||||
name: {{ $data.secretStore.name }}
|
||||
kind: {{ $data.secretStore.kind | default "SecretStore" }}
|
||||
{{- else }}
|
||||
secretStoreRef:
|
||||
name: {{ $.Values.externalSecret.secretStore.name }}
|
||||
kind: {{ $.Values.externalSecret.secretStore.kind | default "SecretStore" }}
|
||||
{{- end}}
|
||||
target:
|
||||
name: {{ template "formbricks.name" $ }}-{{ $nameSuffix }}
|
||||
template:
|
||||
type: {{ $data.type | default "Opaque" }}
|
||||
{{- if or $data.annotations $data.labels}}
|
||||
metadata:
|
||||
{{- if $data.annotations }}
|
||||
annotations:
|
||||
{{ toYaml $data.annotations | indent 10 }}
|
||||
{{- end }}
|
||||
{{- if $data.labels }}
|
||||
labels:
|
||||
{{ toYaml $data.labels | indent 10 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,39 +1,33 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
---
|
||||
{{- if .Capabilities.APIVersions.Has "autoscaling/v2/HorizontalPodAutoscaler" }}
|
||||
apiVersion: autoscaling/v2
|
||||
{{- else }}
|
||||
apiVersion: autoscaling/v2beta2
|
||||
{{- end }}
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "formbricks.fullname" . }}
|
||||
name: {{ template "formbricks.name" . }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" . | nindent 4 }}
|
||||
{{- with .Values.autoscaling.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.annotations }}
|
||||
annotations:
|
||||
{{- toYaml .Values.autoscaling.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "formbricks.fullname" . }}
|
||||
name: {{ template "formbricks.name" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- toYaml .Values.autoscaling.metrics | nindent 4 }}
|
||||
{{- if .Values.autoscaling.behavior }}
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 100
|
||||
periodSeconds: 15
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 0
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 100
|
||||
periodSeconds: 15
|
||||
- type: Pods
|
||||
value: 4
|
||||
periodSeconds: 15
|
||||
selectPolicy: Max
|
||||
{{- end }}
|
||||
{{- toYaml .Values.autoscaling.behavior | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,23 +1,45 @@
|
||||
{{- if .Values.traefik.enabled -}}
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
{{- if (.Values.ingress).enabled -}}
|
||||
{{- $applicationNameTpl := include "formbricks.name" . -}}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "formbricks.fullname" . }}
|
||||
name: {{ template "formbricks.name" . }}
|
||||
namespace: {{ include "formbricks.namespace" . }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" . | nindent 4 }}
|
||||
{{- with .Values.traefik.ingressRoute.annotations }}
|
||||
{{- include "formbricks.labels" $ | nindent 4 }}
|
||||
{{- if .Values.ingress.additionalLabels }}
|
||||
{{ toYaml .Values.ingress.additionalLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{ toYaml .Values.ingress.annotations | indent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`{{ .Values.hostname }}`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: {{ include "formbricks.fullname" . }}
|
||||
port: {{ .Values.service.port }}
|
||||
{{- if .Values.ingress.ingressClassName }}
|
||||
ingressClassName: {{ .Values.ingress.ingressClassName }}
|
||||
{{- end}}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ tpl .host $ }}
|
||||
http:
|
||||
paths:
|
||||
{{- if .paths }}
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
pathType: {{ default "ImplementationSpecific" (.pathType) }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ default $applicationNameTpl (.serviceName) }}
|
||||
port:
|
||||
name: {{ default "http" (.servicePort) }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{ fail "Specify paths for ingress host, check values.yaml" }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
{{- end }}
|
||||
{{ include "formbricks.tplvalues.render" (dict "value" .Values.ingress.tls "context" $) | indent 3 }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -1,19 +1,37 @@
|
||||
{{- if and (.Values.secret) (.Values.secret.enabled) }}
|
||||
|
||||
{{- $postgresAdminPassword := include "formbricks.postgresAdminPassword" . }}
|
||||
{{- $postgresUserPassword := include "formbricks.postgresUserPassword" . }}
|
||||
{{- $redisPassword := include "formbricks.redisPassword" . }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "formbricks.fullname" . }}-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{- if .Values.postgresql.externalUrl }}
|
||||
DATABASE_URL: {{ .Values.postgresql.externalUrl }}
|
||||
{{- else if .Values.postgresql.enabled }}
|
||||
DATABASE_URL: postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ .Release.Name }}-postgresql:5432/{{ .Values.postgresql.auth.database }}
|
||||
name: {{ template "formbricks.name" . }}-app-secrets
|
||||
labels:
|
||||
{{- include "formbricks.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- if .Values.redis.enabled }}
|
||||
REDIS_URL: {{ printf "redis://:%s@formbricks-redis-master:6379" $redisPassword | b64enc }}
|
||||
{{- else }}
|
||||
REDIS_URL: {{ .Values.redis.externalRedisUrl | b64enc }}
|
||||
{{- end }}
|
||||
{{- if .Values.redis.externalUrl }}
|
||||
REDIS_URL: {{ .Values.redis.externalUrl }}
|
||||
{{- else if .Values.redis.enabled }}
|
||||
REDIS_URL: redis://:{{ .Values.redis.auth.password }}@{{ .Release.Name }}-redis-master:6379
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
DATABASE_URL: {{ printf "postgresql://formbricks:%s@formbricks-postgresql/formbricks" $postgresUserPassword | b64enc }}
|
||||
{{- else }}
|
||||
DATABASE_URL: {{ .Values.postgresql.externalDatabaseUrl | b64enc }}
|
||||
{{- end }}
|
||||
NEXTAUTH_SECRET: {{ .Values.formbricksConfig.nextAuthSecret | default (randAlphaNum 32) | quote }}
|
||||
ENCRYPTION_KEY: {{ .Values.formbricksConfig.encryptionKey | default (randAlphaNum 32) | quote }}
|
||||
CRON_SECRET: {{ .Values.formbricksConfig.cronSecret | default (randAlphaNum 32) | quote }}
|
||||
CRON_SECRET: {{ include "formbricks.cronSecret" . | b64enc }}
|
||||
ENCRYPTION_KEY: {{ include "formbricks.encryptionKey" . | b64enc }}
|
||||
NEXTAUTH_SECRET: {{ include "formbricks.nextAuthSecret" . | b64enc }}
|
||||
{{- if and (.Values.enterprise.licenseKey) (ne .Values.enterprise.licenseKey "") }}
|
||||
ENTERPRISE_LICENSE_KEY: {{ .Values.enterprise.licenseKey | b64enc }}
|
||||
{{- end }}
|
||||
{{- if .Values.redis.enabled }}
|
||||
REDIS_PASSWORD: {{ $redisPassword | b64enc }}
|
||||
{{- end }}
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
POSTGRES_ADMIN_PASSWORD: {{ $postgresAdminPassword | b64enc }}
|
||||
POSTGRES_USER_PASSWORD: {{ $postgresUserPassword | b64enc }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user