FRONTEND_URL may hold a list of origins (e.g. CORS needs both
panel.abrban.com and abrban.com). The domain resolvers ran new URL()
on the whole string, so new URL('https://a,https://b').hostname became
"a,https" and leaked into ingress hosts, which k8s then rejected with
the generic "HTTP request failed" surfaced in the UI. CORS likewise
never split the list, so the second origin never matched.
Parse only the first URL for domain/preview-root resolution, and split
the list into an array for enableCors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
☁️ CloudHost — Self-Service PaaS Platform
A self-service Platform-as-a-Service (PaaS) that lets developers deploy Node.js, Laravel, and WordPress applications onto Kubernetes with zero DevOps overhead. Includes wallet-based billing, automated lifecycle management, and Helm-based deployments.
Architecture Overview
┌─────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Next.js 16 │ REST │ NestJS API │ K8s │ Kubernetes │
│ Frontend │◄───────►│ Backend │◄──────►│ Cluster(s) │
└─────────────┘ └────────┬────────┘ └──────────────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
PostgreSQL Redis Container
(Bull) Registry
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, Tailwind CSS v4, React Query, Zustand |
| Backend API | NestJS 11, TypeORM, Passport JWT, Bull (Redis) |
| Build Engine | Kaniko (in-cluster, daemon-less Docker builds) |
| Deployment | Helm v3 charts, @kubernetes/client-node |
| Database | PostgreSQL 16 |
| Queue | Redis 7 + BullMQ |
📖 See ARCHITECTURE.md for detailed system design. 🔼 See UPGRADE.en.md (فارسی) for the latest dependency-upgrade notes (React 19, Next 16, NestJS 11, Tailwind 4, k8s-client v1).
Features
For Developers
- 🚀 One-click deploys from uploaded code archive (zip)
- 🟢 Node.js — auto-detected via
package.json(npm build & start) - 🟣 Laravel — PHP 8.x + Nginx + Supervisor (auto-detected via
artisan) - 🔵 WordPress — official image + custom entrypoint for wp-content merging
- 🗄️ Managed databases — PostgreSQL or MySQL provisioned via Helm
- 💰 Wallet system — deposit funds, pay for plans (hourly/monthly/yearly)
- 📊 Live logs & deployment history with rollback
- 🔒 Environment variables managed as Kubernetes Secrets
- ⚙️ Resource controls — CPU, memory, replica count
- 📸 Snapshots — backup and restore application state
- 🎫 Support tickets — in-app support system
For Super Admins
- 🖥️ Multi-cluster management — register/remove Kubernetes clusters
- 👥 User management — activate, deactivate, change roles
- 📈 Quotas — per-cluster limits (CPU, memory, max apps)
- 💳 Billing oversight — view all transactions, manage wallet deposits
- ⏱️ Lifecycle settings — configure grace periods per billing cycle
- 🔐 RBAC — role-based guards on every endpoint
Project Structure
host/
├── ARCHITECTURE.md # Detailed architecture document
├── README.md # This file
├── CHANGELOG.md # Version history
├── CONTRIBUTING.md # Development workflow & conventions
├── docker-compose.yml # Local dev / production compose
│
├── backend/ # NestJS API
│ ├── Dockerfile
│ ├── package.json
│ ├── helm/
│ │ ├── cloudhost-platform/ # Helm chart (control plane)
│ │ └── cloudhost-app/ # Helm chart (user apps)
│ │ ├── Chart.yaml
│ │ ├── values.yaml
│ │ └── templates/ # K8s manifest templates
│ ├── src/
│ │ ├── main.ts / app.module.ts
│ │ ├── auth/ # JWT auth (register, login, refresh)
│ │ ├── users/ # User CRUD + admin ops
│ │ ├── applications/ # Application CRUD + code upload
│ │ ├── deployments/ # Deployment pipeline orchestration
│ │ ├── clusters/ # Cluster management (admin)
│ │ ├── kubernetes/ # K8s client + Helm service
│ │ ├── build/ # Kaniko build jobs (Bull queue)
│ │ ├── billing/ # Wallet, transactions, plan costs
│ │ ├── lifecycle/ # Auto-suspend/delete scanner
│ │ ├── snapshots/ # App snapshot management
│ │ ├── tickets/ # Support ticket system
│ │ ├── common/ # Enums, decorators, guards
│ │ └── config/ # Env configuration loader
│ └── templates/ # Legacy Handlebars templates (deprecated)
│
├── frontend/ # Next.js 14 App Router
│ ├── Dockerfile
│ ├── package.json
│ └── src/
│ ├── app/
│ │ ├── login/ & register/
│ │ └── dashboard/
│ │ ├── apps/ # App list + detail (lifecycle status)
│ │ ├── deploy/ # Multi-step deploy wizard
│ │ └── admin/ # Admin: users, clusters, billing, apps
│ ├── components/
│ ├── lib/ # API client, auth store
│ ├── hooks/
│ └── types/ # TypeScript interfaces
│
└── uploads/ # User-uploaded code archives
Quick Start
Prerequisites
| Tool | Version |
|---|---|
| Node.js | ≥ 20 |
| Docker & Compose | ≥ 24 |
| PostgreSQL | 16 |
| Redis | 7 |
| Helm | ≥ 3.12 |
1. Clone & Install
git clone <repo-url> host && cd 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 files with your DB, JWT, Redis, and registry settings
3. Run with Docker Compose
docker compose up --build
Backend at port 4000, Frontend at port 3000.
4. Deploy Platform on Kubernetes (Helm)
Prerequisites: NGINX Ingress, cert-manager (if TLS enabled), StorageClass for PVCs.
# Build images (set API URL to match ingress.api.host when TLS is on)
export REG=your-registry.example.com
docker build -t $REG/cloudhost-backend:latest ./backend
docker build -t $REG/cloudhost-frontend:latest \
--build-arg NEXT_PUBLIC_API_URL=https://api.platform.example.com ./frontend
docker push $REG/cloudhost-backend:latest $REG/cloudhost-frontend:latest
# Install (copy and edit values-production.example.yaml first)
helm upgrade --install cloudhost ./backend/helm/cloudhost-platform \
-n cloudhost --create-namespace \
-f backend/helm/cloudhost-platform/values-production.example.yaml
Key values: ingress.enabled, ingress.tls.enabled, ingress.frontend.host, ingress.api.host, postgres.password, secrets.jwtSecret.
See chart defaults in backend/helm/cloudhost-platform/values.yaml and post-install notes via helm get notes cloudhost -n cloudhost.
5. Run Locally (development)
# Terminal 1 — Backend
cd backend && npm run start:dev
# Terminal 2 — Frontend
cd frontend && npm run dev
API Endpoints
All endpoints prefixed with /api/v1. Full Swagger docs at http://localhost:4000/docs.
Auth
| Method | Path | Description |
|---|---|---|
| POST | /auth/register | Create account |
| POST | /auth/login | Get JWT tokens |
| POST | /auth/refresh | Refresh access token |
Applications
| Method | Path | Description |
|---|---|---|
| POST | /applications | Create app |
| GET | /applications | List user's apps |
| GET | /applications/:id | App details |
| PATCH | /applications/:id | Update app |
| DELETE | /applications/:id | Delete app + K8s resources |
Deployments
| Method | Path | Description |
|---|---|---|
| POST | /applications/:appId/deployments | Trigger deploy |
| GET | /applications/:appId/deployments | List deployments |
| GET | /deployments/:id | Deployment detail |
| GET | /deployments/:id/logs | Pod logs |
| POST | /deployments/:id/stop | Stop deployment |
| POST | /deployments/:id/restart | Restart deployment |
Billing
| Method | Path | Description |
|---|---|---|
| GET | /billing/balance | Get wallet balance |
| POST | /billing/deposit | Add funds to wallet |
| GET | /billing/transactions | Transaction history |
| POST | /billing/pay/:appId | Pay for app plan |
Lifecycle (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /lifecycle/settings | Get retention periods |
| PATCH | /lifecycle/settings | Update retention periods |
Snapshots
| Method | Path | Description |
|---|---|---|
| POST | /snapshots | Create snapshot |
| GET | /snapshots | List snapshots |
| POST | /snapshots/:id/restore | Restore snapshot |
Tickets
| Method | Path | Description |
|---|---|---|
| POST | /tickets | Create ticket |
| GET | /tickets | List tickets |
| PATCH | /tickets/:id | Update ticket |
Users
| Method | Path | Description |
|---|---|---|
| GET | /users/me | Current user |
| PATCH | /users/me | Update profile |
Admin — Users
| Method | Path | Description |
|---|---|---|
| GET | /users | List all users |
| PATCH | /users/:id/activate | Activate user |
| PATCH | /users/:id/deactivate | Deactivate user |
| PATCH | /users/:id/role | Change role |
Admin — Clusters
| Method | Path | Description |
|---|---|---|
| POST | /clusters | Add cluster |
| GET | /clusters | List clusters |
| GET | /clusters/:id | Cluster details |
| PATCH | /clusters/:id | Update cluster |
| DELETE | /clusters/:id | Remove cluster |
Configuration
| Variable | Description | Default |
|---|---|---|
PORT |
Backend port | 4000 |
DB_HOST |
PostgreSQL host | localhost |
DB_PORT |
PostgreSQL port | 5432 |
DB_USERNAME |
Database user | cloudhost |
DB_PASSWORD |
Database password | — |
DB_NAME |
Database name | cloudhost |
JWT_SECRET |
JWT signing secret | — |
JWT_EXPIRES_IN |
Access token TTL | 15m |
REDIS_HOST |
Redis host | localhost |
REDIS_PORT |
Redis port | 6379 |
REGISTRY_URL |
Container registry URL | localhost:30500 |
PLATFORM_DOMAIN |
Base domain for app subdomains | apps.cloudhost.ir |
LIFECYCLE_SCAN_INTERVAL_MS |
Lifecycle scanner interval | 60000 |
LIFECYCLE_HOURLY_DELETE_AFTER_MS |
Hourly plan grace period | 3600000 (1h) |
LIFECYCLE_MONTHLY_DELETE_AFTER_MS |
Monthly plan grace period | 259200000 (3d) |
LIFECYCLE_YEARLY_DELETE_AFTER_MS |
Yearly plan grace period | 604800000 (7d) |
Security
- JWT access + refresh tokens with configurable expiry
- Bcrypt password hashing (12 rounds)
- Helmet HTTP security headers
- RBAC role-based route guards (
@Roles(UserRole.ADMIN)) - Namespace isolation — each user deploys to their own K8s namespace
- Secrets — env vars stored as K8s Secrets, never in plain manifests
- Input validation —
class-validatoron all DTOs
License
MIT