keyhan 1572b3ce66
Build and Deploy Platform / build-and-deploy (push) Failing after 50m25s
fix(chart): support existingSecret and provide CLUSTER_KUBECONFIG_KEY to backend
Backend now fails production validation without CLUSTER_KUBECONFIG_KEY.
Add cluster-kubeconfig-key to the chart secret and env, plus
secrets.existingSecret so GitOps deployments can use a pre-created
(sealed) Secret instead of the lookup/randAlphaNum template that churns
under Argo CD's helm template rendering.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 16:39:06 +03:30

☁️ CloudHost — Self-Service PaaS Platform

A self-service Platform-as-a-Service (PaaS) that lets developers deploy applications onto Kubernetes with zero DevOps overhead. Source code is turned into a container image inside the cluster with Kaniko (no Docker daemon), then rolled out with Helm — complete with managed databases, wallet-based billing, automated lifecycle management, live logs, and a bilingual (Persian/English) panel.

Supported runtimes — each built from a platform-maintained Dockerfile template: Node.js, Laravel, Go, PHP, Python, Django, .NET, and WordPress (official image + custom wp-content entrypoint).

🇮🇷 Production deployment on the abrban.com k3s cluster — including all the Iran-network workarounds — is documented step-by-step in RUNBOOK.fa.md (Persian).


Architecture Overview

┌──────────────┐   REST    ┌──────────────────┐   K8s API   ┌──────────────┐
│  Next.js 16   │ /api/v1   │   NestJS 11 API   │  + Helm     │  Kubernetes  │
│  Frontend     │◄─────────►│   Backend         │◄───────────►│  Cluster(s)  │
└──────────────┘           └────────┬─────────┘             └──────┬───────┘
                                    │                                │ build Jobs
                  ┌─────────────────┼─────────────────┐             ▼
                  ▼                 ▼                 ▼         ┌──────────┐
              PostgreSQL          Redis           Registry     │  Kaniko  │
                16            (cache + Bull)        (:2)        └──────────┘
Layer Technology
Frontend Next.js 16 (App Router, SSR), React 19, Tailwind CSS v4, React Query, Zustand
Backend API NestJS 11, TypeORM, Passport JWT, Bull (Redis)
Build engine Kaniko (daemon-less in-cluster builds) with platform-generated per-runtime Dockerfiles
Source ingestion Uploaded archive (zip/tar.gz) streamed into a build PVC, or git clone
Deployment Helm v3 charts, @kubernetes/client-node
Database PostgreSQL 16 (control plane); per-app MySQL/MariaDB/PostgreSQL/MongoDB
Queue / cache Redis 7 + Bull (service-access grants, app migrations)
Registry In-cluster registry:2
Auth Mobile number + OTP (SMS) and password, JWT access/refresh

📖 See ARCHITECTURE.md for detailed system design. 🔼 See UPGRADE.en.md (فارسی) for dependency-upgrade notes. 📋 See RUNBOOK.en.md (فارسی) for operations.


Features

For Developers

  • 🚀 Deploy from a code archive (zip/tar.gz) or a git URL (public or private via token)
  • 🟢 Multi-runtime — Node.js, Laravel, Go, PHP, Python, Django, .NET, each built from a maintained Dockerfile template
  • 🔵 WordPress — official image + custom entrypoint that merges your wp-content
  • 🗄️ Managed databases & services — PostgreSQL, MySQL, MariaDB, MongoDB, Redis, RabbitMQ provisioned via Helm
  • 💰 Wallet system — deposit funds, pay per plan (hourly / monthly / yearly), coupons & discounts
  • 📊 Live build & runtime logs (Elasticsearch-backed) + deployment history with rollback
  • 🔒 Environment variables stored as Kubernetes Secrets
  • ⚙️ Resource controls — CPU, memory, replicas, expandable disk
  • 🌐 Custom domains with automatic TLS
  • 📸 Snapshots — backup & restore application state
  • 🎫 Support tickets with technical/sales departments

For Super Admins

  • 🖥️ Multi-cluster management — register/remove Kubernetes clusters (kubeconfig stored encrypted)
  • 👥 User management — activate, deactivate, change roles, per-user detail dashboard
  • 📈 Quotas & pricing — per-cluster limits and a configurable pricing catalog
  • 💳 Billing oversight — transactions, invoices, wallet deposits, global discount
  • ⏱️ Lifecycle settings — grace periods per billing cycle
  • 🔐 RBAC — role-based guards on every endpoint (user / admin / technical / sales)

How the Build & Deploy Pipeline Works

When a user triggers a deploy, the backend runs the build-and-deploy pipeline and creates the build as a Kubernetes Job in the cloudhost-builds namespace:

1. SOURCE
   ├─ uploaded archive → saved to disk (UPLOAD_DIR) → streamed into a per-build PVC
   │   via a short-lived helper pod + `kubectl cp`, then unpacked   (init: prepare source)
   └─ git URL → cloned in-pod; private repos inject the token into the clone URL  (init: git-clone)

