vibecarbon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +663 -0
- package/README.md +188 -0
- package/carbon/.claude/hooks/task-completed-gate.sh +19 -0
- package/carbon/.claude/hooks/teammate-idle-gate.sh +29 -0
- package/carbon/.claude/settings.json +29 -0
- package/carbon/.cursor/rules/vibecarbon.mdc +6 -0
- package/carbon/.dockerignore +57 -0
- package/carbon/.env.example +219 -0
- package/carbon/.github/copilot-instructions.md +3 -0
- package/carbon/.github/workflows/deploy.yml +346 -0
- package/carbon/.github/workflows/vibecarbon-build.yml +81 -0
- package/carbon/.windsurfrules +74 -0
- package/carbon/AGENTS.md +422 -0
- package/carbon/CLAUDE.md +3 -0
- package/carbon/DEVELOPMENT.md +187 -0
- package/carbon/Dockerfile +98 -0
- package/carbon/LICENSE +21 -0
- package/carbon/PRODUCTION.md +364 -0
- package/carbon/README.md +193 -0
- package/carbon/TESTING.md +138 -0
- package/carbon/backup/Dockerfile +75 -0
- package/carbon/backup/backup.sh +95 -0
- package/carbon/backup/compose-backup.sh +140 -0
- package/carbon/biome.json +83 -0
- package/carbon/cloud-init/docker-ce-setup.yaml +55 -0
- package/carbon/cloud-init/k3s/master-init.sh +215 -0
- package/carbon/cloud-init/k3s/supabase-init.sh +167 -0
- package/carbon/cloud-init/k3s/worker-init.sh +147 -0
- package/carbon/components.json +24 -0
- package/carbon/content/blog/authentication-guide.mdx +34 -0
- package/carbon/content/blog/getting-started.mdx +41 -0
- package/carbon/content/changelog/v0-1-0.mdx +40 -0
- package/carbon/content/docs/analytics.mdx +90 -0
- package/carbon/content/docs/authentication.mdx +231 -0
- package/carbon/content/docs/background-jobs.mdx +116 -0
- package/carbon/content/docs/cli.mdx +630 -0
- package/carbon/content/docs/database.mdx +236 -0
- package/carbon/content/docs/deployment.mdx +227 -0
- package/carbon/content/docs/development.mdx +238 -0
- package/carbon/content/docs/environments.mdx +84 -0
- package/carbon/content/docs/getting-started.mdx +112 -0
- package/carbon/content/docs/legal/privacy-policy.mdx +89 -0
- package/carbon/content/docs/legal/terms-of-service.mdx +99 -0
- package/carbon/content/docs/optional-services.mdx +160 -0
- package/carbon/db/Dockerfile +23 -0
- package/carbon/docker-compose.dev-init.yml +5 -0
- package/carbon/docker-compose.metabase.override.yml +14 -0
- package/carbon/docker-compose.metabase.prod.yml +28 -0
- package/carbon/docker-compose.metabase.yml +84 -0
- package/carbon/docker-compose.n8n.override.yml +14 -0
- package/carbon/docker-compose.n8n.prod.yml +31 -0
- package/carbon/docker-compose.n8n.yml +97 -0
- package/carbon/docker-compose.observability.override.yml +18 -0
- package/carbon/docker-compose.observability.prod.yml +28 -0
- package/carbon/docker-compose.observability.yml +125 -0
- package/carbon/docker-compose.override.yml +28 -0
- package/carbon/docker-compose.prod.yml +294 -0
- package/carbon/docker-compose.yml +508 -0
- package/carbon/docker-entrypoint.sh +12 -0
- package/carbon/ha/activate-standby.sh +52 -0
- package/carbon/ha/primary-init.sql +36 -0
- package/carbon/ha/standby-init.sh +74 -0
- package/carbon/k8s/LICENSE +99 -0
- package/carbon/k8s/README.md +272 -0
- package/carbon/k8s/base/app/deployment.yaml +130 -0
- package/carbon/k8s/base/app/hpa.yaml +44 -0
- package/carbon/k8s/base/app/kustomization.yaml +10 -0
- package/carbon/k8s/base/app/network-policy.yaml +124 -0
- package/carbon/k8s/base/app/pdb.yaml +14 -0
- package/carbon/k8s/base/app/rbac.yaml +36 -0
- package/carbon/k8s/base/app/service.yaml +16 -0
- package/carbon/k8s/base/backup/cronjob.yaml +120 -0
- package/carbon/k8s/base/backup/kustomization.yaml +7 -0
- package/carbon/k8s/base/backup/network-policy.yaml +31 -0
- package/carbon/k8s/base/backup/rbac.yaml +7 -0
- package/carbon/k8s/base/cluster-autoscaler/deployment.yaml +103 -0
- package/carbon/k8s/base/cluster-autoscaler/kustomization.yaml +17 -0
- package/carbon/k8s/base/cluster-autoscaler/rbac.yaml +109 -0
- package/carbon/k8s/base/config/configmap.yaml +48 -0
- package/carbon/k8s/base/config/kustomization.yaml +8 -0
- package/carbon/k8s/base/hetzner-ccm/kustomization.yaml +9 -0
- package/carbon/k8s/base/hetzner-csi/kustomization.yaml +9 -0
- package/carbon/k8s/base/kustomization.yaml +43 -0
- package/carbon/k8s/base/namespace.yaml +7 -0
- package/carbon/k8s/base/network-policies.yaml +95 -0
- package/carbon/k8s/base/registry/kustomization.yaml +5 -0
- package/carbon/k8s/base/registry/local-registry.yaml +193 -0
- package/carbon/k8s/base/traefik/certificate.yaml +21 -0
- package/carbon/k8s/base/traefik/configmap.yaml +13 -0
- package/carbon/k8s/base/traefik/deployment.yaml +165 -0
- package/carbon/k8s/base/traefik/ingressroute.yaml +141 -0
- package/carbon/k8s/base/traefik/kustomization.yaml +11 -0
- package/carbon/k8s/base/traefik/middleware.yaml +109 -0
- package/carbon/k8s/base/traefik/network-policy.yaml +92 -0
- package/carbon/k8s/base/traefik/service.yaml +38 -0
- package/carbon/k8s/flux/README.md +49 -0
- package/carbon/k8s/flux/clusters/primary/vibecarbon.yaml +128 -0
- package/carbon/k8s/flux/clusters/standby/vibecarbon.yaml +83 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-release.yaml +38 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-repository.yaml +16 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/kustomization.yaml +10 -0
- package/carbon/k8s/gitops/supabase/helm-release.yaml +66 -0
- package/carbon/k8s/gitops/supabase/helm-repository.yaml +18 -0
- package/carbon/k8s/gitops/supabase/kustomization.yaml +33 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-cloudflare.yaml +51 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-hetzner.yaml +59 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-manual.yaml +43 -0
- package/carbon/k8s/infra/cert-manager-resources/kustomization.yaml +15 -0
- package/carbon/k8s/infra/kustomization.yaml +21 -0
- package/carbon/k8s/infra/traefik-crds/kustomization.yaml +10 -0
- package/carbon/k8s/overlays/local/configmap.yaml +23 -0
- package/carbon/k8s/overlays/local/ingressroute-studio.yaml +104 -0
- package/carbon/k8s/overlays/local/kustomization.yaml +215 -0
- package/carbon/k8s/overlays/local/namespace.yaml +8 -0
- package/carbon/k8s/overlays/local/secrets.yaml +32 -0
- package/carbon/k8s/overlays/production/kustomization.yaml +34 -0
- package/carbon/k8s/test-local.sh +318 -0
- package/carbon/k8s/values/supabase.values.yaml +133 -0
- package/carbon/package.json +154 -0
- package/carbon/runtime.Dockerfile +17 -0
- package/carbon/scripts/_dev-jwt.mjs +45 -0
- package/carbon/scripts/check-rls.ts +53 -0
- package/carbon/scripts/dev-init.js +191 -0
- package/carbon/scripts/dev.js +191 -0
- package/carbon/scripts/docker-down.js +63 -0
- package/carbon/scripts/docker-logs.js +59 -0
- package/carbon/scripts/docker-up.js +222 -0
- package/carbon/scripts/generate-dev-configs.sh +131 -0
- package/carbon/scripts/generate-rss.ts +116 -0
- package/carbon/scripts/generate-sitemap.ts +102 -0
- package/carbon/scripts/k8s-apply.js +71 -0
- package/carbon/scripts/k8s-delete.js +75 -0
- package/carbon/scripts/lib/manifest.js +176 -0
- package/carbon/scripts/secret-scan.mjs +278 -0
- package/carbon/scripts/validate-dev-configs.sh +101 -0
- package/carbon/src/client/App.tsx +202 -0
- package/carbon/src/client/assets/hyperformant-dark.svg +29 -0
- package/carbon/src/client/assets/hyperformant-light.svg +29 -0
- package/carbon/src/client/assets/logos/aider.svg +1 -0
- package/carbon/src/client/assets/logos/antigravity.svg +1 -0
- package/carbon/src/client/assets/logos/bolt.svg +1 -0
- package/carbon/src/client/assets/logos/claude-code.svg +1 -0
- package/carbon/src/client/assets/logos/copilot.svg +1 -0
- package/carbon/src/client/assets/logos/cursor.svg +1 -0
- package/carbon/src/client/assets/logos/gemini-cli.svg +1 -0
- package/carbon/src/client/assets/logos/lovable.svg +1 -0
- package/carbon/src/client/assets/logos/openai-codex.svg +1 -0
- package/carbon/src/client/assets/logos/v0.svg +1 -0
- package/carbon/src/client/assets/logos/windsurf.svg +1 -0
- package/carbon/src/client/assets/vibecarbon-icon.svg +19 -0
- package/carbon/src/client/assets/vibecarbon-logo-dark.svg +29 -0
- package/carbon/src/client/assets/vibecarbon-logo-light.svg +29 -0
- package/carbon/src/client/components/AIIntegrationSection.tsx +120 -0
- package/carbon/src/client/components/AppSidebar.tsx +760 -0
- package/carbon/src/client/components/ArchitectureSection.tsx +46 -0
- package/carbon/src/client/components/CTAFooter.tsx +59 -0
- package/carbon/src/client/components/ComparisonSection.tsx +132 -0
- package/carbon/src/client/components/ContentPanel.tsx +66 -0
- package/carbon/src/client/components/ContentSkeleton.tsx +21 -0
- package/carbon/src/client/components/ErrorBoundary.tsx +46 -0
- package/carbon/src/client/components/FAQSection.tsx +76 -0
- package/carbon/src/client/components/FileUpload.tsx +210 -0
- package/carbon/src/client/components/HeaderActions.tsx +17 -0
- package/carbon/src/client/components/Hero.tsx +608 -0
- package/carbon/src/client/components/ImpersonationBanner.tsx +31 -0
- package/carbon/src/client/components/LanguageSwitcher.tsx +67 -0
- package/carbon/src/client/components/Logo.tsx +87 -0
- package/carbon/src/client/components/LogoStrip.tsx +76 -0
- package/carbon/src/client/components/MetricsStrip.tsx +48 -0
- package/carbon/src/client/components/Nav.tsx +195 -0
- package/carbon/src/client/components/NewsletterSignup.tsx +147 -0
- package/carbon/src/client/components/NotificationDrawer.tsx +171 -0
- package/carbon/src/client/components/PageHeader.tsx +24 -0
- package/carbon/src/client/components/PlanGate.tsx +36 -0
- package/carbon/src/client/components/PricingSection.tsx +371 -0
- package/carbon/src/client/components/SEO.tsx +34 -0
- package/carbon/src/client/components/SmoothScroll.tsx +45 -0
- package/carbon/src/client/components/TechStackSection.tsx +165 -0
- package/carbon/src/client/components/WorkflowSection.tsx +604 -0
- package/carbon/src/client/components/admin/DockerLogs.tsx +276 -0
- package/carbon/src/client/components/admin/PerformanceMetrics.tsx +230 -0
- package/carbon/src/client/components/admin/ServiceCard.tsx +45 -0
- package/carbon/src/client/components/admin/ServicesStatus.tsx +296 -0
- package/carbon/src/client/components/architecture/ArchitectureDiagram.tsx +413 -0
- package/carbon/src/client/components/auth/AuthProvider.tsx +466 -0
- package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +35 -0
- package/carbon/src/client/components/effects/FresnelEdge.tsx +49 -0
- package/carbon/src/client/components/effects/GlowTracker.tsx +46 -0
- package/carbon/src/client/components/effects/SparkBurst.tsx +145 -0
- package/carbon/src/client/components/effects/VGlowEffect.tsx +83 -0
- package/carbon/src/client/components/effects/index.ts +2 -0
- package/carbon/src/client/components/layouts/SidebarLayout.tsx +20 -0
- package/carbon/src/client/components/scroll/ScrollSection.tsx +76 -0
- package/carbon/src/client/components/scroll/ScrollytellingProvider.tsx +81 -0
- package/carbon/src/client/components/scroll/index.ts +2 -0
- package/carbon/src/client/components/ui/accordion.tsx +71 -0
- package/carbon/src/client/components/ui/alert-dialog.tsx +162 -0
- package/carbon/src/client/components/ui/alert.tsx +73 -0
- package/carbon/src/client/components/ui/aspect-ratio.tsx +22 -0
- package/carbon/src/client/components/ui/avatar.tsx +91 -0
- package/carbon/src/client/components/ui/badge.tsx +50 -0
- package/carbon/src/client/components/ui/breadcrumb.tsx +100 -0
- package/carbon/src/client/components/ui/button-group.tsx +78 -0
- package/carbon/src/client/components/ui/button.tsx +120 -0
- package/carbon/src/client/components/ui/calendar.tsx +182 -0
- package/carbon/src/client/components/ui/card.tsx +85 -0
- package/carbon/src/client/components/ui/carousel.tsx +227 -0
- package/carbon/src/client/components/ui/chart.tsx +357 -0
- package/carbon/src/client/components/ui/checkbox.tsx +27 -0
- package/carbon/src/client/components/ui/collapsible.tsx +15 -0
- package/carbon/src/client/components/ui/command.tsx +178 -0
- package/carbon/src/client/components/ui/context-menu.tsx +233 -0
- package/carbon/src/client/components/ui/dialog.tsx +132 -0
- package/carbon/src/client/components/ui/drawer.tsx +118 -0
- package/carbon/src/client/components/ui/dropdown-menu.tsx +242 -0
- package/carbon/src/client/components/ui/empty.tsx +94 -0
- package/carbon/src/client/components/ui/field.tsx +226 -0
- package/carbon/src/client/components/ui/hover-card.tsx +44 -0
- package/carbon/src/client/components/ui/input-group.tsx +146 -0
- package/carbon/src/client/components/ui/input-otp.tsx +83 -0
- package/carbon/src/client/components/ui/input.tsx +20 -0
- package/carbon/src/client/components/ui/item.tsx +188 -0
- package/carbon/src/client/components/ui/kbd.tsx +26 -0
- package/carbon/src/client/components/ui/label.tsx +21 -0
- package/carbon/src/client/components/ui/menubar.tsx +250 -0
- package/carbon/src/client/components/ui/navigation-menu.tsx +155 -0
- package/carbon/src/client/components/ui/pagination.tsx +102 -0
- package/carbon/src/client/components/ui/popover.tsx +75 -0
- package/carbon/src/client/components/ui/progress.tsx +66 -0
- package/carbon/src/client/components/ui/radio-group.tsx +36 -0
- package/carbon/src/client/components/ui/resizable.tsx +46 -0
- package/carbon/src/client/components/ui/scroll-area.tsx +48 -0
- package/carbon/src/client/components/ui/select.tsx +186 -0
- package/carbon/src/client/components/ui/separator.tsx +21 -0
- package/carbon/src/client/components/ui/sheet.tsx +124 -0
- package/carbon/src/client/components/ui/sidebar.tsx +702 -0
- package/carbon/src/client/components/ui/skeleton.tsx +13 -0
- package/carbon/src/client/components/ui/slider.tsx +57 -0
- package/carbon/src/client/components/ui/sonner.tsx +45 -0
- package/carbon/src/client/components/ui/spinner.tsx +15 -0
- package/carbon/src/client/components/ui/switch.tsx +30 -0
- package/carbon/src/client/components/ui/table.tsx +89 -0
- package/carbon/src/client/components/ui/tabs.tsx +73 -0
- package/carbon/src/client/components/ui/textarea.tsx +18 -0
- package/carbon/src/client/components/ui/toggle-group.tsx +87 -0
- package/carbon/src/client/components/ui/toggle.tsx +44 -0
- package/carbon/src/client/components/ui/tooltip.tsx +56 -0
- package/carbon/src/client/hooks/api/index.ts +14 -0
- package/carbon/src/client/hooks/api/useAuthSettings.ts +80 -0
- package/carbon/src/client/hooks/api/useSubscription.ts +87 -0
- package/carbon/src/client/hooks/use-mobile.ts +21 -0
- package/carbon/src/client/hooks/useMousePosition.ts +91 -0
- package/carbon/src/client/hooks/useNotifications.tsx +124 -0
- package/carbon/src/client/hooks/useOrganizationMembers.ts +127 -0
- package/carbon/src/client/hooks/useOrganizations.tsx +230 -0
- package/carbon/src/client/hooks/usePackageManager.ts +69 -0
- package/carbon/src/client/hooks/useReducedMotion.ts +20 -0
- package/carbon/src/client/hooks/useRunningServices.ts +114 -0
- package/carbon/src/client/hooks/useScrollProgress.ts +56 -0
- package/carbon/src/client/index.css +467 -0
- package/carbon/src/client/index.html +56 -0
- package/carbon/src/client/lib/admin-services.ts +151 -0
- package/carbon/src/client/lib/api.ts +32 -0
- package/carbon/src/client/lib/blog.ts +35 -0
- package/carbon/src/client/lib/changelog.ts +33 -0
- package/carbon/src/client/lib/docs-search.ts +101 -0
- package/carbon/src/client/lib/docs.ts +37 -0
- package/carbon/src/client/lib/i18n.ts +32 -0
- package/carbon/src/client/lib/supabase.ts +72 -0
- package/carbon/src/client/lib/tailwind-colors.ts +357 -0
- package/carbon/src/client/lib/theme.ts +117 -0
- package/carbon/src/client/lib/utils.ts +22 -0
- package/carbon/src/client/locales/de.json +529 -0
- package/carbon/src/client/locales/en.json +461 -0
- package/carbon/src/client/locales/es.json +529 -0
- package/carbon/src/client/locales/fr.json +529 -0
- package/carbon/src/client/locales/pt.json +529 -0
- package/carbon/src/client/main.tsx +56 -0
- package/carbon/src/client/mdx.d.ts +13 -0
- package/carbon/src/client/pages/ApiDocs.tsx +76 -0
- package/carbon/src/client/pages/AuthCallback.tsx +34 -0
- package/carbon/src/client/pages/Blog.tsx +167 -0
- package/carbon/src/client/pages/Changelog.tsx +171 -0
- package/carbon/src/client/pages/Charts.tsx +388 -0
- package/carbon/src/client/pages/Checkout.tsx +227 -0
- package/carbon/src/client/pages/Contact.tsx +174 -0
- package/carbon/src/client/pages/Dashboard.tsx +368 -0
- package/carbon/src/client/pages/Docs.tsx +372 -0
- package/carbon/src/client/pages/ForgotPassword.tsx +111 -0
- package/carbon/src/client/pages/Home.tsx +187 -0
- package/carbon/src/client/pages/Legal.tsx +100 -0
- package/carbon/src/client/pages/Login.tsx +408 -0
- package/carbon/src/client/pages/MFAVerify.tsx +156 -0
- package/carbon/src/client/pages/NotFound.tsx +21 -0
- package/carbon/src/client/pages/Onboarding.tsx +246 -0
- package/carbon/src/client/pages/ResetPassword.tsx +200 -0
- package/carbon/src/client/pages/UIComponents.tsx +390 -0
- package/carbon/src/client/pages/admin/ContactSubmissions.tsx +220 -0
- package/carbon/src/client/pages/admin/Dashboard.tsx +24 -0
- package/carbon/src/client/pages/admin/Infrastructure.tsx +257 -0
- package/carbon/src/client/pages/admin/Jobs.tsx +225 -0
- package/carbon/src/client/pages/admin/Logs.tsx +18 -0
- package/carbon/src/client/pages/admin/Newsletter.tsx +300 -0
- package/carbon/src/client/pages/admin/Notifications.tsx +603 -0
- package/carbon/src/client/pages/admin/Organizations.tsx +306 -0
- package/carbon/src/client/pages/admin/Settings.tsx +314 -0
- package/carbon/src/client/pages/admin/Theme.tsx +465 -0
- package/carbon/src/client/pages/admin/Users.tsx +365 -0
- package/carbon/src/client/pages/api-docs.css +156 -0
- package/carbon/src/client/pages/organizations/Details.tsx +200 -0
- package/carbon/src/client/pages/organizations/Members.tsx +402 -0
- package/carbon/src/client/pages/settings/Billing.tsx +473 -0
- package/carbon/src/client/pages/settings/Profile.tsx +160 -0
- package/carbon/src/client/pages/settings/Security.tsx +341 -0
- package/carbon/src/client/public/favicon.svg +19 -0
- package/carbon/src/client/public/robots.txt +8 -0
- package/carbon/src/server/billing/index.ts +104 -0
- package/carbon/src/server/billing/provider.ts +81 -0
- package/carbon/src/server/billing/providers/paddle.ts +314 -0
- package/carbon/src/server/billing/providers/polar.ts +325 -0
- package/carbon/src/server/billing/providers/stripe.ts +233 -0
- package/carbon/src/server/emails/templates.ts +116 -0
- package/carbon/src/server/index.ts +554 -0
- package/carbon/src/server/lib/auth.ts +6 -0
- package/carbon/src/server/lib/email.ts +64 -0
- package/carbon/src/server/lib/env.ts +112 -0
- package/carbon/src/server/lib/errors.ts +21 -0
- package/carbon/src/server/lib/logger.ts +17 -0
- package/carbon/src/server/lib/rate-limiter.ts +288 -0
- package/carbon/src/server/lib/request.ts +34 -0
- package/carbon/src/server/lib/stripe.ts +42 -0
- package/carbon/src/server/lib/supabase.ts +65 -0
- package/carbon/src/server/middleware/requirePlan.ts +80 -0
- package/carbon/src/server/routes/_internal/services-status.ts +958 -0
- package/carbon/src/server/routes/_internal/verify-role.ts +185 -0
- package/carbon/src/server/routes/health.ts +48 -0
- package/carbon/src/server/routes/v1/admin/contact.ts +128 -0
- package/carbon/src/server/routes/v1/admin/jobs.ts +171 -0
- package/carbon/src/server/routes/v1/admin/newsletter.ts +237 -0
- package/carbon/src/server/routes/v1/auth.ts +390 -0
- package/carbon/src/server/routes/v1/billing.ts +718 -0
- package/carbon/src/server/routes/v1/contact.ts +93 -0
- package/carbon/src/server/routes/v1/index.ts +1333 -0
- package/carbon/src/server/routes/v1/newsletter.ts +181 -0
- package/carbon/src/server/routes/v1/performance.ts +157 -0
- package/carbon/src/server/routes/v1/stats.ts +170 -0
- package/carbon/src/server/routes/v1/theme.ts +106 -0
- package/carbon/src/server/routes/webhooks/billing.ts +376 -0
- package/carbon/src/server/routes/webhooks/stripe.ts +276 -0
- package/carbon/src/server/types.ts +11 -0
- package/carbon/src/shared/pricing.ts +155 -0
- package/carbon/src/shared/types.ts +338 -0
- package/carbon/supabase/migrations/00001_init.sql +717 -0
- package/carbon/supabase/migrations/00002_theme_settings.sql +13 -0
- package/carbon/supabase/migrations/00003_pg_cron.sql +121 -0
- package/carbon/supabase/migrations/00004_contact_newsletter.sql +81 -0
- package/carbon/supabase/migrations/00005_localization_languages.sql +22 -0
- package/carbon/supabase/seed.sql +16 -0
- package/carbon/tests/_helpers/app.ts +45 -0
- package/carbon/tests/_helpers/env.ts +37 -0
- package/carbon/tests/_helpers/factories.ts +69 -0
- package/carbon/tests/_helpers/jwt.ts +23 -0
- package/carbon/tests/_helpers/setup-integration.ts +20 -0
- package/carbon/tests/_helpers/setup-rtl.ts +12 -0
- package/carbon/tests/component/ErrorBoundary.test.tsx +53 -0
- package/carbon/tests/component/use-auth-settings.test.tsx +119 -0
- package/carbon/tests/integration/server/routes/contact.test.ts +162 -0
- package/carbon/tests/integration/server/routes/health.test.ts +61 -0
- package/carbon/tests/structural/i18n-parity.test.ts +42 -0
- package/carbon/tests/unit/client/utils.test.ts +49 -0
- package/carbon/tests/unit/shared/pricing.test.ts +93 -0
- package/carbon/tsconfig.json +27 -0
- package/carbon/tsconfig.server.json +27 -0
- package/carbon/tsconfig.test.json +9 -0
- package/carbon/vite.config.ts +110 -0
- package/carbon/vitest.config.ts +74 -0
- package/carbon/volumes/db/jwt.sql +57 -0
- package/carbon/volumes/db/metabase-init.sh +29 -0
- package/carbon/volumes/db/n8n-init.sh +25 -0
- package/carbon/volumes/db/realtime.sql +33 -0
- package/carbon/volumes/db/roles.sql +93 -0
- package/carbon/volumes/db/set-passwords.sh +12 -0
- package/carbon/volumes/db/super-admin.dev.sql +113 -0
- package/carbon/volumes/db/super-admin.generated.sql +113 -0
- package/carbon/volumes/db/super-admin.sql +114 -0
- package/carbon/volumes/grafana/dashboards/logs.json +179 -0
- package/carbon/volumes/grafana/dashboards/overview.json +523 -0
- package/carbon/volumes/grafana/dashboards/postgresql.json +337 -0
- package/carbon/volumes/grafana/dashboards.dev/logs.json +179 -0
- package/carbon/volumes/grafana/dashboards.dev/overview.json +156 -0
- package/carbon/volumes/grafana/dashboards.dev/postgresql.json +337 -0
- package/carbon/volumes/grafana/provisioning/dashboards/dashboards.dev.yml +16 -0
- package/carbon/volumes/grafana/provisioning/dashboards/dashboards.yml +16 -0
- package/carbon/volumes/grafana/provisioning/datasources/datasources.yml +43 -0
- package/carbon/volumes/kong/docker-entrypoint.sh +9 -0
- package/carbon/volumes/kong/kong.yml +208 -0
- package/carbon/volumes/loki/loki-config.yml +58 -0
- package/carbon/volumes/n8n/hooks.js +131 -0
- package/carbon/volumes/n8n/scripts/setup.sh +66 -0
- package/carbon/volumes/prometheus/prometheus.yml +43 -0
- package/carbon/volumes/promtail/promtail-config.yml +64 -0
- package/carbon/volumes/traefik/middlewares.dev.yml +83 -0
- package/carbon/volumes/traefik/middlewares.yml +95 -0
- package/carbon/volumes/traefik/vite-dev.yml +20 -0
- package/package.json +95 -0
- package/src/access.js +354 -0
- package/src/activate.js +187 -0
- package/src/add.js +718 -0
- package/src/backup.js +786 -0
- package/src/cli.js +350 -0
- package/src/configure.js +967 -0
- package/src/console.js +155 -0
- package/src/create.js +1499 -0
- package/src/deploy.js +311 -0
- package/src/destroy.js +2033 -0
- package/src/diagnose.js +735 -0
- package/src/down.js +80 -0
- package/src/failover.js +1032 -0
- package/src/lib/backup-s3.js +179 -0
- package/src/lib/build.js +33 -0
- package/src/lib/checksum.js +28 -0
- package/src/lib/ci-setup.js +666 -0
- package/src/lib/cli/help.js +129 -0
- package/src/lib/cli/parse-flags.js +160 -0
- package/src/lib/cli/select-action.js +65 -0
- package/src/lib/cli/select-environment.js +108 -0
- package/src/lib/cli/tty-guard.js +75 -0
- package/src/lib/cloudflare.js +447 -0
- package/src/lib/colors.js +52 -0
- package/src/lib/command.js +361 -0
- package/src/lib/config.js +359 -0
- package/src/lib/cost.js +103 -0
- package/src/lib/deploy/bundle.js +231 -0
- package/src/lib/deploy/compose/acme-verify.js +121 -0
- package/src/lib/deploy/compose/build-args.js +78 -0
- package/src/lib/deploy/compose/ha.js +1874 -0
- package/src/lib/deploy/compose/index.js +1294 -0
- package/src/lib/deploy/compose/reconcile.js +74 -0
- package/src/lib/deploy/github.js +382 -0
- package/src/lib/deploy/image.js +191 -0
- package/src/lib/deploy/index.js +119 -0
- package/src/lib/deploy/k8s/LICENSE +99 -0
- package/src/lib/deploy/k8s/gitops-deploy.js +298 -0
- package/src/lib/deploy/k8s/ha/index.js +1000 -0
- package/src/lib/deploy/k8s/index.js +62 -0
- package/src/lib/deploy/k8s/k3s.js +2515 -0
- package/src/lib/deploy/orchestrator.js +1401 -0
- package/src/lib/deploy/prompts.js +545 -0
- package/src/lib/deploy/remote-build.js +130 -0
- package/src/lib/deploy/state.js +120 -0
- package/src/lib/deploy/utils.js +328 -0
- package/src/lib/deploy-logger.js +93 -0
- package/src/lib/dns-propagation.js +48 -0
- package/src/lib/environment.js +58 -0
- package/src/lib/fetch-retry.js +103 -0
- package/src/lib/github-environments.js +335 -0
- package/src/lib/hetzner-dns.js +377 -0
- package/src/lib/hetzner-guided-setup.js +275 -0
- package/src/lib/host-keys.js +37 -0
- package/src/lib/iac/cloud-init.js +52 -0
- package/src/lib/iac/index.js +325 -0
- package/src/lib/iac/programs/hetzner-compose.js +130 -0
- package/src/lib/iac/programs/hetzner-k8s.js +320 -0
- package/src/lib/kubectl.js +81 -0
- package/src/lib/licensing/index.js +259 -0
- package/src/lib/licensing/tiers.js +181 -0
- package/src/lib/licensing/validator.js +171 -0
- package/src/lib/merge-package-json.js +90 -0
- package/src/lib/operator-ip.js +381 -0
- package/src/lib/package-manager.js +111 -0
- package/src/lib/perf.js +71 -0
- package/src/lib/project-guard.js +39 -0
- package/src/lib/project.js +334 -0
- package/src/lib/providers/base.js +276 -0
- package/src/lib/providers/hetzner-s3.js +656 -0
- package/src/lib/providers/hetzner.js +755 -0
- package/src/lib/providers/index.js +164 -0
- package/src/lib/s3.js +510 -0
- package/src/lib/secret-scan.js +583 -0
- package/src/lib/secrets.js +63 -0
- package/src/lib/server-types.js +195 -0
- package/src/lib/shell.js +91 -0
- package/src/lib/ssh.js +241 -0
- package/src/lib/tracker.js +242 -0
- package/src/lib/upgrade-policy.js +170 -0
- package/src/lib/validators.js +105 -0
- package/src/lib/version.js +5 -0
- package/src/remove.js +292 -0
- package/src/reset.js +97 -0
- package/src/restore.js +871 -0
- package/src/scale.js +1734 -0
- package/src/shell.js +222 -0
- package/src/status.js +981 -0
- package/src/up.js +264 -0
- package/src/upgrade.js +721 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
# Vibecarbon Docker Compose
|
|
2
|
+
# Supabase self-hosted + Hono API + Traefik reverse proxy
|
|
3
|
+
|
|
4
|
+
services:
|
|
5
|
+
# ===========================================
|
|
6
|
+
# REVERSE PROXY
|
|
7
|
+
# ===========================================
|
|
8
|
+
traefik:
|
|
9
|
+
image: traefik:v3.6.11
|
|
10
|
+
container_name: ${PROJECT_NAME}-traefik
|
|
11
|
+
restart: unless-stopped
|
|
12
|
+
security_opt:
|
|
13
|
+
- no-new-privileges:true
|
|
14
|
+
cap_drop:
|
|
15
|
+
- ALL
|
|
16
|
+
cap_add:
|
|
17
|
+
- NET_BIND_SERVICE
|
|
18
|
+
read_only: true
|
|
19
|
+
tmpfs:
|
|
20
|
+
- /tmp
|
|
21
|
+
command:
|
|
22
|
+
- "--api.dashboard=true"
|
|
23
|
+
- "--api.insecure=true"
|
|
24
|
+
- "--providers.docker=true"
|
|
25
|
+
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
|
|
26
|
+
- "--providers.docker.exposedbydefault=false"
|
|
27
|
+
- "--providers.file.directory=/etc/traefik/dynamic"
|
|
28
|
+
- "--entrypoints.web.address=:80"
|
|
29
|
+
- "--log.level=INFO"
|
|
30
|
+
ports:
|
|
31
|
+
- "${DEV_TRAEFIK_PORT:-80}:80"
|
|
32
|
+
volumes:
|
|
33
|
+
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
34
|
+
- ./volumes/traefik:/etc/traefik/dynamic:ro
|
|
35
|
+
networks:
|
|
36
|
+
- vibecarbon-network
|
|
37
|
+
labels:
|
|
38
|
+
- "traefik.enable=true"
|
|
39
|
+
# Subdomain routing - auth added via prod overlay (docker-compose.prod.yml)
|
|
40
|
+
- "traefik.http.routers.dashboard.rule=Host(`traefik.${DOMAIN:-localhost}`)"
|
|
41
|
+
- "traefik.http.routers.dashboard.entrypoints=web"
|
|
42
|
+
- "traefik.http.routers.dashboard.service=api@internal"
|
|
43
|
+
|
|
44
|
+
# ===========================================
|
|
45
|
+
# APPLICATION
|
|
46
|
+
# ===========================================
|
|
47
|
+
app:
|
|
48
|
+
build:
|
|
49
|
+
context: .
|
|
50
|
+
args:
|
|
51
|
+
VITE_PROJECT_NAME: ${PROJECT_NAME}
|
|
52
|
+
VITE_SUPABASE_URL: ${SITE_URL}
|
|
53
|
+
VITE_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
|
54
|
+
VITE_PLAUSIBLE_DOMAIN: ${VITE_PLAUSIBLE_DOMAIN:-}
|
|
55
|
+
VITE_PLAUSIBLE_SCRIPT_URL: ${VITE_PLAUSIBLE_SCRIPT_URL:-}
|
|
56
|
+
container_name: ${PROJECT_NAME}-app
|
|
57
|
+
restart: unless-stopped
|
|
58
|
+
security_opt:
|
|
59
|
+
- no-new-privileges:true
|
|
60
|
+
cap_drop:
|
|
61
|
+
- ALL
|
|
62
|
+
read_only: true
|
|
63
|
+
tmpfs:
|
|
64
|
+
- /tmp
|
|
65
|
+
environment:
|
|
66
|
+
- NODE_ENV=production
|
|
67
|
+
- SUPABASE_URL=http://kong:8000
|
|
68
|
+
- SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
|
|
69
|
+
- SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}
|
|
70
|
+
- SITE_URL=${SITE_URL:-http://localhost:5173}
|
|
71
|
+
depends_on:
|
|
72
|
+
# The Hono backend uses the Supabase JS client (calls flow through Kong)
|
|
73
|
+
# and never touches postgres directly, so we don't need `db: healthy`
|
|
74
|
+
# gating app startup. `service_started` on both lets the app boot in
|
|
75
|
+
# parallel with db's ~20-30s initial warmup.
|
|
76
|
+
db:
|
|
77
|
+
condition: service_started
|
|
78
|
+
kong:
|
|
79
|
+
condition: service_started
|
|
80
|
+
networks:
|
|
81
|
+
- vibecarbon-network
|
|
82
|
+
labels:
|
|
83
|
+
- "traefik.enable=true"
|
|
84
|
+
- "traefik.http.routers.app.rule=Host(`localhost`)"
|
|
85
|
+
- "traefik.http.routers.app.entrypoints=web"
|
|
86
|
+
- "traefik.http.services.app.loadbalancer.server.port=3000"
|
|
87
|
+
|
|
88
|
+
# ===========================================
|
|
89
|
+
# SUPABASE CORE SERVICES
|
|
90
|
+
# ===========================================
|
|
91
|
+
|
|
92
|
+
# PostgreSQL Database
|
|
93
|
+
db:
|
|
94
|
+
build:
|
|
95
|
+
context: ./db
|
|
96
|
+
image: ${PROJECT_NAME}-db:latest
|
|
97
|
+
# The db image is built locally from ./db (no registry to pull from).
|
|
98
|
+
# Without this, compose's default `pull_policy: missing` first attempts
|
|
99
|
+
# a Docker Hub pull, which 401s with "pull access denied for {image}"
|
|
100
|
+
# before falling back to the local build. Functional but scary noise on
|
|
101
|
+
# every `docker compose up` invocation.
|
|
102
|
+
pull_policy: build
|
|
103
|
+
container_name: ${PROJECT_NAME}-db
|
|
104
|
+
restart: unless-stopped
|
|
105
|
+
security_opt:
|
|
106
|
+
- no-new-privileges:true
|
|
107
|
+
healthcheck:
|
|
108
|
+
test: ["CMD", "pg_isready", "-U", "postgres", "-h", "localhost"]
|
|
109
|
+
interval: 5s
|
|
110
|
+
timeout: 5s
|
|
111
|
+
retries: 10
|
|
112
|
+
# First-time init runs all Supabase migrations + WAL-G init before the
|
|
113
|
+
# server accepts TCP connections (~60-150s under fanout-3 load — observed
|
|
114
|
+
# restore re-deploy failing at deploy.compose.up=160s while the same db
|
|
115
|
+
# eventually became healthy ~3min later, fanout9 e1 2026-05-01). Without
|
|
116
|
+
# start_period, retries × interval = 50s expires before first init
|
|
117
|
+
# completes, compose marks db unhealthy, and dependent services fail
|
|
118
|
+
# with "dependency failed to start". start_period suppresses the
|
|
119
|
+
# unhealthy verdict during initial boot.
|
|
120
|
+
start_period: 180s
|
|
121
|
+
command:
|
|
122
|
+
- postgres
|
|
123
|
+
- -c
|
|
124
|
+
- config_file=/etc/postgresql/postgresql.conf
|
|
125
|
+
- -c
|
|
126
|
+
- log_min_messages=fatal
|
|
127
|
+
- -c
|
|
128
|
+
- wal_level=replica
|
|
129
|
+
- -c
|
|
130
|
+
- archive_mode=on
|
|
131
|
+
- -c
|
|
132
|
+
- archive_command=/usr/local/bin/wal-g wal-push %p
|
|
133
|
+
- -c
|
|
134
|
+
- archive_timeout=60
|
|
135
|
+
- -c
|
|
136
|
+
# hot_standby=on lets a server in recovery mode (i.e. our standby)
|
|
137
|
+
# accept read-only psql connections. Required so failover's
|
|
138
|
+
# pg_promote() call can connect to the standby (psql connections
|
|
139
|
+
# otherwise return "database is not accepting connections / Hot
|
|
140
|
+
# standby mode is disabled" and pg_promote can't be invoked).
|
|
141
|
+
# No-op on the primary — hot_standby only affects standbys. PG15
|
|
142
|
+
# default is on, but supabase/postgres:15.8.1.085 ships with it
|
|
143
|
+
# off, so set it explicitly. RCA: compose-ha 2026-05-01 fanout13
|
|
144
|
+
# failover — pg_promote retried 5 times against an unreachable
|
|
145
|
+
# standby psql before aborting.
|
|
146
|
+
- hot_standby=on
|
|
147
|
+
environment:
|
|
148
|
+
POSTGRES_HOST: /var/run/postgresql
|
|
149
|
+
PGPORT: 5432
|
|
150
|
+
POSTGRES_PORT: 5432
|
|
151
|
+
PGPASSWORD: ${POSTGRES_PASSWORD}
|
|
152
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
153
|
+
PGDATABASE: postgres
|
|
154
|
+
POSTGRES_DB: postgres
|
|
155
|
+
JWT_SECRET: ${JWT_SECRET}
|
|
156
|
+
JWT_EXP: 3600
|
|
157
|
+
# WAL-G Configuration
|
|
158
|
+
# `:-` defaults so local dev (no .env-set S3 creds) doesn't trigger
|
|
159
|
+
# `WARN The "S3_*" variable is not set` for every compose invocation.
|
|
160
|
+
# WAL-G is dormant locally without these anyway; the warns were pure
|
|
161
|
+
# noise.
|
|
162
|
+
WALG_S3_PREFIX: s3://${S3_BACKUP_BUCKET:-${S3_BUCKET:-}}/backups/${PROJECT_NAME}/walg
|
|
163
|
+
AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY:-}
|
|
164
|
+
AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY:-}
|
|
165
|
+
AWS_ENDPOINT: ${S3_ENDPOINT:-}
|
|
166
|
+
AWS_REGION: ${S3_REGION:-us-east-1}
|
|
167
|
+
WALG_COMPRESSION_METHOD: lz4
|
|
168
|
+
volumes:
|
|
169
|
+
- db_data:/var/lib/postgresql/data
|
|
170
|
+
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/98-roles.sql:Z
|
|
171
|
+
- ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
|
|
172
|
+
- ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
|
|
173
|
+
- ./volumes/db/set-passwords.sh:/docker-entrypoint-initdb.d/zz-set-passwords.sh:Z
|
|
174
|
+
- ./supabase/migrations:/migrations:ro
|
|
175
|
+
networks:
|
|
176
|
+
- vibecarbon-network
|
|
177
|
+
|
|
178
|
+
# Kong API Gateway
|
|
179
|
+
kong:
|
|
180
|
+
image: kong:3.9.1
|
|
181
|
+
container_name: ${PROJECT_NAME}-kong
|
|
182
|
+
restart: unless-stopped
|
|
183
|
+
security_opt:
|
|
184
|
+
- no-new-privileges:true
|
|
185
|
+
cap_drop:
|
|
186
|
+
- ALL
|
|
187
|
+
cap_add:
|
|
188
|
+
- SETGID
|
|
189
|
+
- SETUID
|
|
190
|
+
read_only: true
|
|
191
|
+
tmpfs:
|
|
192
|
+
- /tmp
|
|
193
|
+
ports:
|
|
194
|
+
- "${DEV_KONG_PORT:-8000}:8000/tcp"
|
|
195
|
+
- "${DEV_KONG_SSL_PORT:-8443}:8443/tcp"
|
|
196
|
+
healthcheck:
|
|
197
|
+
test: ["CMD", "kong", "health"]
|
|
198
|
+
interval: 10s
|
|
199
|
+
timeout: 5s
|
|
200
|
+
retries: 5
|
|
201
|
+
entrypoint: /home/kong/docker-entrypoint.sh
|
|
202
|
+
environment:
|
|
203
|
+
KONG_DATABASE: "off"
|
|
204
|
+
KONG_PREFIX: /tmp/kong
|
|
205
|
+
KONG_DECLARATIVE_CONFIG: /tmp/kong.yml
|
|
206
|
+
KONG_DNS_ORDER: LAST,A,CNAME
|
|
207
|
+
KONG_PLUGINS: request-transformer,cors,key-auth,acl
|
|
208
|
+
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
|
|
209
|
+
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
|
|
210
|
+
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
|
211
|
+
SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
|
212
|
+
volumes:
|
|
213
|
+
- ./volumes/kong/kong.yml:/home/kong/kong.yml.template:ro
|
|
214
|
+
- ./volumes/kong/docker-entrypoint.sh:/home/kong/docker-entrypoint.sh:ro
|
|
215
|
+
# Kong runs in DB-less mode (KONG_DATABASE: "off") — config comes from
|
|
216
|
+
# the declarative kong.yml volume. No postgres dependency at all, so
|
|
217
|
+
# we intentionally don't list `db` here. Kong can boot in parallel
|
|
218
|
+
# with the database, shaving ~20s off the compose cold start.
|
|
219
|
+
networks:
|
|
220
|
+
- vibecarbon-network
|
|
221
|
+
|
|
222
|
+
# GoTrue Authentication
|
|
223
|
+
auth:
|
|
224
|
+
image: supabase/gotrue:v2.186.0
|
|
225
|
+
container_name: ${PROJECT_NAME}-auth
|
|
226
|
+
restart: unless-stopped
|
|
227
|
+
security_opt:
|
|
228
|
+
- no-new-privileges:true
|
|
229
|
+
cap_drop:
|
|
230
|
+
- ALL
|
|
231
|
+
read_only: true
|
|
232
|
+
tmpfs:
|
|
233
|
+
- /tmp
|
|
234
|
+
healthcheck:
|
|
235
|
+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9999/health"]
|
|
236
|
+
interval: 5s
|
|
237
|
+
timeout: 5s
|
|
238
|
+
retries: 3
|
|
239
|
+
environment:
|
|
240
|
+
GOTRUE_API_HOST: 0.0.0.0
|
|
241
|
+
GOTRUE_API_PORT: 9999
|
|
242
|
+
API_EXTERNAL_URL: ${SITE_URL}
|
|
243
|
+
GOTRUE_DB_DRIVER: postgres
|
|
244
|
+
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
|
|
245
|
+
GOTRUE_SITE_URL: ${SITE_URL}
|
|
246
|
+
GOTRUE_URI_ALLOW_LIST: "${SITE_URL}/**,http://localhost:${DEV_VITE_PORT:-5173}/**,http://localhost:${DEV_API_PORT:-3000}/**"
|
|
247
|
+
GOTRUE_DISABLE_SIGNUP: "false"
|
|
248
|
+
GOTRUE_JWT_ADMIN_ROLES: service_role
|
|
249
|
+
GOTRUE_JWT_AUD: authenticated
|
|
250
|
+
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
|
|
251
|
+
GOTRUE_JWT_EXP: 3600
|
|
252
|
+
GOTRUE_JWT_SECRET: ${JWT_SECRET}
|
|
253
|
+
GOTRUE_EXTERNAL_EMAIL_ENABLED: "true"
|
|
254
|
+
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: "false"
|
|
255
|
+
GOTRUE_MAILER_AUTOCONFIRM: "true"
|
|
256
|
+
# MFA/TOTP
|
|
257
|
+
GOTRUE_MFA_ENABLED: "true"
|
|
258
|
+
# OAuth Providers
|
|
259
|
+
GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${GOOGLE_ENABLED:-false}
|
|
260
|
+
GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
|
|
261
|
+
GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_CLIENT_SECRET:-}
|
|
262
|
+
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${SITE_URL}/auth/v1/callback
|
|
263
|
+
GOTRUE_EXTERNAL_AZURE_ENABLED: ${MICROSOFT_ENABLED:-false}
|
|
264
|
+
GOTRUE_EXTERNAL_AZURE_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-}
|
|
265
|
+
GOTRUE_EXTERNAL_AZURE_SECRET: ${MICROSOFT_CLIENT_SECRET:-}
|
|
266
|
+
GOTRUE_EXTERNAL_AZURE_URL: https://login.microsoftonline.com/${MICROSOFT_TENANT_ID:-common}
|
|
267
|
+
GOTRUE_EXTERNAL_AZURE_REDIRECT_URI: ${SITE_URL}/auth/v1/callback
|
|
268
|
+
# SMTP (optional)
|
|
269
|
+
GOTRUE_SMTP_HOST: ${SMTP_HOST:-}
|
|
270
|
+
GOTRUE_SMTP_PORT: ${SMTP_PORT:-587}
|
|
271
|
+
GOTRUE_SMTP_USER: ${SMTP_USER:-}
|
|
272
|
+
GOTRUE_SMTP_PASS: ${SMTP_PASS:-}
|
|
273
|
+
GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL:-}
|
|
274
|
+
GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME:-}
|
|
275
|
+
GOTRUE_MAILER_URLPATHS_INVITE: /auth/v1/verify
|
|
276
|
+
GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify
|
|
277
|
+
GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify
|
|
278
|
+
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify
|
|
279
|
+
depends_on:
|
|
280
|
+
db:
|
|
281
|
+
condition: service_healthy
|
|
282
|
+
networks:
|
|
283
|
+
- vibecarbon-network
|
|
284
|
+
|
|
285
|
+
# PostgREST API
|
|
286
|
+
rest:
|
|
287
|
+
image: postgrest/postgrest:v14.6
|
|
288
|
+
container_name: ${PROJECT_NAME}-rest
|
|
289
|
+
restart: unless-stopped
|
|
290
|
+
security_opt:
|
|
291
|
+
- no-new-privileges:true
|
|
292
|
+
cap_drop:
|
|
293
|
+
- ALL
|
|
294
|
+
read_only: true
|
|
295
|
+
tmpfs:
|
|
296
|
+
- /tmp
|
|
297
|
+
environment:
|
|
298
|
+
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
|
|
299
|
+
PGRST_DB_SCHEMAS: public,storage,graphql_public
|
|
300
|
+
PGRST_DB_ANON_ROLE: anon
|
|
301
|
+
PGRST_JWT_SECRET: ${JWT_SECRET}
|
|
302
|
+
PGRST_DB_USE_LEGACY_GUCS: "false"
|
|
303
|
+
PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
|
|
304
|
+
PGRST_APP_SETTINGS_JWT_EXP: 3600
|
|
305
|
+
command: postgrest
|
|
306
|
+
depends_on:
|
|
307
|
+
db:
|
|
308
|
+
condition: service_healthy
|
|
309
|
+
networks:
|
|
310
|
+
- vibecarbon-network
|
|
311
|
+
|
|
312
|
+
# Realtime Server
|
|
313
|
+
realtime:
|
|
314
|
+
image: supabase/realtime:v2.76.5
|
|
315
|
+
container_name: ${PROJECT_NAME}-realtime
|
|
316
|
+
restart: unless-stopped
|
|
317
|
+
cap_drop:
|
|
318
|
+
- ALL
|
|
319
|
+
cap_add:
|
|
320
|
+
- SETUID
|
|
321
|
+
- SETGID
|
|
322
|
+
read_only: true
|
|
323
|
+
tmpfs:
|
|
324
|
+
- /tmp
|
|
325
|
+
- /app/rel/realtime/tmp
|
|
326
|
+
- /app/rel/realtime/releases/2.76.5
|
|
327
|
+
healthcheck:
|
|
328
|
+
test: ["CMD-SHELL", "curl -so /dev/null -w '%{http_code}' http://localhost:4000/ | grep -qE '^[0-9]+$'"]
|
|
329
|
+
interval: 10s
|
|
330
|
+
timeout: 5s
|
|
331
|
+
retries: 5
|
|
332
|
+
start_period: 30s
|
|
333
|
+
environment:
|
|
334
|
+
PORT: 4000
|
|
335
|
+
DB_HOST: db
|
|
336
|
+
DB_PORT: 5432
|
|
337
|
+
DB_USER: supabase_admin
|
|
338
|
+
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
|
339
|
+
DB_NAME: postgres
|
|
340
|
+
DB_AFTER_CONNECT_QUERY: "SET search_path TO _realtime"
|
|
341
|
+
DB_ENC_KEY: ${DB_ENC_KEY:-devdbencryptkey1}
|
|
342
|
+
API_JWT_SECRET: ${JWT_SECRET}
|
|
343
|
+
SECRET_KEY_BASE: ${REALTIME_SECRET:-realtimesecret}
|
|
344
|
+
ERL_AFLAGS: -proto_dist inet_tcp
|
|
345
|
+
DNS_NODES: "''"
|
|
346
|
+
RLIMIT_NOFILE: "10000"
|
|
347
|
+
APP_NAME: realtime
|
|
348
|
+
SEED_SELF_HOST: "true"
|
|
349
|
+
FLY_ALLOC_ID: fly123
|
|
350
|
+
FLY_APP_NAME: realtime
|
|
351
|
+
REPLICATION_MODE: RLS
|
|
352
|
+
REPLICATION_POLL_INTERVAL: 100
|
|
353
|
+
SECURE_CHANNELS: "true"
|
|
354
|
+
SLOT_NAME: supabase_realtime_rls
|
|
355
|
+
TEMPORARY_SLOT: "true"
|
|
356
|
+
depends_on:
|
|
357
|
+
db:
|
|
358
|
+
condition: service_healthy
|
|
359
|
+
networks:
|
|
360
|
+
- vibecarbon-network
|
|
361
|
+
|
|
362
|
+
# ===========================================
|
|
363
|
+
# SUPABASE STORAGE
|
|
364
|
+
# ===========================================
|
|
365
|
+
|
|
366
|
+
storage:
|
|
367
|
+
image: supabase/storage-api:v1.44.2
|
|
368
|
+
container_name: ${PROJECT_NAME}-storage
|
|
369
|
+
restart: unless-stopped
|
|
370
|
+
security_opt:
|
|
371
|
+
- no-new-privileges:true
|
|
372
|
+
cap_drop:
|
|
373
|
+
- ALL
|
|
374
|
+
read_only: true
|
|
375
|
+
tmpfs:
|
|
376
|
+
- /tmp
|
|
377
|
+
healthcheck:
|
|
378
|
+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5000/status"]
|
|
379
|
+
interval: 5s
|
|
380
|
+
timeout: 5s
|
|
381
|
+
retries: 3
|
|
382
|
+
environment:
|
|
383
|
+
ANON_KEY: ${SUPABASE_ANON_KEY}
|
|
384
|
+
SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
|
385
|
+
POSTGREST_URL: http://rest:3000
|
|
386
|
+
PGRST_JWT_SECRET: ${JWT_SECRET}
|
|
387
|
+
DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
|
|
388
|
+
FILE_SIZE_LIMIT: 52428800
|
|
389
|
+
STORAGE_BACKEND: s3
|
|
390
|
+
TENANT_ID: stub
|
|
391
|
+
REGION: ${S3_REGION:-stub}
|
|
392
|
+
GLOBAL_S3_BUCKET: ${S3_BUCKET:-stub}
|
|
393
|
+
GLOBAL_S3_ENDPOINT: ${S3_ENDPOINT:-}
|
|
394
|
+
GLOBAL_S3_FORCE_PATH_STYLE: "true"
|
|
395
|
+
AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY:-}
|
|
396
|
+
AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY:-}
|
|
397
|
+
ENABLE_IMAGE_TRANSFORMATION: "true"
|
|
398
|
+
IMGPROXY_URL: http://imgproxy:5001
|
|
399
|
+
VAULT_ENC_KEY: ${VAULT_ENC_KEY:-vaultenckey}
|
|
400
|
+
volumes:
|
|
401
|
+
- storage_data:/var/lib/storage
|
|
402
|
+
depends_on:
|
|
403
|
+
db:
|
|
404
|
+
condition: service_healthy
|
|
405
|
+
rest:
|
|
406
|
+
condition: service_started
|
|
407
|
+
imgproxy:
|
|
408
|
+
condition: service_started
|
|
409
|
+
networks:
|
|
410
|
+
- vibecarbon-network
|
|
411
|
+
|
|
412
|
+
imgproxy:
|
|
413
|
+
image: darthsim/imgproxy:v3.30.1
|
|
414
|
+
container_name: ${PROJECT_NAME}-imgproxy
|
|
415
|
+
restart: unless-stopped
|
|
416
|
+
security_opt:
|
|
417
|
+
- no-new-privileges:true
|
|
418
|
+
cap_drop:
|
|
419
|
+
- ALL
|
|
420
|
+
read_only: true
|
|
421
|
+
tmpfs:
|
|
422
|
+
- /tmp
|
|
423
|
+
healthcheck:
|
|
424
|
+
test: ["CMD", "imgproxy", "health"]
|
|
425
|
+
interval: 5s
|
|
426
|
+
timeout: 5s
|
|
427
|
+
retries: 3
|
|
428
|
+
environment:
|
|
429
|
+
IMGPROXY_BIND: ":5001"
|
|
430
|
+
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
|
|
431
|
+
IMGPROXY_USE_ETAG: "true"
|
|
432
|
+
IMGPROXY_ENABLE_WEBP_DETECTION: "true"
|
|
433
|
+
volumes:
|
|
434
|
+
- storage_data:/var/lib/storage
|
|
435
|
+
networks:
|
|
436
|
+
- vibecarbon-network
|
|
437
|
+
|
|
438
|
+
# ===========================================
|
|
439
|
+
# SUPABASE MANAGEMENT
|
|
440
|
+
# ===========================================
|
|
441
|
+
|
|
442
|
+
meta:
|
|
443
|
+
image: supabase/postgres-meta:v0.95.2
|
|
444
|
+
container_name: ${PROJECT_NAME}-meta
|
|
445
|
+
restart: unless-stopped
|
|
446
|
+
security_opt:
|
|
447
|
+
- no-new-privileges:true
|
|
448
|
+
cap_drop:
|
|
449
|
+
- ALL
|
|
450
|
+
environment:
|
|
451
|
+
PG_META_PORT: 8080
|
|
452
|
+
PG_META_DB_HOST: db
|
|
453
|
+
PG_META_DB_PORT: 5432
|
|
454
|
+
PG_META_DB_NAME: postgres
|
|
455
|
+
PG_META_DB_USER: supabase_admin
|
|
456
|
+
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
|
|
457
|
+
PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY:-pgmetacryptokey}
|
|
458
|
+
depends_on:
|
|
459
|
+
db:
|
|
460
|
+
condition: service_healthy
|
|
461
|
+
networks:
|
|
462
|
+
- vibecarbon-network
|
|
463
|
+
|
|
464
|
+
studio:
|
|
465
|
+
image: supabase/studio:2026.03.16-sha-5528817
|
|
466
|
+
container_name: ${PROJECT_NAME}-studio
|
|
467
|
+
restart: unless-stopped
|
|
468
|
+
security_opt:
|
|
469
|
+
- no-new-privileges:true
|
|
470
|
+
cap_drop:
|
|
471
|
+
- ALL
|
|
472
|
+
healthcheck:
|
|
473
|
+
disable: true
|
|
474
|
+
environment:
|
|
475
|
+
STUDIO_PG_META_URL: http://meta:8080
|
|
476
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
477
|
+
DEFAULT_ORGANIZATION_NAME: ${PROJECT_NAME}
|
|
478
|
+
DEFAULT_PROJECT_NAME: ${PROJECT_NAME}
|
|
479
|
+
SUPABASE_URL: http://kong:8000
|
|
480
|
+
SUPABASE_PUBLIC_URL: ${SITE_URL}
|
|
481
|
+
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
|
482
|
+
SUPABASE_SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
|
|
483
|
+
AUTH_JWT_SECRET: ${JWT_SECRET}
|
|
484
|
+
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY:-logflarekey}
|
|
485
|
+
LOGFLARE_URL: http://analytics:4000
|
|
486
|
+
NEXT_PUBLIC_ENABLE_LOGS: "false"
|
|
487
|
+
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
|
|
488
|
+
labels:
|
|
489
|
+
- "traefik.enable=true"
|
|
490
|
+
# Subdomain routing - auth added via prod overlay (docker-compose.prod.yml)
|
|
491
|
+
- "traefik.http.routers.studio.rule=Host(`studio.${DOMAIN:-localhost}`)"
|
|
492
|
+
- "traefik.http.routers.studio.entrypoints=web"
|
|
493
|
+
- "traefik.http.services.studio.loadbalancer.server.port=3000"
|
|
494
|
+
depends_on:
|
|
495
|
+
db:
|
|
496
|
+
condition: service_healthy
|
|
497
|
+
meta:
|
|
498
|
+
condition: service_started
|
|
499
|
+
networks:
|
|
500
|
+
- vibecarbon-network
|
|
501
|
+
|
|
502
|
+
volumes:
|
|
503
|
+
db_data:
|
|
504
|
+
storage_data:
|
|
505
|
+
|
|
506
|
+
networks:
|
|
507
|
+
vibecarbon-network:
|
|
508
|
+
driver: bridge
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "Starting ${PROJECT_NAME}..."
|
|
5
|
+
|
|
6
|
+
# Note: Database migrations are handled by Supabase
|
|
7
|
+
# Run migrations manually with: docker compose exec db psql -U postgres -d postgres -f /migrations/init.sql
|
|
8
|
+
|
|
9
|
+
echo "Starting application..."
|
|
10
|
+
|
|
11
|
+
# Execute the main command
|
|
12
|
+
exec "$@"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Promote a standby PostgreSQL to primary and scale up services.
|
|
3
|
+
#
|
|
4
|
+
# This script runs ON the standby master node and uses kubectl to:
|
|
5
|
+
# 1. Promote PostgreSQL from standby to primary
|
|
6
|
+
# 2. Wait for the promotion to complete
|
|
7
|
+
# 3. Scale up Supabase and application services
|
|
8
|
+
#
|
|
9
|
+
# Optional env vars:
|
|
10
|
+
# NAMESPACE — Kubernetes namespace (default: vibecarbon)
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
NAMESPACE="${NAMESPACE:-vibecarbon}"
|
|
15
|
+
PGDATA="/var/lib/postgresql/data"
|
|
16
|
+
|
|
17
|
+
# Find the postgres pod
|
|
18
|
+
POD=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/name=supabase-db -o jsonpath='{.items[0].metadata.name}')
|
|
19
|
+
if [ -z "$POD" ]; then
|
|
20
|
+
echo "ERROR: No postgres pod found in namespace $NAMESPACE"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
echo "Promoting standby: pod=$POD"
|
|
25
|
+
|
|
26
|
+
# 1. Promote PostgreSQL
|
|
27
|
+
echo "Promoting PostgreSQL standby to primary..."
|
|
28
|
+
kubectl exec -n "$NAMESPACE" "$POD" -- su postgres -c "pg_ctl promote -D $PGDATA" 2>/dev/null || true
|
|
29
|
+
|
|
30
|
+
# 2. Wait for promotion to complete
|
|
31
|
+
echo "Waiting for promotion..."
|
|
32
|
+
for i in $(seq 1 30); do
|
|
33
|
+
result=$(kubectl exec -n "$NAMESPACE" "$POD" -- psql -U supabase_admin -tAc 'SELECT pg_is_in_recovery()' 2>/dev/null || echo "t")
|
|
34
|
+
if [ "$result" = "f" ]; then
|
|
35
|
+
echo "PostgreSQL promoted successfully"
|
|
36
|
+
break
|
|
37
|
+
fi
|
|
38
|
+
if [ "$i" = "30" ]; then
|
|
39
|
+
echo "WARNING: Promotion not confirmed after 60 seconds"
|
|
40
|
+
fi
|
|
41
|
+
sleep 2
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
# 3. Scale up services
|
|
45
|
+
echo "Scaling up services..."
|
|
46
|
+
kubectl scale deployment app -n "$NAMESPACE" --replicas=2
|
|
47
|
+
kubectl scale deployment auth -n "$NAMESPACE" --replicas=2
|
|
48
|
+
kubectl scale deployment rest -n "$NAMESPACE" --replicas=2
|
|
49
|
+
kubectl scale deployment realtime -n "$NAMESPACE" --replicas=2
|
|
50
|
+
kubectl scale deployment storage -n "$NAMESPACE" --replicas=1
|
|
51
|
+
|
|
52
|
+
echo "Standby activation complete"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Configure primary PostgreSQL for streaming replication.
|
|
2
|
+
-- Run on the primary database before initializing the standby.
|
|
3
|
+
--
|
|
4
|
+
-- This script is idempotent: safe to run multiple times.
|
|
5
|
+
|
|
6
|
+
-- Create dedicated replication role (if not exists), or update its password on re-runs.
|
|
7
|
+
DO $$
|
|
8
|
+
BEGIN
|
|
9
|
+
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'replicator') THEN
|
|
10
|
+
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'repl_password';
|
|
11
|
+
ELSE
|
|
12
|
+
ALTER ROLE replicator WITH PASSWORD 'repl_password';
|
|
13
|
+
END IF;
|
|
14
|
+
END $$;
|
|
15
|
+
|
|
16
|
+
-- Enable WAL-level replication
|
|
17
|
+
ALTER SYSTEM SET wal_level = 'replica';
|
|
18
|
+
ALTER SYSTEM SET max_wal_senders = 5;
|
|
19
|
+
ALTER SYSTEM SET wal_keep_size = '512MB';
|
|
20
|
+
|
|
21
|
+
-- Create a physical replication slot for the standby. Without a slot, the
|
|
22
|
+
-- standby's follow position isn't persisted on primary; if it falls behind
|
|
23
|
+
-- by more than wal_keep_size (512MB), primary recycles WAL and streaming
|
|
24
|
+
-- breaks silently. The slot tells primary to hold WAL until the standby
|
|
25
|
+
-- confirms receipt. Idempotent: wrapped in a DO block that checks existence.
|
|
26
|
+
DO $$
|
|
27
|
+
BEGIN
|
|
28
|
+
IF NOT EXISTS (
|
|
29
|
+
SELECT 1 FROM pg_replication_slots WHERE slot_name = 'vibecarbon_standby_slot'
|
|
30
|
+
) THEN
|
|
31
|
+
PERFORM pg_create_physical_replication_slot('vibecarbon_standby_slot');
|
|
32
|
+
END IF;
|
|
33
|
+
END $$;
|
|
34
|
+
|
|
35
|
+
-- Reload config (wal_level change requires restart, handled by caller)
|
|
36
|
+
SELECT pg_reload_conf();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Initialize standby PostgreSQL as a streaming replica of the primary.
|
|
3
|
+
#
|
|
4
|
+
# This script runs ON the standby master node and uses kubectl to
|
|
5
|
+
# exec into the postgres pod. It:
|
|
6
|
+
# 1. Stops the standby database
|
|
7
|
+
# 2. Wipes data and runs pg_basebackup from the primary
|
|
8
|
+
# 3. Configures standby.signal and primary_conninfo
|
|
9
|
+
# 4. Restarts as a hot standby
|
|
10
|
+
#
|
|
11
|
+
# Required env vars:
|
|
12
|
+
# PRIMARY_HOST — IP address of the primary cluster (master node)
|
|
13
|
+
#
|
|
14
|
+
# Optional env vars:
|
|
15
|
+
# PRIMARY_PORT — PostgreSQL port on the primary (default: 5432, use 30432 for NodePort)
|
|
16
|
+
# REPL_PASSWORD — Replication user password (required; generated by vibecarbon create in .env.local)
|
|
17
|
+
# NAMESPACE — Kubernetes namespace (default: vibecarbon)
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
: "${PRIMARY_HOST:?PRIMARY_HOST is required}"
|
|
22
|
+
PRIMARY_PORT="${PRIMARY_PORT:-5432}"
|
|
23
|
+
: "${REPL_PASSWORD:?REPL_PASSWORD is required — generate one in .env.local and export it before running standby-init.sh}"
|
|
24
|
+
NAMESPACE="${NAMESPACE:-vibecarbon}"
|
|
25
|
+
PGDATA="/var/lib/postgresql/data"
|
|
26
|
+
|
|
27
|
+
# Find the postgres pod
|
|
28
|
+
POD=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/name=supabase-db -o jsonpath='{.items[0].metadata.name}')
|
|
29
|
+
if [ -z "$POD" ]; then
|
|
30
|
+
echo "ERROR: No postgres pod found in namespace $NAMESPACE"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "Standby init: pod=$POD, primary=$PRIMARY_HOST:$PRIMARY_PORT"
|
|
35
|
+
|
|
36
|
+
# 1. Stop postgres on standby
|
|
37
|
+
echo "Stopping standby PostgreSQL..."
|
|
38
|
+
kubectl exec -n "$NAMESPACE" "$POD" -- su postgres -c "pg_ctl stop -D $PGDATA -m fast" 2>/dev/null || true
|
|
39
|
+
sleep 5
|
|
40
|
+
|
|
41
|
+
# 2. Wipe data and run pg_basebackup from primary
|
|
42
|
+
echo "Running pg_basebackup from primary ($PRIMARY_HOST:$PRIMARY_PORT)..."
|
|
43
|
+
kubectl exec -n "$NAMESPACE" "$POD" -- bash -c "
|
|
44
|
+
rm -rf ${PGDATA}/*
|
|
45
|
+
PGPASSWORD='${REPL_PASSWORD}' pg_basebackup -h ${PRIMARY_HOST} -p ${PRIMARY_PORT} -U replicator -D ${PGDATA} -Fp -Xs -P -R
|
|
46
|
+
"
|
|
47
|
+
|
|
48
|
+
# 3. Configure standby signal and primary connection info
|
|
49
|
+
echo "Configuring standby.signal and primary_conninfo..."
|
|
50
|
+
kubectl exec -n "$NAMESPACE" "$POD" -- bash -c "
|
|
51
|
+
touch ${PGDATA}/standby.signal
|
|
52
|
+
echo \"primary_conninfo = 'host=${PRIMARY_HOST} port=${PRIMARY_PORT} user=replicator password=${REPL_PASSWORD}'\" >> ${PGDATA}/postgresql.auto.conf
|
|
53
|
+
chown -R postgres:postgres ${PGDATA}
|
|
54
|
+
"
|
|
55
|
+
|
|
56
|
+
# 4. Start postgres as hot standby
|
|
57
|
+
echo "Starting standby PostgreSQL..."
|
|
58
|
+
kubectl exec -n "$NAMESPACE" "$POD" -- su postgres -c "pg_ctl start -D $PGDATA -l /var/log/postgresql/postgresql.log"
|
|
59
|
+
|
|
60
|
+
# 5. Verify recovery mode
|
|
61
|
+
echo "Verifying standby is in recovery mode..."
|
|
62
|
+
sleep 5
|
|
63
|
+
for i in $(seq 1 15); do
|
|
64
|
+
result=$(kubectl exec -n "$NAMESPACE" "$POD" -- psql -U supabase_admin -tAc 'SELECT pg_is_in_recovery()' 2>/dev/null || echo "")
|
|
65
|
+
if [ "$result" = "t" ]; then
|
|
66
|
+
echo "Standby is in recovery mode — streaming replication active"
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
echo " Waiting for standby to enter recovery mode (attempt $i/15)..."
|
|
70
|
+
sleep 2
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
echo "WARNING: Could not confirm standby is in recovery mode"
|
|
74
|
+
exit 1
|