Adds typebot app.

This commit is contained in:
2026-06-19 21:33:26 +00:00
parent a2852dab0a
commit 0449272102
12 changed files with 454 additions and 0 deletions

5
typebot/app.yaml Normal file
View File

@@ -0,0 +1,5 @@
name: typebot
is: typebot
description: Typebot is a powerful open-source chatbot builder that lets you create conversational forms and chatbots with a no-code interface.
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/typebot.svg
latest: "3"

View File

@@ -0,0 +1,38 @@
# Typebot
Typebot is a chatbot builder that lets you create conversational forms, surveys, and chat flows, and embed them anywhere.
## Dependencies
- **PostgreSQL** - Database for storing bots and submissions
- **SMTP** - For magic link login emails
## Configuration
Key settings in `config.yaml`:
- **builderDomain** - Where the Typebot builder UI is accessible (default: `typebot.{your-cloud-domain}`)
- **viewerDomain** - Where published chatbots are served (default: `bot.{your-cloud-domain}`)
- **adminEmail** - Email address that is granted admin access (defaults to your operator email)
- **storage** - Persistent volume size (default: `2Gi`)
- **smtp** - Email settings inherited from your Wild Cloud SMTP service
## First-Time Setup
1. Add and deploy the app:
```bash
wild app add typebot
wild app deploy typebot
```
2. Visit the builder URL and sign in with the `adminEmail` address.
3. Typebot sends a magic link to that email — click it to log in.
4. Create your first typebot from the dashboard and publish it to the viewer domain.
## Notes
- Typebot uses **magic link authentication** — SMTP must be working for login to function
- The builder UI and the viewer are on separate domains — both must be accessible for published bots to work
- The `adminEmail` is the only account that can access the builder by default; invite others via the workspace settings

View File

@@ -0,0 +1,63 @@
apiVersion: batch/v1
kind: Job
metadata:
name: typebot-db-init
labels:
component: db-init
spec:
template:
metadata:
labels:
component: db-init
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
seccompProfile:
type: RuntimeDefault
containers:
- name: db-init
image: postgres:17
command: ["/bin/bash", "-c"]
args:
- |
PGPASSWORD=${POSTGRES_ADMIN_PASSWORD} psql -h ${DB_HOSTNAME} -U postgres <<EOF
DO \$\$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '${DB_USER}') THEN
CREATE USER ${DB_USER} WITH ENCRYPTED PASSWORD '${DB_PASSWORD}';
ELSE
ALTER USER ${DB_USER} WITH ENCRYPTED PASSWORD '${DB_PASSWORD}';
END IF;
END
\$\$;
SELECT 'CREATE DATABASE ${DB_NAME}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${DB_NAME}')\gexec
ALTER DATABASE ${DB_NAME} OWNER TO ${DB_USER};
GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};
EOF
env:
- name: POSTGRES_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secrets
key: password
- name: DB_HOSTNAME
value: "{{ .db.host }}"
- name: DB_NAME
value: "{{ .db.name }}"
- name: DB_USER
value: "{{ .db.user }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: typebot-secrets
key: dbPassword
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
restartPolicy: OnFailure

View File