2. DOCKERFILE
   The platform detects the runtime (or uses the app's selected runtime) and generates a
   Dockerfile for it — Node.js, Laravel, WordPress, Go, PHP, Python, Django, or .NET.

3. BUILD  (container: kaniko)
   Kaniko builds the image (layer cache per user) and pushes it to the in-cluster
   registry — no Docker daemon, no privileged pod.

4. DEPLOY
   Helm installs/upgrades the `cloudhost-app` chart → Deployment, Service, Ingress,
   per-app DB/Redis/RabbitMQ, PVCs, Secrets, log shipper. App goes live at its subdomain.

The build runs inline within the deploy request and its progress/logs are tracked in memory, then polled by the frontend. (Bull/Redis queues are used elsewhere — service-access grants and application migrations — but not for image builds.)


Project Structure

cloud-host/
├── README.md                 # This file
├── ARCHITECTURE.md           # Detailed system design
├── RUNBOOK.fa.md             # Persian runbook: local dev + abrban/k3s production deploy
├── CHANGELOG.md / CONTRIBUTING.md / UPGRADE.md / UPGRADE.en.md
├── docker-compose.yml        # Local dev stack (Postgres + Redis + API + UI)
│
├── backend/                  # NestJS 11 API (REST under /api/v1)
│   ├── Dockerfile
│   ├── helm/
│   │   ├── cloudhost-platform/   # Helm chart — control plane (API, UI, Postgres, Redis)
│   │   ├── cloudhost-app/        # Helm chart — a single user application + its services
│   │   └── cloudhost-logging/    # Helm chart — Elasticsearch / Kibana / Fluent-bit
│   ├── k8s/                  # Standalone manifests (logging, mail)
│   ├── migrations/           # SQL migrations (applied via one-off Jobs in prod)
│   └── src/
│       ├── main.ts / app.module.ts
│       ├── auth/             # Mobile-OTP + password login, JWT strategies, role guards
│       ├── users/            # User CRUD, profile, phone verification
│       ├── admin/            # Super-admin user-detail dashboard & ops
│       ├── applications/     # App CRUD, code upload (→ disk), git config
│       ├── application-migrations/  # Import/migrate existing apps (Bull queue)
│       ├── deployments/      # Deploy orchestration, history, stop/restart
│       ├── build/            # Kaniko build (build.service): per-runtime Dockerfile generation
│       ├── kubernetes/       # K8s client, Helm wrapper, registry service
│       ├── clusters/         # Multi-cluster management, kubeconfig storage
│       ├── billing/          # Wallet, transactions, invoices, pricing catalog, coupons
│       ├── lifecycle/        # Scanner: auto-suspend/delete expired apps
│       ├── snapshots/        # App snapshot/restore
│       ├── tickets/          # Support tickets
│       ├── notifications/    # User notifications
│       ├── access/           # Time-limited external service access (NodePort grants, Bull queue)
│       ├── common/           # Enums, guards, decorators
│       └── config/           # Env configuration loader + TypeORM config
│
├── frontend/                 # Next.js 16 App Router (bilingual fa-IR / en-US)
│   ├── Dockerfile            # ARG NEXT_PUBLIC_API_URL baked at build time
│   └── src/
│       ├── middleware.ts     # Locale routing + landing (abrban.com) vs panel split
│       ├── app/[lang]/
│       │   ├── page.tsx      # Landing
│       │   ├── login/ register/
│       │   ├── blog/
│       │   └── dashboard/    # apps, deploy, logs, invoices, wallet, services,
│       │       │             #   tickets, account, staff, admin
│       │       └── ...
│       ├── components/  hooks/  lib/ (API client, auth store)  types/
│       └── i18n/             # Dictionaries, provider, language switcher

Quick Start (Local Development)

Prerequisites: Node.js ≥ 20, Docker & Docker Compose, and (for actually building/deploying user apps) a Kubernetes cluster reachable via kubeconfig.

The API and UI run fine locally against Postgres + Redis. The build/deploy pipeline itself runs as Kubernetes Jobs, so triggering a real user-app build requires a cluster (with the in-cluster registry). For pure UI/API development you don't need one.

1. Clone & install

git clone <repo-url> cloud-host && cd cloud-host
cd backend && npm install && cd ..
cd frontend && npm install && cd ..

2. Environment variables

cp backend/.env.example backend/.env
cp frontend/.env.local.example frontend/.env.local
# Edit both — at minimum DB, JWT, Redis. See the Configuration table below.

3. Start Postgres + Redis

docker compose up -d postgres redis

4. Run the apps

# Terminal 1 — Backend (http://localhost:4000, prefix /api/v1, Swagger at /docs)
cd backend && npm run start:dev

# Terminal 2 — Frontend (http://localhost:3000)
cd frontend && npm run dev

In development NODE_ENV=development, so TypeORM synchronize builds the schema automatically and the pricing catalog self-seeds. Set frontend NEXT_PUBLIC_API_URL to the backend URL.

To run the whole stack (API + UI + Postgres + Redis) in containers instead: docker compose up --build (backend on :4000, frontend on :3000).


Deploy on Kubernetes (Helm)

This is the generic path. For the production abrban.com k3s cluster — base-image mirroring, the Iran-network proxy/npmmirror, the wildcard TLS cert, registry bootstrap, and the exact image-build flow — follow RUNBOOK.fa.md.

Prerequisites: a Kubernetes cluster, an Ingress controller (Traefik on k3s by default, or set INGRESS_CLASS=nginx), a default StorageClass for PVCs, and a container registry reachable by the cluster.

1. Build & push the platform images

export REG=your-registry.example.com
docker build -t $REG/cloudhost-backend:1.0.0 ./backend
docker build -t $REG/cloudhost-frontend:1.0.0 \
  --build-arg NEXT_PUBLIC_API_URL=https://api.platform.example.com ./frontend
docker push $REG/cloudhost-backend:1.0.0
docker push $REG/cloudhost-frontend:1.0.0

2. Install the control plane

cp backend/helm/cloudhost-platform/values-production.example.yaml my-values.yaml
# Edit my-values.yaml: image tags, ingress hosts, postgres password, jwtSecret, registry, SMS/OTP

helm upgrade --install cloudhost ./backend/helm/cloudhost-platform \
  -n cloudhost --create-namespace \
  -f my-values.yaml \
  --set images.backend.tag=1.0.0 \
  --set images.frontend.tag=1.0.0

Key values: ingress.enabled, ingress.tls.*, ingress.frontend.host, ingress.api.host, postgres.password, secrets.jwtSecret, migrations.enabled. Chart defaults live in backend/helm/cloudhost-platform/values.yaml; post-install notes via helm get notes cloudhost -n cloudhost.

3. Cluster-side prerequisites for the build pipeline

Ensure the cloudhost-builds namespace has:

  • the in-cluster registry (registry:2) reachable at REGISTRY_URL,
  • a kaniko-builder ServiceAccount with an imagePullSecret for the registry,
  • enough ephemeral storage for the per-build source PVC + helper pod.

4. Verify

kubectl get deploy -n cloudhost            # backend & frontend 1/1
helm status cloudhost -n cloudhost         # STATUS: deployed
curl -s -o /dev/null -w '%{http_code}\n' https://<frontend.host>

Configuration (key env vars)

Variable Description Default
PORT Backend port 4000
DB_HOST / DB_PORT / DB_USERNAME / DB_PASSWORD / DB_DATABASE PostgreSQL connection localhost / 5432 / cloudhost / — / cloudhost
JWT_SECRET / JWT_EXPIRES_IN Access token secret + TTL — / 1h
JWT_REFRESH_SECRET / JWT_REFRESH_EXPIRES_IN Refresh token secret + TTL — / 7d
REDIS_HOST / REDIS_PORT Redis (cache + Bull queues) localhost / 6379
SMS_PROVIDER OTP provider (mizbansms | kavenegar) mizbansms
MIZBANSMS_USERNAME / MIZBANSMS_PASSWORD / MIZBANSMS_FROM OTP SMS credentials (required or OTP send 503s)
REGISTRY_URL / REGISTRY_PULL_URL In-cluster registry (push / pull) registry.cloudhost-builds.svc.cluster.local:5000
BUILD_NAMESPACE / BUILD_SERVICE_ACCOUNT Build Jobs namespace + SA cloudhost-builds / kaniko-builder
KANIKO_IMAGE Kaniko executor image gcr.io/kaniko-project/executor:v1.23.2
UPLOAD_DIR Disk path for uploaded source archives ./uploads
INGRESS_CLASS Ingress controller for app Ingress objects traefik
PLATFORM_DOMAIN / PREVIEW_BASE_DOMAIN Base domain for app subdomains / previews apps.cloudhost.local / —
PLATFORM_STORAGE_CLASS StorageClass for new PVCs (needs volume expansion) cloudhost-expandable
ELASTICSEARCH_HOST / ELASTICSEARCH_PORT Log search backend cluster DNS / 9200
LIFECYCLE_SCAN_INTERVAL_MS Lifecycle scanner tick 60000

Authentication

Login is mobile-number based: the user receives a one-time SMS code (OTP) and can also set a password. On every request JwtStrategy re-reads the user's role and active status from the database (not from the token), so promotions/deactivations take effect immediately. Tokens: JWT access (JWT_EXPIRES_IN, default 1h) + refresh (7d).


API

All endpoints are prefixed with /api/v1. Interactive Swagger docs at http://localhost:4000/api/docs. Major route groups: auth (OTP request/verify, login, refresh), applications, deployments, clusters, billing (wallet, invoices, transactions, pricing), snapshots, tickets, users, admin, notifications.


Security

  • JWT access + refresh tokens; live role/active-status enforcement from DB
  • Bcrypt password hashing
  • Helmet HTTP security headers, class-validator on all DTOs
  • RBAC role-based route guards (@Roles(...))
  • Namespace isolation — each user deploys to their own Kubernetes namespace
  • Secrets — env vars stored as K8s Secrets

License

MIT

S
Description
No description provided
Readme MIT 3.2 MiB
Languages
TypeScript 96.3%
HTML 1.2%
Shell 0.7%
Go Template 0.5%
CSS 0.5%
Other 0.7%