diff --git a/argocd-apps/homelab.yaml b/argocd-apps/homelab.yaml index f27427d..bd8de40 100644 --- a/argocd-apps/homelab.yaml +++ b/argocd-apps/homelab.yaml @@ -855,4 +855,26 @@ spec: syncOptions: - CreateNamespace=true - PruneLast=true +--- +# Booking (Cal.com) +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: booking + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: homelab + source: + repoURL: https://gitea.dooplex.hu/admin/homelab-manifests.git + targetRevision: main + path: booking-system + destination: + server: https://kubernetes.default.svc + namespace: booking-system + syncPolicy: + syncOptions: + - CreateNamespace=true + - PruneLast=true --- \ No newline at end of file diff --git a/booking-system/booking.yaml b/booking-system/booking.yaml new file mode 100644 index 0000000..5ff4636 --- /dev/null +++ b/booking-system/booking.yaml @@ -0,0 +1,513 @@ +--- +# Cal.com - Open-source scheduling infrastructure +# https://cal.com/ +# +# Features: +# - Stripe payment integration (native) +# - Configurable time slots +# - Cancellation policies with fees +# - Google Calendar sync +# - Email notifications +# - Multiple event types +# - Booking notes +# - White-label capable +# - OIDC/SAML SSO support (Authentik integration!) +# +# Prerequisites: +# 1. Create databases in shared PostgreSQL: +# kubectl exec -it postgresql-1 -n database-system -- psql -U postgres +# +# -- Main database +# CREATE DATABASE calcom; +# CREATE USER calcom WITH PASSWORD 'your-secure-password'; +# GRANT ALL PRIVILEGES ON DATABASE calcom TO calcom; +# \c calcom +# GRANT ALL ON SCHEMA public TO calcom; +# +# -- SSO/SAML database (required for OIDC!) +# CREATE DATABASE calcom_saml; +# GRANT ALL PRIVILEGES ON DATABASE calcom_saml TO calcom; +# \c calcom_saml +# GRANT ALL ON SCHEMA public TO calcom; +# +# 2. Set up Stripe account at https://stripe.com (Hungary supported) +# Get API keys from Dashboard -> Developers -> API keys +# +# 3. Set up Authentik OIDC provider: +# - Create OAuth2/OpenID Provider in Authentik +# - Client type: Confidential +# - Redirect URIs: https://booking.dooplex.hu/api/auth/callback/oidc +# - Note the Client ID, Client Secret +# - Well-known URL: https://authentik.dooplex.hu/application/o//.well-known/openid-configuration +# +# 4. (Optional) Set up Google OAuth for calendar sync: +# https://console.cloud.google.com/apis/credentials +# +# After deployment: +# 1. Access https://booking.dooplex.hu +# 2. Create first user with email matching SAML_ADMINS +# 3. Go to Settings → Security → SSO +# 4. Click "Configure SSO with OIDC" +# 5. Enter Client ID, Client Secret, Well-known URL from Authentik +# 6. Configure event types, availability, and Stripe integration +--- +apiVersion: v1 +kind: Namespace +metadata: + name: booking-system + labels: + app.kubernetes.io/name: calcom +--- +# Redis for Cal.com (required for sessions, rate limiting, etc.) +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: calcom-redis + namespace: booking-system + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: redis +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: calcom-redis + namespace: booking-system + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: redis +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: redis + template: + metadata: + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: redis + spec: + containers: + - name: redis + image: redis:7-alpine + imagePullPolicy: IfNotPresent + args: + - redis-server + - --appendonly + - "yes" + - --maxmemory + - "256mb" + - --maxmemory-policy + - "allkeys-lru" + ports: + - containerPort: 6379 + name: redis + protocol: TCP + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi + volumeMounts: + - name: data + mountPath: /data + livenessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: data + persistentVolumeClaim: + claimName: calcom-redis +--- +apiVersion: v1 +kind: Service +metadata: + name: calcom-redis + namespace: booking-system + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: redis +spec: + type: ClusterIP + ports: + - port: 6379 + targetPort: redis + protocol: TCP + name: redis + selector: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: redis +--- +# Cal.com Web Application +apiVersion: apps/v1 +kind: Deployment +metadata: + name: calcom + namespace: booking-system + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: calcom + app.kubernetes.io/version: "v6.0.8" +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: calcom + template: + metadata: + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: calcom + app.kubernetes.io/version: "v6.0.8" + spec: + initContainers: + # Wait for PostgreSQL + - name: wait-for-db + image: busybox:1.36 + command: + - sh + - -c + - | + echo "Waiting for PostgreSQL..." + until nc -z postgresql-rw.database-system.svc.cluster.local 5432; do + echo "PostgreSQL not ready, waiting..." + sleep 2 + done + echo "PostgreSQL is ready!" + # Wait for Redis + - name: wait-for-redis + image: busybox:1.36 + command: + - sh + - -c + - | + echo "Waiting for Redis..." + until nc -z calcom-redis 6379; do + echo "Redis not ready, waiting..." + sleep 2 + done + echo "Redis is ready!" + containers: + - name: calcom + image: calcom/cal.com:v6.0.8 + imagePullPolicy: IfNotPresent + env: + # License (empty for AGPL, set key for enterprise features) + - name: CALCOM_LICENSE_KEY + value: "" + # Telemetry + - name: NEXT_PUBLIC_IS_E2E + value: "false" + - name: CALCOM_TELEMETRY_DISABLED + value: "1" + + # URLs + - name: NEXT_PUBLIC_WEBAPP_URL + value: "https://booking.dooplex.hu" + - name: NEXTAUTH_URL + value: "https://booking.dooplex.hu/api/auth" + - name: NEXT_PUBLIC_API_V2_URL + value: "https://booking.dooplex.hu/api/v2" + + # Database - using shared PostgreSQL + - name: DATABASE_URL + value: "postgresql://$(DB_USER):$(DB_PASS)@postgresql-rw.database-system.svc.cluster.local:5432/calcom" + - name: DATABASE_DIRECT_URL + value: "postgresql://$(DB_USER):$(DB_PASS)@postgresql-rw.database-system.svc.cluster.local:5432/calcom" + - name: DB_USER + valueFrom: + secretKeyRef: + name: calcom-db + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: calcom-db + key: password + + # Redis + - name: REDIS_URL + value: "redis://calcom-redis:6379" + + # SSO/OIDC Configuration (for Authentik integration) + # Requires separate database - see prerequisites + - name: SAML_DATABASE_URL + value: "postgresql://$(DB_USER):$(DB_PASS)@postgresql-rw.database-system.svc.cluster.local:5432/calcom_saml" + # Admin email(s) who can configure SSO in the UI + # Must match the email used when creating the first user + - name: SAML_ADMINS + valueFrom: + secretKeyRef: + name: calcom-app + key: saml-admin-email + + # Auth secrets + - name: NEXTAUTH_SECRET + valueFrom: + secretKeyRef: + name: calcom-app + key: nextauth-secret + - name: CALENDSO_ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: calcom-app + key: calendso-encryption-key + + # Email/SMTP + - name: EMAIL_FROM + valueFrom: + secretKeyRef: + name: smtp-credentials + key: from-address + - name: EMAIL_SERVER_HOST + valueFrom: + secretKeyRef: + name: smtp-credentials + key: host + - name: EMAIL_SERVER_PORT + valueFrom: + secretKeyRef: + name: smtp-credentials + key: port + - name: EMAIL_SERVER_USER + valueFrom: + secretKeyRef: + name: smtp-credentials + key: username + - name: EMAIL_SERVER_PASSWORD + valueFrom: + secretKeyRef: + name: smtp-credentials + key: password + + # Stripe (optional - for payments) + - name: STRIPE_API_KEY + valueFrom: + secretKeyRef: + name: calcom-app + key: stripe-api-key + - name: STRIPE_WEBHOOK_SECRET + valueFrom: + secretKeyRef: + name: calcom-app + key: stripe-webhook-secret + - name: NEXT_PUBLIC_STRIPE_PUBLIC_KEY + valueFrom: + secretKeyRef: + name: calcom-app + key: next-public-stripe-key + - name: PAYMENT_FEE_PERCENTAGE + value: "0" + - name: PAYMENT_FEE_FIXED + value: "0" + + # Google Calendar (optional) + - name: GOOGLE_API_CREDENTIALS + valueFrom: + secretKeyRef: + name: calcom-app + key: google-client-id + optional: true + + # Timezone + - name: TZ + value: "Europe/Budapest" + + # Misc + - name: NODE_ENV + value: "production" + - name: NODE_TLS_REJECT_UNAUTHORIZED + value: "0" + # Allow signup (set to "false" after creating your account if you want to restrict) + - name: NEXT_PUBLIC_DISABLE_SIGNUP + value: "false" + # CSP - needed for embedded iframes if you want to embed booking widget + - name: CSP_POLICY + value: "" + + ports: + - containerPort: 3000 + name: http + protocol: TCP + resources: + requests: + cpu: 200m + memory: 512Mi + limits: + cpu: "2" + memory: 2Gi + livenessProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 + readinessProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 60 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + startupProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 10 + failureThreshold: 30 +--- +apiVersion: v1 +kind: Service +metadata: + name: calcom + namespace: booking-system + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: calcom +spec: + type: ClusterIP + ports: + - port: 3000 + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: calcom +--- +# Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: calcom + namespace: booking-system + labels: + app.kubernetes.io/instance: calcom + app.kubernetes.io/name: calcom + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + external-dns.alpha.kubernetes.io/hostname: booking.dooplex.hu,booking.home + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "64m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/proxy-send-timeout: "300" + # Required for WebSocket connections (if using Cal.com video) + nginx.ingress.kubernetes.io/proxy-http-version: "1.1" + nginx.ingress.kubernetes.io/proxy-set-headers: "booking-system/calcom-proxy-headers" +spec: + ingressClassName: nginx-internal + rules: + - host: booking.dooplex.hu + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: calcom + port: + number: 3000 + - host: booking.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: calcom + port: + number: 3000 + tls: + - hosts: + - booking.dooplex.hu + secretName: calcom-tls +--- +# ConfigMap for nginx proxy headers (WebSocket support) +apiVersion: v1 +kind: ConfigMap +metadata: + name: calcom-proxy-headers + namespace: booking-system +data: + Upgrade: "$http_upgrade" + Connection: "upgrade" +--- +# Optional: Prisma Studio for database management/first user creation +# Uncomment if you need direct database access +# Access via port-forward: kubectl port-forward svc/calcom-prisma-studio 5555:5555 -n booking-system +# --- +# apiVersion: apps/v1 +# kind: Deployment +# metadata: +# name: calcom-prisma-studio +# namespace: booking-system +# labels: +# app.kubernetes.io/instance: calcom +# app.kubernetes.io/name: prisma-studio +# spec: +# replicas: 1 +# selector: +# matchLabels: +# app.kubernetes.io/instance: calcom +# app.kubernetes.io/name: prisma-studio +# template: +# metadata: +# labels: +# app.kubernetes.io/instance: calcom +# app.kubernetes.io/name: prisma-studio +# spec: +# containers: +# - name: prisma-studio +# image: calcom/cal.com:v5.9.15 +# command: ["npx", "prisma", "studio"] +# env: +# - name: DATABASE_URL +# value: "postgresql://calcom:YOUR_PASSWORD@postgresql-rw.database-system.svc.cluster.local:5432/calcom" +# ports: +# - containerPort: 5555 +# name: http +# --- +# apiVersion: v1 +# kind: Service +# metadata: +# name: calcom-prisma-studio +# namespace: booking-system +# spec: +# type: ClusterIP +# ports: +# - port: 5555 +# targetPort: 5555 +# selector: +# app.kubernetes.io/instance: calcom +# app.kubernetes.io/name: prisma-studio \ No newline at end of file