@@ -0,0 +1,108 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: typebot-builder
namespace: {{ .namespace }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
component: builder
template:
metadata:
labels:
component: builder
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: typebot-builder
image: baptistearno/typebot-builder:3.17.1
ports:
- name: http
containerPort: 3000
protocol: TCP
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: typebot-secrets
key: dbUrl
- name: NEXTAUTH_SECRET
valueFrom:
secretKeyRef:
name: typebot-secrets
key: nextauthSecret
- name: ENCRYPTION_SECRET
valueFrom:
secretKeyRef:
name: typebot-secrets
key: encryptionSecret
- name: NEXTAUTH_URL
value: https://{{ .builderDomain }}
- name: NEXT_PUBLIC_VIEWER_URL
value: https://{{ .viewerDomain }}
- name: ADMIN_EMAIL
value: "{{ .adminEmail }}"
- name: SMTP_HOST
value: "{{ .smtp.host }}"
- name: SMTP_PORT
value: "{{ .smtp.port }}"
- name: SMTP_USERNAME
value: "{{ .smtp.user }}"
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: typebot-secrets
key: smtpPassword
- name: SMTP_FROM
value: "{{ .smtp.from }}"
- name: SMTP_SECURE
value: "true"
- name: DISABLE_SIGNUP
value: "false"
resources:
limits:
cpu: 500m
ephemeral-storage: 1Gi
memory: 768Mi
requests:
cpu: 50m
ephemeral-storage: 50Mi
memory: 256Mi
volumeMounts:
- name: typebot-data
mountPath: /app/packages/scripts/uploads
livenessProbe:
httpGet:
path: /signin
port: 3000
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 15
failureThreshold: 6
readinessProbe:
httpGet:
path: /signin
port: 3000
initialDelaySeconds: 30
timeoutSeconds: 3
periodSeconds: 10
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
volumes:
- name: typebot-data
persistentVolumeClaim:
claimName: typebot-data
restartPolicy: Always

View File

@@ -0,0 +1,97 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: typebot-viewer
namespace: {{ .namespace }}
spec:
replicas: 1
strategy:
type: RollingUpdate
selector:
matchLabels:
component: viewer
template:
metadata:
labels:
component: viewer
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: typebot-viewer
image: baptistearno/typebot-viewer:3.17.1
ports:
- name: http
containerPort: 3000
protocol: TCP
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: typebot-secrets
key: dbUrl
- name: NEXTAUTH_SECRET
valueFrom:
secretKeyRef:
name: typebot-secrets
key: nextauthSecret
- name: ENCRYPTION_SECRET
valueFrom:
secretKeyRef:
name: typebot-secrets
key: encryptionSecret
- name: NEXTAUTH_URL
value: https://{{ .viewerDomain }}
- name: NEXT_PUBLIC_VIEWER_URL
value: https://{{ .viewerDomain }}
- name: BUILDER_URL
value: https://{{ .builderDomain }}
- name: SMTP_HOST
value: "{{ .smtp.host }}"
- name: SMTP_PORT
value: "{{ .smtp.port }}"
- name: SMTP_USERNAME
value: "{{ .smtp.user }}"
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: typebot-secrets
key: smtpPassword
- name: SMTP_FROM
value: "{{ .smtp.from }}"
- name: SMTP_SECURE
value: "true"
resources:
limits:
cpu: 500m
ephemeral-storage: 1Gi
memory: 512Mi
requests:
cpu: 50m
ephemeral-storage: 50Mi
memory: 256Mi
livenessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 15
failureThreshold: 6
readinessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 30
timeoutSeconds: 3
periodSeconds: 10
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
restartPolicy: Always

View File

@@ -0,0 +1,53 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: typebot-builder
namespace: {{ .namespace }}
annotations:
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/ttl: "60"
spec:
ingressClassName: traefik
rules:
- host: {{ .builderDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: typebot-builder
port:
number: 80
tls:
- hosts:
- {{ .builderDomain }}
secretName: {{ .tlsSecretName }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: typebot-viewer
namespace: {{ .namespace }}
annotations:
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/ttl: "60"
spec:
ingressClassName: traefik
rules:
- host: {{ .viewerDomain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: typebot-viewer
port:
number: 80
tls:
- hosts:
- {{ .viewerDomain }}
secretName: {{ .tlsSecretName }}

View File

@@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{ .namespace }}
labels:
- includeSelectors: true
pairs:
app: typebot
managedBy: kustomize
partOf: wild-cloud
resources:
- namespace.yaml
- deployment-builder.yaml
- deployment-viewer.yaml
- service-builder.yaml
- service-viewer.yaml
- ingress.yaml
- pvc.yaml
- db-init-job.yaml

View File

@@ -0,0 +1,31 @@
version: 3.17.1-1
requires:
- name: postgres
- name: smtp
defaultConfig:
namespace: typebot
externalDnsDomain: '{{ .cloud.domain }}'
builderDomain: typebot.{{ .cloud.domain }}
viewerDomain: bot.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls
storage: 2Gi
db:
host: '{{ .apps.postgres.host }}'
port: '{{ .apps.postgres.port }}'
name: typebot
user: typebot
adminEmail: '{{ .operator.email }}'
smtp:
host: '{{ .apps.smtp.host }}'
port: '{{ .apps.smtp.port }}'
from: '{{ .apps.smtp.from }}'
user: '{{ .apps.smtp.user }}'
defaultSecrets:
- key: nextauthSecret
- key: encryptionSecret
- key: dbPassword
- key: dbUrl
default: 'postgresql://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}?sslmode=disable'
- key: smtpPassword
requiredSecrets:
- postgres.password

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ .namespace }}

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: typebot-data
namespace: {{ .namespace }}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .storage }}

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: typebot-builder
namespace: {{ .namespace }}
spec:
selector:
component: builder
ports:
- name: http
port: 80
targetPort: 3000
protocol: TCP

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: typebot-viewer
namespace: {{ .namespace }}
spec:
selector:
component: viewer
ports:
- name: http
port: 80
targetPort: 3000
protocol: TCP