mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-14 11:30:11 -05:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eeda5ac361 | |||
| c8a1f6f0de | |||
| 4fc81cb2a2 | |||
| e1f958c905 | |||
| 4b9a819abf | |||
| 709917eb8f | |||
| 3ba70122d5 | |||
| 5ff025543e | |||
| 896d5bad12 |
@@ -29,7 +29,6 @@ export const GET = async (req: NextRequest) => {
|
||||
<h2 tw="flex flex-col text-[8] sm:text-4xl font-bold tracking-tight text-slate-900 text-left mt-15">
|
||||
{name}
|
||||
</h2>
|
||||
<span tw="text-slate-600 text-xl">Complete in ~ 4 minutes</span>
|
||||
</div>
|
||||
</div>
|
||||
<div tw="flex justify-end mr-10 ">
|
||||
|
||||
@@ -32,7 +32,7 @@ export const getSurveyMetadata = reactCache(async (surveyId: string) =>
|
||||
return survey;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
logger.error(error, "Error getting survey metadata");
|
||||
logger.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
|
||||
@@ -1,416 +0,0 @@
|
||||
#!/bin/env bash
|
||||
|
||||
set -e
|
||||
ubuntu_version=$(lsb_release -a 2>/dev/null | grep -v "No LSB modules are available." | grep "Description:" | awk -F "Description:\t" '{print $2}')
|
||||
|
||||
install_formbricks() {
|
||||
# Friendly welcome
|
||||
echo "🧱 Welcome to the Formbricks Setup Script"
|
||||
echo ""
|
||||
echo "🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your $ubuntu_version server."
|
||||
echo ""
|
||||
|
||||
# Remove any old Docker installations, without stopping the script if they're not found
|
||||
echo "🧹 Time to sweep away any old Docker installations."
|
||||
sudo apt-get remove docker docker-engine docker.io containerd runc >/dev/null 2>&1 || true
|
||||
|
||||
# Update package list
|
||||
echo "🔄 Updating your package list."
|
||||
sudo apt-get update >/dev/null 2>&1
|
||||
|
||||
# Install dependencies
|
||||
echo "📦 Installing the necessary dependencies."
|
||||
sudo apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release >/dev/null 2>&1
|
||||
|
||||
# Set up Docker's official GPG key & stable repository
|
||||
echo "🔑 Adding Docker's official GPG key and setting up the stable repository."
|
||||
sudo mkdir -m 0755 -p /etc/apt/keyrings >/dev/null 2>&1
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg >/dev/null 2>&1
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null 2>&1
|
||||
|
||||
# Update package list again
|
||||
echo "🔄 Updating your package list again."
|
||||
sudo apt-get update >/dev/null 2>&1
|
||||
|
||||
# Install Docker
|
||||
echo "🐳 Installing Docker."
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin >/dev/null 2>&1
|
||||
|
||||
# Test Docker installation
|
||||
echo "🚀 Testing your Docker installation."
|
||||
if docker --version >/dev/null 2>&1; then
|
||||
echo "🎉 Docker is installed!"
|
||||
else
|
||||
echo "❌ Docker is not installed. Please install Docker before proceeding."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Adding your user to the Docker group
|
||||
echo "🐳 Adding your user to the Docker group to avoid using sudo with docker commands."
|
||||
sudo groupadd docker >/dev/null 2>&1 || true
|
||||
sudo usermod -aG docker $USER >/dev/null 2>&1
|
||||
|
||||
echo "🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance!"
|
||||
|
||||
mkdir -p formbricks && cd formbricks
|
||||
echo "📁 Created Formbricks Quickstart directory at ./formbricks."
|
||||
|
||||
# Ask the user for their domain name
|
||||
echo "🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)):"
|
||||
read domain_name
|
||||
|
||||
echo "🔗 Do you want us to set up an HTTPS certificate for you? [Y/n]"
|
||||
read https_setup
|
||||
https_setup=$(echo "$https_setup" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for HTTPS setup
|
||||
if [[ -z $https_setup ]]; then
|
||||
https_setup="y"
|
||||
fi
|
||||
|
||||
if [[ $https_setup == "y" ]]; then
|
||||
echo "🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n]"
|
||||
read dns_setup
|
||||
dns_setup=$(echo "$dns_setup" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for DNS setup
|
||||
if [[ -z $dns_setup ]]; then
|
||||
dns_setup="y"
|
||||
fi
|
||||
|
||||
if [[ $dns_setup == "y" ]]; then
|
||||
echo "💡 Please enter your email address for the SSL certificate:"
|
||||
read email_address
|
||||
|
||||
echo "🔗 Do you want to enforce HTTPS (HSTS)? [Y/n]"
|
||||
read hsts_enabled
|
||||
hsts_enabled=$(echo "$hsts_enabled" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for HSTS
|
||||
if [[ -z $hsts_enabled ]]; then
|
||||
hsts_enabled="y"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "❌ Ports 80 & 443 are not open. We can't help you in providing the SSL certificate."
|
||||
https_setup="n"
|
||||
hsts_enabled="n"
|
||||
fi
|
||||
else
|
||||
https_setup="n"
|
||||
hsts_enabled="n"
|
||||
fi
|
||||
|
||||
# Ask for HSTS configuration for HTTPS redirection if custom certificate is used
|
||||
if [[ $https_setup == "n" ]]; then
|
||||
echo "You have chosen not to set up HTTPS certificate for your domain. Please make sure to set up HTTPS on your own. You can refer to the Formbricks documentation(https://formbricks.com/docs/self-hosting/custom-ssl) for more information."
|
||||
|
||||
echo "🔗 Do you want to enforce HTTPS (HSTS)? [Y/n]"
|
||||
read hsts_enabled
|
||||
hsts_enabled=$(echo "$hsts_enabled" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for HSTS
|
||||
if [[ -z $hsts_enabled ]]; then
|
||||
hsts_enabled="y"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Installing Traefik
|
||||
echo "🚗 Configuring Traefik..."
|
||||
|
||||
if [[ $hsts_enabled == "y" ]]; then
|
||||
hsts_middlewares="middlewares:
|
||||
- hstsHeader"
|
||||
http_redirection="http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true"
|
||||
else
|
||||
hsts_middlewares=""
|
||||
http_redirection=""
|
||||
fi
|
||||
|
||||
if [[ $https_setup == "y" ]]; then
|
||||
certResolver="certResolver: default"
|
||||
certificates_resolvers="certificatesResolvers:
|
||||
default:
|
||||
acme:
|
||||
email: $email_address
|
||||
storage: acme.json
|
||||
caServer: "https://acme-v01.api.letsencrypt.org/directory"
|
||||
tlsChallenge: {}"
|
||||
else
|
||||
certResolver=""
|
||||
certificates_resolvers=""
|
||||
fi
|
||||
|
||||
cat <<EOT >traefik.yaml
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
$http_redirection
|
||||
websecure:
|
||||
address: ":443"
|
||||
http:
|
||||
tls:
|
||||
$certResolver
|
||||
options: default
|
||||
$hsts_middlewares
|
||||
providers:
|
||||
docker:
|
||||
watch: true
|
||||
exposedByDefault: false
|
||||
file:
|
||||
directory: /
|
||||
$certificates_resolvers
|
||||
EOT
|
||||
|
||||
cat <<EOT >traefik-dynamic.yaml
|
||||
# configuring min TLS version
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
cipherSuites:
|
||||
# TLS 1.2 Ciphers
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
|
||||
# TLS 1.3 Ciphers (These are automatically used for TLS 1.3 connections)
|
||||
- TLS_AES_128_GCM_SHA256
|
||||
- TLS_AES_256_GCM_SHA384
|
||||
- TLS_CHACHA20_POLY1305_SHA256
|
||||
|
||||
# Fallback
|
||||
- TLS_FALLBACK_SCSV
|
||||
EOT
|
||||
|
||||
echo "💡 Created traefik.yaml and traefik-dynamic.yaml file."
|
||||
|
||||
if [[ $https_setup == "y" ]]; then
|
||||
touch acme.json
|
||||
chmod 600 acme.json
|
||||
echo "💡 Created acme.json file with correct permissions."
|
||||
fi
|
||||
|
||||
# Prompt for email service setup
|
||||
read -p "📧 Do you want to set up the email service? You will need SMTP credentials for the same! [y/N]" email_service
|
||||
email_service=$(echo "$email_service" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for email service setup
|
||||
if [[ -z $email_service ]]; then
|
||||
email_service="n"
|
||||
fi
|
||||
|
||||
if [[ $email_service == "y" ]]; then
|
||||
echo "Please provide the following email service details: "
|
||||
|
||||
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
|
||||
|
||||
echo -n "Enter your SMTP Host Port: "
|
||||
read smtp_port
|
||||
|
||||
echo -n "Enter your SMTP username: "
|
||||
read smtp_user
|
||||
|
||||
echo -n "Enter your SMTP password: "
|
||||
read smtp_password
|
||||
|
||||
echo -n "Enable Authenticated SMTP? Enter 1 for yes and 0 for no(default is 1): "
|
||||
read smtp_authenticated
|
||||
|
||||
echo -n "Enable Secure SMTP (use SSL)? Enter 1 for yes and 0 for no: "
|
||||
read smtp_secure_enabled
|
||||
|
||||
else
|
||||
mail_from=""
|
||||
mail_from_name=""
|
||||
smtp_host=""
|
||||
smtp_port=""
|
||||
smtp_user=""
|
||||
smtp_password=""
|
||||
smtp_authenticated=1
|
||||
smtp_secure_enabled=0
|
||||
fi
|
||||
|
||||
echo "📥 Downloading docker-compose.yml from Formbricks GitHub repository..."
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/formbricks/formbricks/main/docker/docker-compose.yml
|
||||
|
||||
echo "🚙 Updating docker-compose.yml with your custom inputs..."
|
||||
sed -i "/WEBAPP_URL:/s|WEBAPP_URL:.*|WEBAPP_URL: \"https://$domain_name\"|" docker-compose.yml
|
||||
sed -i "/NEXTAUTH_URL:/s|NEXTAUTH_URL:.*|NEXTAUTH_URL: \"https://$domain_name\"|" docker-compose.yml
|
||||
|
||||
nextauth_secret=$(openssl rand -hex 32) && sed -i "/NEXTAUTH_SECRET:$/s/NEXTAUTH_SECRET:.*/NEXTAUTH_SECRET: $nextauth_secret/" docker-compose.yml
|
||||
echo "🚗 NEXTAUTH_SECRET updated successfully!"
|
||||
|
||||
encryption_key=$(openssl rand -hex 32) && sed -i "/ENCRYPTION_KEY:$/s/ENCRYPTION_KEY:.*/ENCRYPTION_KEY: $encryption_key/" docker-compose.yml
|
||||
echo "🚗 ENCRYPTION_KEY updated successfully!"
|
||||
|
||||
cron_secret=$(openssl rand -hex 32) && sed -i "/CRON_SECRET:$/s/CRON_SECRET:.*/CRON_SECRET: $cron_secret/" docker-compose.yml
|
||||
echo "🚗 CRON_SECRET updated successfully!"
|
||||
|
||||
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
|
||||
sed -i "s|# SMTP_USER:|SMTP_USER: \"$smtp_user\"|" docker-compose.yml
|
||||
sed -i "s|# SMTP_PASSWORD:|SMTP_PASSWORD: \"$smtp_password\"|" docker-compose.yml
|
||||
sed -i "s|# SMTP_AUTHENTICATED:|SMTP_AUTHENTICATED: $smtp_authenticated|" docker-compose.yml
|
||||
fi
|
||||
|
||||
awk -v domain_name="$domain_name" -v hsts_enabled="$hsts_enabled" '
|
||||
/formbricks:/,/^ *$/ {
|
||||
if ($0 ~ /depends_on:/) {
|
||||
inserting_labels=1
|
||||
}
|
||||
if (inserting_labels && ($0 ~ /ports:/)) {
|
||||
print " labels:"
|
||||
print " - \"traefik.enable=true\" # Enable Traefik for this service"
|
||||
print " - \"traefik.http.routers.formbricks.rule=Host(`" domain_name "`)\" # Use your actual domain or IP"
|
||||
print " - \"traefik.http.routers.formbricks.entrypoints=websecure\" # Use the websecure entrypoint (port 443 with TLS)"
|
||||
print " - \"traefik.http.routers.formbricks.tls=true\" # Enable TLS"
|
||||
print " - \"traefik.http.routers.formbricks.tls.certresolver=default\" # Specify the certResolver"
|
||||
print " - \"traefik.http.services.formbricks.loadbalancer.server.port=3000\" # Forward traffic to Formbricks on port 3000"
|
||||
if (hsts_enabled == "y") {
|
||||
print " - \"traefik.http.middlewares.hstsHeader.headers.stsSeconds=31536000\" # Set HSTS (HTTP Strict Transport Security) max-age to 1 year (31536000 seconds)"
|
||||
print " - \"traefik.http.middlewares.hstsHeader.headers.forceSTSHeader=true\" # Ensure the HSTS header is always included in responses"
|
||||
print " - \"traefik.http.middlewares.hstsHeader.headers.stsPreload=true\" # Allow the domain to be preloaded in browser HSTS preload list"
|
||||
print " - \"traefik.http.middlewares.hstsHeader.headers.stsIncludeSubdomains=true\" # Apply HSTS policy to all subdomains as well"
|
||||
} else {
|
||||
print " - \"traefik.http.routers.formbricks_http.entrypoints=web\" # Use the web entrypoint (port 80)"
|
||||
print " - \"traefik.http.routers.formbricks_http.rule=Host(`" domain_name "`)\" # Use your actual domain or IP"
|
||||
}
|
||||
inserting_labels=0
|
||||
}
|
||||
print
|
||||
next
|
||||
}
|
||||
/^volumes:/ {
|
||||
print " traefik:"
|
||||
print " image: \"traefik:v2.7\""
|
||||
print " restart: always"
|
||||
print " container_name: \"traefik\""
|
||||
print " depends_on:"
|
||||
print " - formbricks"
|
||||
print " ports:"
|
||||
print " - \"80:80\""
|
||||
print " - \"443:443\""
|
||||
print " - \"8080:8080\""
|
||||
print " volumes:"
|
||||
print " - ./traefik.yaml:/traefik.yaml"
|
||||
print " - ./traefik-dynamic.yaml:/traefik-dynamic.yaml"
|
||||
print " - ./acme.json:/acme.json"
|
||||
print " - /var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
print ""
|
||||
}
|
||||
1
|
||||
' docker-compose.yml >tmp.yml && mv tmp.yml docker-compose.yml
|
||||
|
||||
newgrp docker <<END
|
||||
|
||||
docker compose up -d
|
||||
|
||||
echo "🔗 To edit more variables and deeper config, go to the formbricks/docker-compose.yml, edit the file, and restart the container!"
|
||||
|
||||
echo "🚨 Make sure you have set up the DNS records as well as inbound rules for the domain name and IP address of this instance."
|
||||
echo ""
|
||||
echo "🎉 All done! Please setup your Formbricks instance by visiting your domain at https://$domain_name. You can check the status of Formbricks & Traefik with 'cd formbricks && sudo docker compose ps.'"
|
||||
|
||||
END
|
||||
|
||||
}
|
||||
|
||||
uninstall_formbricks() {
|
||||
echo "🗑️ Preparing to Uninstalling Formbricks..."
|
||||
read -p "Are you sure you want to uninstall Formbricks? This will delete all the data associated with it! (yes/no): " uninstall_confirmation
|
||||
uninstall_confirmation=$(echo "$uninstall_confirmation" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ $uninstall_confirmation == "yes" ]]; then
|
||||
cd formbricks
|
||||
sudo docker compose down
|
||||
cd ..
|
||||
sudo rm -rf formbricks
|
||||
echo "🛑 Formbricks uninstalled successfully!"
|
||||
else
|
||||
echo "❌ Uninstalling Formbricks has been cancelled."
|
||||
fi
|
||||
}
|
||||
|
||||
stop_formbricks() {
|
||||
echo "🛑 Stopping Formbricks..."
|
||||
cd formbricks
|
||||
sudo docker compose down
|
||||
echo "🎉 Formbricks instance stopped successfully!"
|
||||
}
|
||||
|
||||
update_formbricks() {
|
||||
echo "🔄 Updating Formbricks..."
|
||||
cd formbricks
|
||||
sudo docker compose pull
|
||||
sudo docker compose down
|
||||
sudo docker compose up -d
|
||||
echo "🎉 Formbricks updated successfully!"
|
||||
echo "🎉 Check the status of Formbricks & Traefik with 'cd formbricks && sudo docker compose logs.'"
|
||||
}
|
||||
|
||||
restart_formbricks() {
|
||||
echo "🔄 Restarting Formbricks..."
|
||||
cd formbricks
|
||||
sudo docker compose restart
|
||||
echo "🎉 Formbricks restarted successfully!"
|
||||
}
|
||||
|
||||
get_logs() {
|
||||
echo "📃 Getting Formbricks logs..."
|
||||
cd formbricks
|
||||
sudo docker compose logs
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
install)
|
||||
install_formbricks
|
||||
;;
|
||||
update)
|
||||
update_formbricks
|
||||
;;
|
||||
stop)
|
||||
stop_formbricks
|
||||
;;
|
||||
restart)
|
||||
restart_formbricks
|
||||
;;
|
||||
logs)
|
||||
get_logs
|
||||
;;
|
||||
uninstall)
|
||||
uninstall_formbricks
|
||||
;;
|
||||
*)
|
||||
echo "🚀 Executing default step of installing Formbricks"
|
||||
install_formbricks
|
||||
;;
|
||||
esac
|
||||
@@ -19,17 +19,12 @@ Ensure you have the following before proceeding:
|
||||
## 1. Installation Steps
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone the Helm Chart">
|
||||
```sh
|
||||
git clone https://github.com/formbricks/formbricks
|
||||
cd helm-chart
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install with Default Configuration">
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace
|
||||
```
|
||||
> **Note:** To specify specific version use `--version` flag. E.g., `--version 1.0.0`. Starting from 3.5.0, the chart is available on the GitHub Container Registry (GHCR).
|
||||
|
||||
By default:
|
||||
- PostgreSQL and Redis are deployed within the cluster.
|
||||
- Secrets are dynamically generated and stored as Kubernetes Secrets.
|
||||
@@ -37,7 +32,7 @@ By default:
|
||||
|
||||
<Step title="Install with an Enterprise License">
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace --set enterprise.licenseKey="YOUR_LICENSE_KEY"
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace --set enterprise.licenseKey="YOUR_LICENSE_KEY"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -107,7 +102,7 @@ externalSecret:
|
||||
|
||||
Install with:
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
---
|
||||
@@ -129,7 +124,7 @@ redis:
|
||||
```
|
||||
Install with:
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
---
|
||||
@@ -147,7 +142,7 @@ redis:
|
||||
```
|
||||
Apply with:
|
||||
```sh
|
||||
helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
```
|
||||
|
||||
---
|
||||
@@ -155,17 +150,17 @@ helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
|
||||
## 4. Upgrading the Deployment
|
||||
To apply changes:
|
||||
```sh
|
||||
helm upgrade formbricks ./ -n formbricks
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks
|
||||
```
|
||||
|
||||
### Scaling Resources
|
||||
```sh
|
||||
helm upgrade formbricks ./ -n formbricks --set deployment.resources.limits.cpu=2 --set deployment.resources.limits.memory=4Gi
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --set deployment.resources.limits.cpu=2 --set deployment.resources.limits.memory=4Gi
|
||||
```
|
||||
|
||||
### Enabling Autoscaling
|
||||
```sh
|
||||
helm upgrade formbricks ./ -n formbricks --set autoscaling.enabled=true --set autoscaling.minReplicas=3 --set autoscaling.maxReplicas=10
|
||||
helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --set autoscaling.enabled=true --set autoscaling.minReplicas=3 --set autoscaling.maxReplicas=10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -20,9 +20,7 @@ icon: "eye-slash"
|
||||
|
||||

|
||||
|
||||
## Set Hidden Field in Responses
|
||||
|
||||
### Link Surveys
|
||||
### Set Hidden Field in Link Surveys
|
||||
|
||||
Single Hidden Field:
|
||||
|
||||
@@ -36,20 +34,8 @@ Multiple Hidden Fields:
|
||||
sh https://formbricks.com/clin3dxja02k8l80hpwmx4bjy?screen=landing_page&job=Founder
|
||||
```
|
||||
|
||||
### App & Website Surveys
|
||||
|
||||
For in-product surveys, you can set hidden fields in the response by adding them to the `formbricks.track` call:
|
||||
|
||||
<CodeGroup>
|
||||
```JS action.js
|
||||
formbricks.track("my event", {
|
||||
hiddenFields: {
|
||||
screen: "landing_page",
|
||||
job: "Founder"
|
||||
},
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
### Website & App Surveys
|
||||
We're reworking our approach to setting hidden fields in Website & App Surveys.
|
||||
|
||||
## View Hidden Fields in Responses
|
||||
|
||||
|
||||
@@ -5,5 +5,8 @@ dependencies:
|
||||
- name: redis
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: 20.11.2
|
||||
digest: sha256:6233567e6d133fd87585de7cb11f835125ab649fc7979eac7b17d4b2881f54dc
|
||||
generated: "2025-03-06T15:48:20.190945+05:30"
|
||||
- name: minio
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: 15.0.7
|
||||
digest: sha256:ce42b49e555fb89d365b44de289a2020c6cc8696eaa2aab6f5317b9ee8558ec2
|
||||
generated: "2025-03-27T14:35:28.229585+05:30"
|
||||
|
||||
@@ -26,3 +26,7 @@ dependencies:
|
||||
version: 20.11.2
|
||||
repository: "oci://registry-1.docker.io/bitnamicharts"
|
||||
condition: redis.enabled
|
||||
- name: minio
|
||||
repository: "oci://registry-1.docker.io/bitnamicharts"
|
||||
version: 15.0.7
|
||||
condition: minio.enabled
|
||||
|
||||
@@ -79,7 +79,7 @@ spec:
|
||||
terminationGracePeriodSeconds: {{ .Values.deployment.terminationGracePeriodSeconds | default 30 }}
|
||||
containers:
|
||||
- name: {{ template "formbricks.name" . }}
|
||||
image: "{{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion }}"
|
||||
image: {{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion | default "latest" }}
|
||||
imagePullPolicy: {{ .Values.deployment.image.pullPolicy }}
|
||||
{{- if .Values.deployment.command }}
|
||||
command:
|
||||
@@ -127,6 +127,20 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- if .Values.minio.enabled }}
|
||||
- name: S3_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: formbricks-minio
|
||||
key: root-user
|
||||
- name: S3_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: formbricks-minio
|
||||
key: root-password
|
||||
- name: S3_BUCKET_NAME
|
||||
value: formbricks
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.deployment.env }}
|
||||
- name: {{ include "formbricks.tplvalues.render" ( dict "value" $key "context" $ ) }}
|
||||
{{- if kindIs "string" $value }}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{{- if and (.Values.prometheusRule).enabled (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") -}}
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: {{ template "formbricks.name" . }}
|
||||
namespace: {{ include "formbricks.namespace" . }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" $ | nindent 4 }}
|
||||
{{- if .Values.prometheusRule.additionalLabels }}
|
||||
{{ toYaml .Values.prometheusRule.additionalLabels | indent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
groups:
|
||||
{{ toYaml .Values.prometheusRule.groups | indent 4 }}
|
||||
{{- end -}}
|
||||
@@ -12,7 +12,7 @@ metadata:
|
||||
{{- include "formbricks.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- if .Values.redis.enabled }}
|
||||
REDIS_URL: {{ printf "redis://:%s@formbricks-redis-master:6379" $redisPassword | b64enc }}
|
||||
REDIS_URL: {{ printf "redis://default:%s@formbricks-redis-master:6379" $redisPassword | b64enc }}
|
||||
{{- else }}
|
||||
REDIS_URL: {{ .Values.redis.externalRedisUrl | b64enc }}
|
||||
{{- end }}
|
||||
@@ -28,10 +28,13 @@ data:
|
||||
ENTERPRISE_LICENSE_KEY: {{ .Values.enterprise.licenseKey | b64enc }}
|
||||
{{- end }}
|
||||
{{- if .Values.redis.enabled }}
|
||||
REDIS_PASSWORD: {{ $redisPassword | b64enc }}
|
||||
redis-password: {{ $redisPassword | b64enc }}
|
||||
{{- end }}
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
POSTGRES_ADMIN_PASSWORD: {{ $postgresAdminPassword | b64enc }}
|
||||
POSTGRES_USER_PASSWORD: {{ $postgresUserPassword | b64enc }}
|
||||
{{- end }}
|
||||
{{- if .Values.minio.enabled }}
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{{- if and (.Values.serviceMonitor).enabled (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") }}
|
||||
---
|
||||
apiVersion: "monitoring.coreos.com/v1"
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ template "formbricks.name" . }}-svc-monitor
|
||||
namespace: {{ include "formbricks.namespace" . }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" $ | nindent 4 }}
|
||||
{{- if .Values.serviceMonitor.additionalLabels }}
|
||||
{{ toYaml .Values.serviceMonitor.additionalLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.serviceMonitor.annotations }}
|
||||
annotations:
|
||||
{{- end }}
|
||||
{{- if or .Values.serviceMonitor.annotations }}
|
||||
{{ toYaml .Values.serviceMonitor.annotations | indent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{ include "formbricks.selectorLabels" $ | indent 6 }}
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- {{ include "formbricks.namespace" . }}
|
||||
endpoints:
|
||||
{{ toYaml .Values.serviceMonitor.endpoints | indent 4 }}
|
||||
{{- end }}
|
||||
+43
-2
@@ -62,6 +62,8 @@ deployment:
|
||||
env:
|
||||
DOCKER_CRON_ENABLED:
|
||||
value: "0"
|
||||
PROMETHEUS_ENABLED:
|
||||
value: "1"
|
||||
|
||||
# Tolerations for scheduling pods on tainted nodes
|
||||
tolerations: []
|
||||
@@ -236,7 +238,7 @@ redis:
|
||||
auth:
|
||||
enabled: true
|
||||
existingSecret: "formbricks-app-secrets"
|
||||
existingSecretPasswordKey: "REDIS_PASSWORD"
|
||||
existingSecretPasswordKey: "redis-password"
|
||||
networkPolicy:
|
||||
enabled: false
|
||||
master:
|
||||
@@ -296,4 +298,43 @@ postgresql:
|
||||
containerSecurityContext:
|
||||
enabled: true
|
||||
runAsUser: 1001
|
||||
readOnlyRootFilesystem: false
|
||||
readOnlyRootFilesystem: false
|
||||
|
||||
##########################################################
|
||||
# Prometheus Rule Configuration
|
||||
##########################################################
|
||||
prometheusRule:
|
||||
# -- (bool) Deploy a PrometheusRule (Prometheus Operator) resource.
|
||||
# @section -- PrometheusRule Parameters
|
||||
enabled: false
|
||||
# -- (object) Additional labels for PrometheusRule.
|
||||
# @section -- PrometheusRule Parameters
|
||||
additionalLabels:
|
||||
# prometheus: stakater-workload-monitoring
|
||||
# role: alert-rules
|
||||
# -- (list) Groups with alerting rules.
|
||||
# Read more about it at [https://docs.openshift.com/container-platform/4.7/rest_api/monitoring_apis/prometheusrule-monitoring-coreos-com-v1.html](OpenShift's PrometheusRule documentation).
|
||||
# @section -- PrometheusRule Parameters
|
||||
groups:
|
||||
- name: formbricks
|
||||
rules:
|
||||
- alert: AppDown
|
||||
annotations:
|
||||
message: >-
|
||||
Not able to scrape formbricks, maybe app is Down (or not reachable)
|
||||
expr: up{job="formbricks"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
|
||||
##########################################################
|
||||
# Minio
|
||||
##########################################################
|
||||
minio:
|
||||
enabled: true
|
||||
fullnameOverride: formbricks-minio
|
||||
mode: standalone
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 50Gi
|
||||
defaultBuckets: "formbricks"
|
||||
|
||||
@@ -73,13 +73,17 @@ cronJob:
|
||||
|
||||
## Deployment & Autoscaling
|
||||
deployment:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2Gi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
env:
|
||||
DOCKER_CRON_ENABLED:
|
||||
value: "0"
|
||||
RATE_LIMITING_DISABLED:
|
||||
value: "1"
|
||||
S3_BUCKET_NAME:
|
||||
value: {{ requiredEnv "FORMBRICKS_S3_BUCKET" }}
|
||||
envFrom:
|
||||
app-env:
|
||||
nameSuffix: app-env
|
||||
@@ -89,7 +93,7 @@ deployment:
|
||||
reloadOnChange: true
|
||||
autoscaling:
|
||||
enabled: true
|
||||
maxReplicas: 10
|
||||
maxReplicas: 95
|
||||
minReplicas: 3
|
||||
metrics:
|
||||
- resource:
|
||||
@@ -162,3 +166,5 @@ postgresql:
|
||||
enabled: false
|
||||
redis:
|
||||
enabled: false
|
||||
minio:
|
||||
enabled: false
|
||||
|
||||
Executable
+363
@@ -0,0 +1,363 @@
|
||||
#!/bin/env bash
|
||||
|
||||
set -e
|
||||
ubuntu_version=$(lsb_release -a 2>/dev/null | grep -v "No LSB modules are available." | grep "Description:" | awk -F "Description:\t" '{print $2}')
|
||||
|
||||
install_formbricks() {
|
||||
# Friendly welcome
|
||||
echo "🧱 Welcome to the Formbricks Setup Script"
|
||||
echo ""
|
||||
echo "🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your $ubuntu_version server with microK8s."
|
||||
echo ""
|
||||
|
||||
# Update package list
|
||||
echo "🔄 Updating your package list."
|
||||
sudo apt-get update >/dev/null 2>&1
|
||||
|
||||
# Install dependencies
|
||||
echo "📦 Installing the necessary dependencies."
|
||||
sudo apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release \
|
||||
snapd >/dev/null 2>&1
|
||||
|
||||
# Install microK8s
|
||||
echo "☸️ Installing microK8s Kubernetes..."
|
||||
sudo snap install microk8s --classic >/dev/null 2>&1
|
||||
|
||||
# Add user to microk8s group
|
||||
echo "👥 Adding your user to the microk8s group..."
|
||||
sudo usermod -a -G microk8s $USER >/dev/null 2>&1
|
||||
sudo mkdir -p ~/.kube >/dev/null 2>&1
|
||||
sudo chown -R $USER ~/.kube >/dev/null 2>&1
|
||||
|
||||
# Create alias for kubectl
|
||||
echo "🔧 Creating kubectl alias..."
|
||||
sudo snap alias microk8s.kubectl kubectl >/dev/null 2>&1
|
||||
|
||||
# Wait for microk8s to be ready
|
||||
echo "⏳ Waiting for microK8s to be ready..."
|
||||
sudo microk8s status --wait-ready >/dev/null 2>&1
|
||||
|
||||
# Setting up microK8s configuration
|
||||
mkdir -p ~/.kube
|
||||
sudo microk8s config > ~/.kube/config
|
||||
sudo chown -R $USER ~/.kube
|
||||
|
||||
# Enable required add-ons
|
||||
echo "🔌 Enabling required microK8s add-ons (DNS, storage, ingress, helm3, cert-manager)..."
|
||||
sudo microk8s enable dns storage ingress helm3 cert-manager >/dev/null 2>&1
|
||||
|
||||
echo "⏳ Waiting for add-ons to be ready..."
|
||||
sleep 10
|
||||
|
||||
# Create formbricks directory
|
||||
mkdir -p formbricks && cd formbricks
|
||||
echo "📁 Created Formbricks directory at ./formbricks."
|
||||
|
||||
# Prompt for enabling Minio
|
||||
echo "🪣 Do you want to enable Minio? [Y/n] (default is Y):"
|
||||
read enable_minio
|
||||
enable_minio=$(echo "$enable_minio" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ -z $enable_minio ]]; then
|
||||
enable_minio="y"
|
||||
fi
|
||||
|
||||
# Ask the user for their domain name
|
||||
echo "🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)):"
|
||||
read domain_name
|
||||
|
||||
echo "🔗 Please enter your Minio Console domain (Press Enter to use default: minio.${domain_name}):"
|
||||
read minio_domain
|
||||
minio_domain=${minio_domain:-minio.${domain_name}}
|
||||
|
||||
echo "🔗 Please enter your Minio API domain (Press Enter to use default: minio-api.${domain_name}):"
|
||||
read minio_api_domain
|
||||
minio_api_domain=${minio_api_domain:-minio-api.${domain_name}}
|
||||
|
||||
echo "🔗 Do you want us to set up an HTTPS certificate for you? [Y/n]"
|
||||
read https_setup
|
||||
https_setup=$(echo "$https_setup" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for HTTPS setup
|
||||
if [[ -z $https_setup ]]; then
|
||||
https_setup="y"
|
||||
fi
|
||||
|
||||
if [[ $https_setup == "y" ]]; then
|
||||
echo "🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n]"
|
||||
read dns_setup
|
||||
dns_setup=$(echo "$dns_setup" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for DNS setup
|
||||
if [[ -z $dns_setup ]]; then
|
||||
dns_setup="y"
|
||||
fi
|
||||
|
||||
if [[ $dns_setup == "y" ]]; then
|
||||
echo "💡 Please enter your email address for the SSL certificate:"
|
||||
read email_address
|
||||
|
||||
# Create ClusterIssuer for Let's Encrypt
|
||||
echo "🔒 Creating Let's Encrypt certificate issuer..."
|
||||
cat <<EOT > cluster-issuer.yaml
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-prod
|
||||
spec:
|
||||
acme:
|
||||
email: ${email_address}
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-prod-account-key
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
ingressClassName: public
|
||||
EOT
|
||||
|
||||
kubectl apply -f cluster-issuer.yaml
|
||||
else
|
||||
echo "❌ Ports 80 & 443 are not open. We can't help you in providing the SSL certificate."
|
||||
https_setup="n"
|
||||
fi
|
||||
else
|
||||
https_setup="n"
|
||||
fi
|
||||
|
||||
# Prompt for email service setup
|
||||
read -p "📧 Do you want to set up the email service? You will need SMTP credentials for the same! [y/N]" email_service
|
||||
email_service=$(echo "$email_service" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Set default value for email service setup
|
||||
if [[ -z $email_service ]]; then
|
||||
email_service="n"
|
||||
fi
|
||||
|
||||
# Create values file for Helm chart
|
||||
echo "📝 Creating values file for Helm chart..."
|
||||
cat <<EOT > values.yaml
|
||||
# Formbricks helm chart values
|
||||
deployment:
|
||||
env:
|
||||
NEXTAUTH_URL:
|
||||
value: "https://${domain_name}"
|
||||
WEBAPP_URL:
|
||||
value: "https://${domain_name}"
|
||||
DOCKER_CRON_ENABLED:
|
||||
value: "0"
|
||||
EOT
|
||||
|
||||
# Add email configuration if selected
|
||||
if [[ $email_service == "y" ]]; then
|
||||
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
|
||||
|
||||
echo -n "Enter your SMTP Host Port: "
|
||||
read smtp_port
|
||||
|
||||
echo -n "Enter your SMTP username: "
|
||||
read smtp_user
|
||||
|
||||
echo -n "Enter your SMTP password: "
|
||||
read smtp_password
|
||||
|
||||
echo -n "Enable Authenticated SMTP? Enter 1 for yes and 0 for no(default is 1): "
|
||||
read smtp_authenticated
|
||||
|
||||
echo -n "Enable Secure SMTP (use SSL)? Enter 1 for yes and 0 for no: "
|
||||
read smtp_secure_enabled
|
||||
|
||||
# Add SMTP configuration to values.yaml
|
||||
cat <<EOT >> values.yaml
|
||||
MAIL_FROM:
|
||||
value: "${mail_from}"
|
||||
MAIL_FROM_NAME:
|
||||
value: "${mail_from_name}"
|
||||
SMTP_HOST:
|
||||
value: "${smtp_host}"
|
||||
SMTP_PORT:
|
||||
value: "${smtp_port}"
|
||||
SMTP_USER:
|
||||
value: "${smtp_user}"
|
||||
SMTP_PASSWORD:
|
||||
value: "${smtp_password}"
|
||||
SMTP_AUTHENTICATED:
|
||||
value: ${smtp_authenticated:-1}
|
||||
SMTP_SECURE_ENABLED:
|
||||
value: ${smtp_secure_enabled:-0}
|
||||
EOT
|
||||
else
|
||||
cat <<EOT >> values.yaml
|
||||
EMAIL_VERIFICATION_DISABLED:
|
||||
value: "1"
|
||||
PASSWORD_RESET_DISABLED:
|
||||
value: "1"
|
||||
EOT
|
||||
fi
|
||||
|
||||
|
||||
if [[ $enable_minio == "y" ]]; then
|
||||
cat <<EOT >> values.yaml
|
||||
S3_ENDPOINT_URL:
|
||||
value: "https://${minio_api_domain}"
|
||||
EOT
|
||||
fi
|
||||
|
||||
# Configure ingress with SSL
|
||||
if [[ $https_setup == "y" ]]; then
|
||||
cat <<EOT >> values.yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: public
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: ${domain_name}
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: formbricks-tls
|
||||
hosts:
|
||||
- ${domain_name}
|
||||
EOT
|
||||
else
|
||||
cat <<EOT >> values.yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: public
|
||||
hosts:
|
||||
- host: ${domain_name}
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
EOT
|
||||
fi
|
||||
# Configure ingress for Minio
|
||||
|
||||
if [[ $enable_minio == "y" ]]; then
|
||||
cat <<EOT >> values.yaml
|
||||
minio:
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: public
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
kubernetes.io/tls-acme: "true"
|
||||
hostname: ${minio_domain}
|
||||
tls: true
|
||||
apiIngress:
|
||||
enabled: true
|
||||
ingressClassName: public
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
kubernetes.io/tls-acme: "true"
|
||||
hostname: ${minio_api_domain}
|
||||
extraHosts:
|
||||
- name: formbricks.${minio_api_domain}
|
||||
path: /
|
||||
tls: true
|
||||
EOT
|
||||
fi
|
||||
|
||||
# Create a namespace for Formbricks
|
||||
echo "🚀 Ensuring namespace 'formbricks' exists..."
|
||||
if ! kubectl get namespace formbricks >/dev/null 2>&1; then
|
||||
kubectl create namespace formbricks
|
||||
echo "✅ Namespace 'formbricks' created."
|
||||
else
|
||||
echo "ℹ️ Namespace 'formbricks' already exists."
|
||||
fi
|
||||
|
||||
# Add helm repo and update
|
||||
# echo "⚓ Adding Formbricks Helm repository..."
|
||||
# microk8s helm3 repo add formbricks-repo oci://ghcr.io/formbricks/helm-charts
|
||||
# microk8s helm3 repo update
|
||||
|
||||
# Install Formbricks with Helm
|
||||
echo "🚀 Installing Formbricks via Helm chart..."
|
||||
microk8s helm3 upgrade -i formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
|
||||
|
||||
echo "⏳ Waiting for Formbricks to be ready..."
|
||||
kubectl -n formbricks rollout status deployment formbricks
|
||||
|
||||
echo "🚨 Make sure you have set up the DNS records for ${domain_name} pointing to this server's IP address."
|
||||
echo ""
|
||||
echo "🎉 All done! Please setup your Formbricks instance by visiting your domain at https://${domain_name}."
|
||||
echo "You can check the status of your deployment with 'kubectl get all -n formbricks'"
|
||||
}
|
||||
|
||||
uninstall_formbricks() {
|
||||
echo "🗑️ Preparing to Uninstall Formbricks..."
|
||||
read -p "Are you sure you want to uninstall Formbricks? This will delete all the data associated with it! (yes/no): " uninstall_confirmation
|
||||
uninstall_confirmation=$(echo "$uninstall_confirmation" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ $uninstall_confirmation == "yes" ]]; then
|
||||
cd formbricks
|
||||
microk8s helm3 uninstall formbricks -n formbricks
|
||||
kubectl delete namespace formbricks
|
||||
echo "🛑 Formbricks uninstalled successfully!"
|
||||
else
|
||||
echo "❌ Uninstalling Formbricks has been cancelled."
|
||||
fi
|
||||
}
|
||||
|
||||
stop_formbricks() {
|
||||
echo "🛑 Stopping Formbricks..."
|
||||
kubectl scale deployment formbricks --replicas=0 -n formbricks
|
||||
echo "🎉 Formbricks instance scaled down to zero successfully!"
|
||||
}
|
||||
|
||||
update_formbricks() {
|
||||
echo "🔄 Updating Formbricks..."
|
||||
cd formbricks
|
||||
microk8s helm3 upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks -f values.yaml
|
||||
echo "🎉 Formbricks updated successfully!"
|
||||
echo "🎉 Check the status of Formbricks with 'kubectl get pods -n formbricks'"
|
||||
}
|
||||
|
||||
restart_formbricks() {
|
||||
echo "🔄 Restarting Formbricks..."
|
||||
kubectl rollout restart deployment formbricks -n formbricks
|
||||
echo "🎉 Formbricks restarted successfully!"
|
||||
}
|
||||
|
||||
get_logs() {
|
||||
echo "📃 Getting Formbricks logs..."
|
||||
kubectl logs -l app.kubernetes.io/name=formbricks -n formbricks --tail=100
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
install)
|
||||
install_formbricks
|
||||
;;
|
||||
update)
|
||||
update_formbricks
|
||||
;;
|
||||
stop)
|
||||
stop_formbricks
|
||||
;;
|
||||
restart)
|
||||
restart_formbricks
|
||||
;;
|
||||
logs)
|
||||
get_logs
|
||||
;;
|
||||
uninstall)
|
||||
uninstall_formbricks
|
||||
;;
|
||||
*)
|
||||
echo "🚀 Executing default step of installing Formbricks"
|
||||
install_formbricks
|
||||
;;
|
||||
esac
|
||||
@@ -27,50 +27,63 @@ module "cloudwatch_cis-alarms" {
|
||||
}
|
||||
|
||||
locals {
|
||||
alb_id = "app/k8s-formbricks-21ab9ecd60/342ed65d128ce4cb"
|
||||
alarms = {
|
||||
ALB_HTTPCode_Target_5XX_Count = {
|
||||
alarm_description = "Average API 5XX target group error code count is too high"
|
||||
comparison_operator = "GreaterThanThreshold"
|
||||
evaluation_periods = 5
|
||||
threshold = 1
|
||||
period = 60
|
||||
threshold = 5
|
||||
period = 600
|
||||
unit = "Count"
|
||||
namespace = "AWS/ApplicationELB"
|
||||
metric_name = "HTTPCode_Target_5XX_Count"
|
||||
statistic = "Sum"
|
||||
dimensions = {
|
||||
LoadBalancer = local.alb_id
|
||||
}
|
||||
}
|
||||
ALB_HTTPCode_ELB_5XX_Count = {
|
||||
alarm_description = "Average API 5XX load balancer error code count is too high"
|
||||
comparison_operator = "GreaterThanThreshold"
|
||||
evaluation_periods = 5
|
||||
threshold = 1
|
||||
period = 60
|
||||
threshold = 5
|
||||
period = 600
|
||||
unit = "Count"
|
||||
namespace = "AWS/ApplicationELB"
|
||||
metric_name = "HTTPCode_ELB_5XX_Count"
|
||||
statistic = "Sum"
|
||||
dimensions = {
|
||||
LoadBalancer = local.alb_id
|
||||
}
|
||||
}
|
||||
ALB_TargetResponseTime = {
|
||||
alarm_description = format("Average API response time is greater than %s", 0.05)
|
||||
alarm_description = format("Average API response time is greater than %s", 5)
|
||||
comparison_operator = "GreaterThanThreshold"
|
||||
evaluation_periods = 5
|
||||
threshold = 0.05
|
||||
threshold = 5
|
||||
period = 60
|
||||
unit = "Seconds"
|
||||
namespace = "AWS/ApplicationELB"
|
||||
metric_name = "TargetResponseTime"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
LoadBalancer = local.alb_id
|
||||
}
|
||||
}
|
||||
ALB_UnHealthyHostCount = {
|
||||
alarm_description = format("Unhealthy host count is greater than %s", 1)
|
||||
alarm_description = format("Unhealthy host count is greater than %s", 2)
|
||||
comparison_operator = "GreaterThanThreshold"
|
||||
evaluation_periods = 5
|
||||
threshold = 1
|
||||
threshold = 2
|
||||
period = 60
|
||||
unit = "Count"
|
||||
namespace = "AWS/ApplicationELB"
|
||||
metric_name = "UnHealthyHostCount"
|
||||
statistic = "Minimum"
|
||||
dimensions = {
|
||||
LoadBalancer = local.alb_id
|
||||
}
|
||||
}
|
||||
RDS_CPUUtilization = {
|
||||
alarm_description = format("Average RDS CPU utilization is greater than %s", 80)
|
||||
@@ -82,6 +95,9 @@ locals {
|
||||
namespace = "AWS/RDS"
|
||||
metric_name = "CPUUtilization"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
|
||||
}
|
||||
}
|
||||
RDS_FreeStorageSpace = {
|
||||
alarm_description = format("Average RDS free storage space is less than %s", 5)
|
||||
@@ -93,6 +109,9 @@ locals {
|
||||
namespace = "AWS/RDS"
|
||||
metric_name = "FreeStorageSpace"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
|
||||
}
|
||||
}
|
||||
RDS_FreeableMemory = {
|
||||
alarm_description = format("Average RDS freeable memory is less than %s", 100)
|
||||
@@ -104,6 +123,9 @@ locals {
|
||||
namespace = "AWS/RDS"
|
||||
metric_name = "FreeableMemory"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
|
||||
}
|
||||
}
|
||||
RDS_DiskQueueDepth = {
|
||||
alarm_description = format("Average RDS disk queue depth is greater than %s", 1)
|
||||
@@ -115,6 +137,9 @@ locals {
|
||||
namespace = "AWS/RDS"
|
||||
metric_name = "DiskQueueDepth"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
|
||||
}
|
||||
}
|
||||
RDS_ReadIOPS = {
|
||||
alarm_description = format("Average RDS read IOPS is greater than %s", 1000)
|
||||
@@ -126,6 +151,9 @@ locals {
|
||||
namespace = "AWS/RDS"
|
||||
metric_name = "ReadIOPS"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
|
||||
}
|
||||
}
|
||||
RDS_WriteIOPS = {
|
||||
alarm_description = format("Average RDS write IOPS is greater than %s", 1000)
|
||||
@@ -137,6 +165,9 @@ locals {
|
||||
namespace = "AWS/RDS"
|
||||
metric_name = "WriteIOPS"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
|
||||
}
|
||||
}
|
||||
SQS_ApproximateAgeOfOldestMessage = {
|
||||
alarm_description = format("Average SQS approximate age of oldest message is greater than %s", 300)
|
||||
@@ -148,6 +179,9 @@ locals {
|
||||
namespace = "AWS/SQS"
|
||||
metric_name = "ApproximateAgeOfOldestMessage"
|
||||
statistic = "Maximum"
|
||||
dimensions = {
|
||||
QueueName = module.karpenter.queue_name
|
||||
}
|
||||
}
|
||||
DynamoDB_ConsumedReadCapacityUnits = {
|
||||
alarm_description = format("Average DynamoDB consumed read capacity units is greater than %s", 90)
|
||||
@@ -159,6 +193,23 @@ locals {
|
||||
namespace = "AWS/DynamoDB"
|
||||
metric_name = "ConsumedReadCapacityUnits"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
TableName = "terraform-lock"
|
||||
}
|
||||
}
|
||||
DynamoDB_ConsumedWriteCapacityUnits = {
|
||||
alarm_description = format("Average DynamoDB consumed write capacity units is greater than %s", 90)
|
||||
comparison_operator = "GreaterThanThreshold"
|
||||
evaluation_periods = 5
|
||||
threshold = 90
|
||||
period = 60
|
||||
unit = "Count"
|
||||
namespace = "AWS/DynamoDB"
|
||||
metric_name = "ConsumedWriteCapacityUnits"
|
||||
statistic = "Average"
|
||||
dimensions = {
|
||||
TableName = "terraform-lock"
|
||||
}
|
||||
}
|
||||
Lambda_Errors = {
|
||||
alarm_description = format("Average Lambda errors is greater than %s", 1)
|
||||
@@ -170,6 +221,9 @@ locals {
|
||||
namespace = "AWS/Lambda"
|
||||
metric_name = "Errors"
|
||||
statistic = "Sum"
|
||||
dimensions = {
|
||||
FunctionName = module.notify-slack.notify_slack_lambda_function_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,18 +232,21 @@ module "metric_alarm" {
|
||||
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
|
||||
version = "5.7.1"
|
||||
|
||||
for_each = local.alarms
|
||||
alarm_name = each.key
|
||||
alarm_description = each.value.alarm_description
|
||||
comparison_operator = each.value.comparison_operator
|
||||
evaluation_periods = each.value.evaluation_periods
|
||||
threshold = each.value.threshold
|
||||
period = each.value.period
|
||||
unit = each.value.unit
|
||||
for_each = local.alarms
|
||||
alarm_name = each.key
|
||||
alarm_description = each.value.alarm_description
|
||||
comparison_operator = each.value.comparison_operator
|
||||
evaluation_periods = each.value.evaluation_periods
|
||||
threshold = each.value.threshold
|
||||
period = each.value.period
|
||||
unit = each.value.unit
|
||||
insufficient_data_actions = []
|
||||
|
||||
namespace = each.value.namespace
|
||||
metric_name = each.value.metric_name
|
||||
statistic = each.value.statistic
|
||||
|
||||
dimensions = each.value.dimensions
|
||||
|
||||
alarm_actions = [module.notify-slack.slack_topic_arn]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
################################################################################
|
||||
# ElastiCache Module
|
||||
################################################################################
|
||||
locals {
|
||||
valkey_major_version = 8
|
||||
}
|
||||
|
||||
resource "random_password" "valkey" {
|
||||
length = 20
|
||||
special = false
|
||||
@@ -46,21 +50,67 @@ module "elasticache_user_group" {
|
||||
})
|
||||
}
|
||||
|
||||
module "valkey" {
|
||||
source = "terraform-aws-modules/elasticache/aws"
|
||||
version = "1.4.1"
|
||||
|
||||
replication_group_id = "${local.name}-valkey"
|
||||
|
||||
engine = "valkey"
|
||||
engine_version = "8.0"
|
||||
node_type = "cache.m7g.large"
|
||||
|
||||
transit_encryption_enabled = true
|
||||
auth_token = random_password.valkey.result
|
||||
maintenance_window = "sun:05:00-sun:09:00"
|
||||
apply_immediately = true
|
||||
|
||||
# Security Group
|
||||
vpc_id = module.vpc.vpc_id
|
||||
security_group_rules = {
|
||||
ingress_vpc = {
|
||||
# Default type is `ingress`
|
||||
# Default port is based on the default engine port
|
||||
description = "VPC traffic"
|
||||
cidr_ipv4 = module.vpc.vpc_cidr_block
|
||||
}
|
||||
}
|
||||
|
||||
log_delivery_configuration = {
|
||||
slow-log = {
|
||||
destination_type = "cloudwatch-logs"
|
||||
log_format = "json"
|
||||
cloudwatch_log_group_retention_in_days = 365
|
||||
}
|
||||
}
|
||||
|
||||
# Subnet Group
|
||||
subnet_group_name = "${local.name}-valkey"
|
||||
subnet_group_description = "${title(local.name)} subnet group"
|
||||
subnet_ids = module.vpc.database_subnets
|
||||
|
||||
# Parameter Group
|
||||
create_parameter_group = true
|
||||
parameter_group_name = "${local.name}-valkey-${local.valkey_major_version}"
|
||||
parameter_group_family = "valkey8"
|
||||
parameter_group_description = "${title(local.name)} parameter group"
|
||||
parameters = [
|
||||
{
|
||||
name = "latency-tracking"
|
||||
value = "yes"
|
||||
}
|
||||
]
|
||||
|
||||
tags = local.tags
|
||||
}
|
||||
|
||||
module "valkey_serverless" {
|
||||
source = "terraform-aws-modules/elasticache/aws//modules/serverless-cache"
|
||||
version = "1.4.1"
|
||||
|
||||
engine = "valkey"
|
||||
cache_name = "${local.name}-valkey-serverless"
|
||||
cache_usage_limits = {
|
||||
data_storage = {
|
||||
maximum = 2
|
||||
}
|
||||
ecpu_per_second = {
|
||||
maximum = 1000
|
||||
}
|
||||
}
|
||||
major_engine_version = 7
|
||||
engine = "valkey"
|
||||
cache_name = "${local.name}-valkey-serverless"
|
||||
major_engine_version = 8
|
||||
subnet_ids = module.vpc.database_subnets
|
||||
|
||||
security_group_ids = [
|
||||
|
||||
+57
-155
@@ -120,6 +120,7 @@ module "eks" {
|
||||
|
||||
enable_cluster_creator_admin_permissions = false
|
||||
cluster_endpoint_public_access = true
|
||||
cloudwatch_log_group_retention_in_days = 365
|
||||
|
||||
cluster_addons = {
|
||||
coredns = {
|
||||
@@ -352,7 +353,7 @@ resource "kubernetes_manifest" "node_pool" {
|
||||
}
|
||||
}
|
||||
limits = {
|
||||
cpu = 100
|
||||
cpu = 1000
|
||||
}
|
||||
disruption = {
|
||||
consolidationPolicy = "WhenEmpty"
|
||||
@@ -412,19 +413,65 @@ module "eks_blueprints_addons" {
|
||||
}
|
||||
|
||||
### Formbricks App
|
||||
module "s3-bucket" {
|
||||
data "aws_iam_policy_document" "replication_bucket_policy" {
|
||||
statement {
|
||||
sid = "Set-permissions-for-objects"
|
||||
effect = "Allow"
|
||||
|
||||
principals {
|
||||
type = "AWS"
|
||||
identifiers = [
|
||||
"arn:aws:iam::050559574035:role/service-role/s3crr_role_for_formbricks-cloud-uploads"
|
||||
]
|
||||
}
|
||||
|
||||
actions = [
|
||||
"s3:ReplicateObject",
|
||||
"s3:ReplicateDelete"
|
||||
]
|
||||
|
||||
resources = [
|
||||
"arn:aws:s3:::formbricks-cloud-eks/*"
|
||||
]
|
||||
}
|
||||
|
||||
statement {
|
||||
sid = "Set permissions on bucket"
|
||||
effect = "Allow"
|
||||
|
||||
principals {
|
||||
type = "AWS"
|
||||
identifiers = [
|
||||
"arn:aws:iam::050559574035:role/service-role/s3crr_role_for_formbricks-cloud-uploads"
|
||||
]
|
||||
}
|
||||
|
||||
actions = [
|
||||
"s3:GetBucketVersioning",
|
||||
"s3:PutBucketVersioning"
|
||||
]
|
||||
|
||||
resources = [
|
||||
"arn:aws:s3:::formbricks-cloud-eks"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module "formbricks_s3_bucket" {
|
||||
source = "terraform-aws-modules/s3-bucket/aws"
|
||||
version = "4.6.0"
|
||||
|
||||
bucket_prefix = "formbricks-"
|
||||
bucket = "formbricks-cloud-eks"
|
||||
force_destroy = true
|
||||
control_object_ownership = true
|
||||
object_ownership = "BucketOwnerPreferred"
|
||||
|
||||
versioning = {
|
||||
enabled = true
|
||||
}
|
||||
policy = data.aws_iam_policy_document.replication_bucket_policy.json
|
||||
}
|
||||
|
||||
|
||||
module "iam_policy" {
|
||||
module "formbricks_app_iam_policy" {
|
||||
source = "terraform-aws-modules/iam/aws//modules/iam-policy"
|
||||
version = "5.53.0"
|
||||
|
||||
@@ -441,8 +488,8 @@ module "iam_policy" {
|
||||
"s3:*",
|
||||
]
|
||||
Resource = [
|
||||
module.s3-bucket.s3_bucket_arn,
|
||||
"${module.s3-bucket.s3_bucket_arn}/*",
|
||||
module.formbricks_s3_bucket.s3_bucket_arn,
|
||||
"${module.formbricks_s3_bucket.s3_bucket_arn}/*",
|
||||
"arn:aws:s3:::formbricks-cloud-uploads",
|
||||
"arn:aws:s3:::formbricks-cloud-uploads/*"
|
||||
]
|
||||
@@ -451,14 +498,14 @@ module "iam_policy" {
|
||||
})
|
||||
}
|
||||
|
||||
module "formkey-aws-access" {
|
||||
module "formbricks_app_iam_role" {
|
||||
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
|
||||
version = "5.53.0"
|
||||
|
||||
role_name_prefix = "formbricks-"
|
||||
|
||||
role_policy_arns = {
|
||||
"formbricks" = module.iam_policy.arn
|
||||
"formbricks" = module.formbricks_app_iam_policy.arn
|
||||
}
|
||||
assume_role_condition_test = "StringLike"
|
||||
|
||||
@@ -469,148 +516,3 @@ module "formkey-aws-access" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resource "helm_release" "formbricks" {
|
||||
name = "formbricks"
|
||||
namespace = "formbricks"
|
||||
repository = "oci://ghcr.io/formbricks/helm-charts"
|
||||
chart = "formbricks"
|
||||
version = "3.5.1"
|
||||
max_history = 5
|
||||
|
||||
values = [
|
||||
<<-EOT
|
||||
postgresql:
|
||||
enabled: false
|
||||
redis:
|
||||
enabled: false
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: alb
|
||||
hosts:
|
||||
- host: "app.${local.domain}"
|
||||
paths:
|
||||
- path: /
|
||||
pathType: "Prefix"
|
||||
serviceName: "formbricks"
|
||||
annotations:
|
||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
||||
alb.ingress.kubernetes.io/target-type: ip
|
||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
|
||||
alb.ingress.kubernetes.io/ssl-redirect: "443"
|
||||
alb.ingress.kubernetes.io/certificate-arn: ${data.aws_acm_certificate.formbricks.arn}
|
||||
alb.ingress.kubernetes.io/healthcheck-path: "/health"
|
||||
alb.ingress.kubernetes.io/group.name: formbricks
|
||||
alb.ingress.kubernetes.io/ssl-policy: "ELBSecurityPolicy-TLS13-1-2-2021-06"
|
||||
secret:
|
||||
enabled: false
|
||||
rbac:
|
||||
enabled: true
|
||||
serviceAccount:
|
||||
enabled: true
|
||||
name: formbricks
|
||||
annotations:
|
||||
eks.amazonaws.com/role-arn: ${module.formkey-aws-access.iam_role_arn}
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
deployment:
|
||||
reloadOnChange: true
|
||||
nodeSelector:
|
||||
karpenter.sh/capacity-type: "on-demand"
|
||||
env:
|
||||
S3_BUCKET_NAME:
|
||||
value: "formbricks-cloud-uploads"
|
||||
RATE_LIMITING_DISABLED:
|
||||
value: "1"
|
||||
envFrom:
|
||||
app-env:
|
||||
type: secret
|
||||
nameSuffix: app-env
|
||||
externalSecret:
|
||||
enabled: true # Enable/disable ExternalSecrets
|
||||
secretStore:
|
||||
name: aws-secrets-manager
|
||||
kind: ClusterSecretStore
|
||||
refreshInterval: "1m"
|
||||
files:
|
||||
app-env:
|
||||
dataFrom:
|
||||
key: "prod/formbricks/environment"
|
||||
app-secrets:
|
||||
dataFrom:
|
||||
key: "prod/formbricks/secrets"
|
||||
cronJob:
|
||||
enabled: true
|
||||
jobs:
|
||||
survey-status:
|
||||
schedule: "0 0 * * *"
|
||||
successfulJobsHistoryLimit: 0
|
||||
env:
|
||||
CRON_SECRET:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "formbricks-app-env"
|
||||
key: "CRON_SECRET"
|
||||
WEBAPP_URL:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "formbricks-app-env"
|
||||
key: "WEBAPP_URL"
|
||||
image:
|
||||
repository: curlimages/curl
|
||||
tag: latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- 'curl -X POST -H "content-type: application/json" -H "x-api-key: $CRON_SECRET" "$WEBAPP_URL/api/cron/survey-status"'
|
||||
weekely-summary:
|
||||
schedule: "0 8 * * 1"
|
||||
successfulJobsHistoryLimit: 0
|
||||
env:
|
||||
CRON_SECRET:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "formbricks-app-env"
|
||||
key: "CRON_SECRET"
|
||||
WEBAPP_URL:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "formbricks-app-env"
|
||||
key: "WEBAPP_URL"
|
||||
image:
|
||||
repository: curlimages/curl
|
||||
tag: latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- 'curl -X POST -H "content-type: application/json" -H "x-api-key: $CRON_SECRET" "$WEBAPP_URL/api/cron/weekly-summary"'
|
||||
ping:
|
||||
schedule: "0 9 * * *"
|
||||
successfulJobsHistoryLimit: 0
|
||||
env:
|
||||
CRON_SECRET:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "formbricks-app-env"
|
||||
key: "CRON_SECRET"
|
||||
WEBAPP_URL:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "formbricks-app-env"
|
||||
key: "WEBAPP_URL"
|
||||
image:
|
||||
repository: curlimages/curl
|
||||
tag: latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- 'curl -X POST -H "content-type: application/json" -H "x-api-key: $CRON_SECRET" "$WEBAPP_URL/api/cron/ping"'
|
||||
EOT
|
||||
]
|
||||
}
|
||||
|
||||
# secrets password/keys
|
||||
|
||||
+10
-9
@@ -15,14 +15,14 @@ module "rds-aurora" {
|
||||
source = "terraform-aws-modules/rds-aurora/aws"
|
||||
version = "9.12.0"
|
||||
|
||||
name = "${local.name}-postgres"
|
||||
engine = data.aws_rds_engine_version.postgresql.engine
|
||||
engine_mode = "provisioned"
|
||||
engine_version = data.aws_rds_engine_version.postgresql.version
|
||||
storage_encrypted = true
|
||||
master_username = "formbricks"
|
||||
master_password = random_password.postgres.result
|
||||
manage_master_user_password = false
|
||||
name = "${local.name}-postgres"
|
||||
engine = data.aws_rds_engine_version.postgresql.engine
|
||||
engine_mode = "provisioned"
|
||||
engine_version = data.aws_rds_engine_version.postgresql.version
|
||||
storage_encrypted = true
|
||||
master_username = "formbricks"
|
||||
master_password = random_password.postgres.result
|
||||
manage_master_user_password = false
|
||||
create_db_cluster_parameter_group = true
|
||||
db_cluster_parameter_group_family = data.aws_rds_engine_version.postgresql.parameter_group_family
|
||||
db_cluster_parameter_group_parameters = [
|
||||
@@ -40,7 +40,8 @@ module "rds-aurora" {
|
||||
cidr_blocks = module.vpc.private_subnets_cidr_blocks
|
||||
}
|
||||
}
|
||||
performance_insights_enabled = true
|
||||
performance_insights_enabled = true
|
||||
cluster_performance_insights_enabled = true
|
||||
|
||||
apply_immediately = true
|
||||
skip_final_snapshot = true
|
||||
|
||||
@@ -3,10 +3,23 @@ resource "aws_secretsmanager_secret" "formbricks_app_secrets" {
|
||||
name = "prod/formbricks/secrets"
|
||||
}
|
||||
|
||||
resource "aws_secretsmanager_secret" "formbricks_app_secrets_temp" {
|
||||
name = "prod/formbricks/secrets_temp"
|
||||
}
|
||||
|
||||
resource "aws_secretsmanager_secret_version" "formbricks_app_secrets" {
|
||||
secret_id = aws_secretsmanager_secret.formbricks_app_secrets.id
|
||||
secret_string = jsonencode({
|
||||
# DATABASE_URL = "postgres://formbricks:${random_password.postgres.result}@${module.rds-aurora.cluster_endpoint}/formbricks"
|
||||
REDIS_URL = "rediss://formbricks:${random_password.valkey.result}@${module.valkey_serverless.serverless_cache_endpoint[0].address}:6379"
|
||||
# DATABASE_URL = "postgres://formbricks:${random_password.postgres.result}@${module.rds-aurora.cluster_endpoint}/formbricks"
|
||||
REDIS_URL = "rediss://:${random_password.valkey.result}@${module.valkey.replication_group_primary_endpoint_address}:6379"
|
||||
# REDIS_URL = "rediss://formbricks:${random_password.valkey.result}@${module.valkey_serverless.serverless_cache_endpoint[0].address}:6379"
|
||||
})
|
||||
}
|
||||
|
||||
resource "aws_secretsmanager_secret_version" "formbricks_app_secrets_temp" {
|
||||
secret_id = aws_secretsmanager_secret.formbricks_app_secrets_temp.id
|
||||
secret_string = jsonencode({
|
||||
DATABASE_URL = "postgres://formbricks:${random_password.postgres.result}@${module.rds-aurora.cluster_endpoint}/formbricks"
|
||||
# REDIS_URL = "rediss://formbricks:${random_password.valkey.result}@${module.valkey_serverless.serverless_cache_endpoint[0].address}:6379"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@formbricks/react-native",
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.1",
|
||||
"license": "MIT",
|
||||
"description": "Formbricks React Native SDK allows you to connect your app to Formbricks, display surveys and trigger events.",
|
||||
"homepage": "https://formbricks.com",
|
||||
@@ -41,24 +41,25 @@
|
||||
"coverage": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/api": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@types/react": "18.3.1",
|
||||
"@vitest/coverage-v8": "3.0.4",
|
||||
"react": "18.3.1",
|
||||
"react-native": "0.74.5",
|
||||
"terser": "5.37.0",
|
||||
"vite": "6.0.12",
|
||||
"vite-plugin-dts": "4.3.0",
|
||||
"vitest": "3.0.5",
|
||||
"react": "18.3.1",
|
||||
"@types/react": "18.3.1"
|
||||
"vitest": "3.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-native-async-storage/async-storage": ">=2.1.0",
|
||||
"react": ">=16.8.0",
|
||||
"react-native": ">=0.60.0",
|
||||
"react-native-webview": ">=13.0.0",
|
||||
"@react-native-async-storage/async-storage": ">=2.1.0"
|
||||
"react-native-webview": ">=13.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { fetch } from "@react-native-community/netinfo";
|
||||
import { RNConfig } from "@/lib/common/config";
|
||||
import { Logger } from "@/lib/common/logger";
|
||||
import { shouldDisplayBasedOnPercentage } from "@/lib/common/utils";
|
||||
@@ -62,24 +63,49 @@ export const trackAction = (name: string, alias?: string): Result<void, NetworkE
|
||||
* @param code - The action code to track
|
||||
* @returns Result indicating success, network error, or invalid code error
|
||||
*/
|
||||
export const track = (code: string): Result<void, NetworkError> | Result<void, InvalidCodeError> => {
|
||||
const appConfig = RNConfig.getInstance();
|
||||
export const track = async (
|
||||
code: string
|
||||
): Promise<
|
||||
| Result<void, NetworkError>
|
||||
| Result<void, InvalidCodeError>
|
||||
| Result<void, { code: "error"; message: string }>
|
||||
> => {
|
||||
try {
|
||||
const appConfig = RNConfig.getInstance();
|
||||
|
||||
const {
|
||||
environment: {
|
||||
data: { actionClasses = [] },
|
||||
},
|
||||
} = appConfig.get();
|
||||
const netInfo = await fetch();
|
||||
|
||||
const codeActionClasses = actionClasses.filter((action) => action.type === "code");
|
||||
const actionClass = codeActionClasses.find((action) => action.key === code);
|
||||
if (!netInfo.isConnected) {
|
||||
return err({
|
||||
code: "network_error",
|
||||
status: 500,
|
||||
message: "No internet connection. Please check your connection and try again.",
|
||||
responseMessage: "No internet connection. Please check your connection and try again.",
|
||||
url: new URL(`${appConfig.get().appUrl}/js/surveys.umd.cjs`),
|
||||
});
|
||||
}
|
||||
|
||||
if (!actionClass) {
|
||||
const {
|
||||
environment: {
|
||||
data: { actionClasses = [] },
|
||||
},
|
||||
} = appConfig.get();
|
||||
|
||||
const codeActionClasses = actionClasses.filter((action) => action.type === "code");
|
||||
const actionClass = codeActionClasses.find((action) => action.key === code);
|
||||
|
||||
if (!actionClass) {
|
||||
return err({
|
||||
code: "invalid_code",
|
||||
message: `${code} action unknown. Please add this action in Formbricks first in order to use it in your code.`,
|
||||
});
|
||||
}
|
||||
|
||||
return trackAction(actionClass.name, code);
|
||||
} catch (error) {
|
||||
return err({
|
||||
code: "invalid_code",
|
||||
message: `${code} action unknown. Please add this action in Formbricks first in order to use it in your code.`,
|
||||
code: "error",
|
||||
message: "Error tracking action",
|
||||
});
|
||||
}
|
||||
|
||||
return trackAction(actionClass.name, code);
|
||||
};
|
||||
|
||||
@@ -36,6 +36,12 @@ vi.mock("@/lib/common/utils", () => ({
|
||||
shouldDisplayBasedOnPercentage: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@react-native-community/netinfo", () => ({
|
||||
fetch: vi.fn(() => ({
|
||||
isConnected: true,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe("survey/action.ts", () => {
|
||||
const mockSurvey = {
|
||||
id: "survey_001",
|
||||
@@ -153,15 +159,14 @@ describe("survey/action.ts", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("tracks a valid action by code", () => {
|
||||
const result = track("testCode");
|
||||
test("tracks a valid action by code", async () => {
|
||||
const result = await track("testCode");
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
// expect(mockLogger.debug).toHaveBeenCalledWith('Formbricks: Action "testAction" tracked');
|
||||
});
|
||||
|
||||
test("returns error for invalid action code", () => {
|
||||
const result = track("invalidCode");
|
||||
test("returns error for invalid action code", async () => {
|
||||
const result = await track("invalidCode");
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
|
||||
|
||||
Generated
+879
-1166
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user