124 Commits

Author SHA1 Message Date
keyhan 58ab81469b Stabilize CI Kaniko tag and keep Harbor free of egress proxy.
Build and Deploy Platform / build-and-deploy (push) Failing after 3m18s
Use the seeded kaniko v1.27.6-debug image, retry npm ci, and push via harbor-core without HTTP_PROXY so Harbor UI metadata and blob uploads keep working.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 16:47:23 +03:30
keyhan fec9ec386f Push Kaniko artifacts via harbor-core for Harbor UI visibility.
Build and Deploy Platform / build-and-deploy (push) Failing after 12m36s
Add REGISTRY_PUSH_URL config, route CI Kaniko to harbor-core, and document dual-host kaniko auth for core push plus registry base-image pull.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-11 11:43:05 +03:30
keyhan ec72ee4fca Fix app image pulls and Harbor kubelet auth for user workloads.
Route k3s registry mirrors through harbor-core ClusterIP with hostname-only auth keys, use HTTP EXT_ENDPOINT so OAuth tokens work on port 80, extend deploy readiness timeout, and harden Kaniko build/dockerfile fallbacks.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 18:38:05 +03:30
keyhan 54ab2f2f05 Fix Kaniko registry auth and push target for Harbor builds.
Mount docker config as config.json (Kaniko requirement), push via
harbor-registry internal URL, and wire harbor_registry_user credentials
in Helm/GitOps values.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 12:29:23 +03:30
keyhan 214b617be0 Fix image pull/build failures on restricted egress clusters.
Use seeded abrban/ images instead of flaky proxy-gcr pulls, fix Kaniko dockerfile path for v1.27, correct docker auth host keys, route /v2/abrban/ through harbor-core, and prefer abrban/ for base images.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 20:08:16 +03:30
keyhan 2679c9d66e Fix Kaniko image refs to use seeded abrban/ images.
v1.27.6-debug does not exist on gcr.io; proxy-gcr pulls are flaky on kubelet. Seed and reference abrban/kaniko-executor:v1.23.2 instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 18:31:37 +03:30
keyhan 1ec4d07939 Add egress proxy to user-app Kaniko build jobs.
Inject registry-egress-proxy into Kaniko and network init containers so npm/apk/composer/pip/git clone work on restricted egress clusters.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 18:07:48 +03:30
keyhan 3d773a4a62 feat(platform): wire OTP SMS env from platform Secret via Helm
Build and Deploy Platform / build-and-deploy (push) Successful in 32m19s
Enable backend.sms in the chart so MizbanSMS credentials from the platform
Secret are injected into the backend deployment for production OTP delivery.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-05 20:05:08 +03:30
keyhan b2ecdad53b fix(logging): use ELASTIC_PASSWORD env in ES health probes
Build and Deploy Platform / build-and-deploy (push) Failing after 30m14s
Hardcoded Basic auth in probes broke after password rotation; exec probes
read the live secret so Elasticsearch stays healthy when credentials change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-03 19:41:19 +03:30
keyhan a2fe61b1f6 fix(platform): inject ELASTIC_PASSWORD from platform secrets in production
Build and Deploy Platform / build-and-deploy (push) Successful in 16m13s
Backend validate-production-config requires a non-default ELASTIC_PASSWORD;
read it from abrban-platform-secrets.elastic-password like other credentials.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-03 19:24:34 +03:30
keyhan d3bbc0c0a0 fix(build): default Kaniko and init images to Harbor via Helm values
Build and Deploy Platform / build-and-deploy (push) Successful in 14m57s
User-app builds no longer pull gcr.io/docker.io directly when build.images
is configured in values.yaml, fixing ImagePullBackOff on clusters without
upstream registry access.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-03 14:20:36 +03:30
keyhan 8163665c86 fix(platform): close remaining audit findings from security review
Harden preview/deploy flows, OTP generation, zip extraction, and multi-replica billing races; document full remediation status in AUDIT-STATUS.fa.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-03 12:30:22 +03:30
keyhan 22359be40e fix(platform): apply production hardening from audit plan
Close billing, tenancy, migration, build, and CI/CD gaps identified in the
audit: wallet/gateway guards, full-UUID namespaces, idempotent migrations with
base schema, stateful service stability, safer Dockerfiles/git builds, and
platform chart hardening (Redis auth, RollingUpdate, backups, Swagger off).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 19:35:07 +03:30
keyhan 6ba77eebcf ci: pull node base image from Harbor instead of docker.io
Build and Deploy Platform / build-and-deploy (push) Failing after 47s
Kaniko builds failed with context deadline exceeded pulling node:24-alpine
from index.docker.io through the egress proxy. Seed node:24-alpine into
abrban/ and pass BASE_IMAGE build-arg so builds use the internal registry.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 18:32:41 +03:30
keyhan 1572b3ce66 fix(chart): support existingSecret and provide CLUSTER_KUBECONFIG_KEY to backend
Build and Deploy Platform / build-and-deploy (push) Failing after 50m25s
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
keyhan 5ed2ef0958 Add GitOps stack for abrban.com with Gitea Actions CI/CD.
Build and Deploy Platform / build-push-deploy (push) Has been cancelled
Harbor in-cluster builds via Kaniko, ArgoCD auto-sync, and production Helm values for abrban.com domains.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 01:27:20 +03:30
keyhan ee5bd0a291 Add Harbor/Ceph runbooks and align single-node Ceph docs with abr deployment.
Document registry ingress split, image mirroring, loop OSD setup, and platform integration paths.
2026-07-01 00:22:06 +03:30
keyhan 8d1855b89c Detect and validate app runtime from uploaded archives.
Reject zip uploads when the selected runtime does not match archive contents, and re-validate before Kaniko builds to fail fast instead of producing the wrong Dockerfile.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 00:27:03 +03:30
keyhan 837f0fa63f Harden platform security, reliability, and CI after full audit.
Close deployment IDOR and gate stub payment endpoints, add production
secret validation, health probes, Redis-backed build progress, GitHub
Actions CI, expanded tests, billing/k8s refactors, and ops runbooks.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 20:59:49 +03:30
keyhan 9c16b462f4 revert(build): remove app build pipeline revamp (Nixpacks/MinIO/Trivy/registry GC)
Reverts commits 3eff38f and c379a23 and restores the previous Kaniko-only
build pipeline (runtime detection + per-runtime Dockerfile generation,
disk-based source upload).

Removed: Nixpacks Dockerfile generation, MinIO source storage (common/storage),
Bull build queue + Redis build state (common/redis, deployment.processor),
Trivy image scan (scan.service, deployment.vulnerabilitySummary), and daily
registry garbage collection (registry-gc). Nothing outside the build/deploy
path depended on these. Backend tsc + 105/106 tests green (the pre-existing
helm.service chartPath failure is unrelated); frontend tsc green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 19:21:30 +03:30
keyhan f7974dd382 feat(landing,billing): public pricing, global discount, services bar & estimator
Backend
- Add platform-wide global discount (platform_settings: global_discount_percent),
  applied centrally in PricingCatalogService.computeTotalsFromDb so it reaches
  every real charge (previews, deploys, renewals, upgrades, invoices). Admin
  GET/PATCH /billing/settings/global-discount.
- Add unauthenticated PublicPricingController (catalog + calculate) for the
  public landing page, returning gross/net and the discount percentage.
- Bill application replicas by the user-selected footprint: app CPU/RAM/storage
  now all scale by replica count; the single-replica database stays unscaled.

Frontend
- Landing: Services bar (PaaS active, DBaaS, KaaS/LaaS "coming soon" with
  expandable runtime/database menus), transparent Pricing section (per-resource
  rate cards with cycle toggle + discount strikethrough), and a cost Estimator
  ("estimate your package").
- Optional services and the database are priced like runtimes: the estimator
  lets users pick their CPU/RAM/storage (and DB type) so the cost scales by need.
- Admin billing: global-discount editor.
- i18n: fa/en strings for services, pricing, estimator and global discount.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:44:30 +03:30
keyhan c379a2389a fix(build): clone private git repos via $GIT_TOKEN env, not credential store 2026-06-20 23:38:11 +03:30
keyhan 3eff38f8d2 feat(build): revamp app build pipeline (queue, Nixpacks, MinIO, Trivy, registry GC)
Rework the application build/deploy pipeline for scalability, reproducibility,
and security:

- Build queue: deploys run through a bounded-concurrency Bull queue
  (BUILD_CONCURRENCY, default 3) so concurrent user deploys can't flood the
  cluster with Kaniko jobs. Build state (progress / cancel / session) moves from
  in-memory Maps to Redis, so cancel + live logs work across backend replicas.
- Nixpacks + BYO Dockerfile: code runtimes build via Nixpacks (or the user's own
  Dockerfile when present); the hand-written per-runtime Dockerfile generators
  and runtime auto-detection are removed. WordPress keeps its templated path.
  Build-time mirror env (NIXPACKS_BUILD_ENV) supports the Iran network.
- Source upload to MinIO: archives stream to in-cluster MinIO; build pods pull
  via a presigned URL. Removes the PVC + helper pod + kubectl cp upload path.
- Report-only Trivy scan after build; per-severity summary stored on the
  deployment and shown as a badge in the dashboard. Never gates a deploy.
- Registry GC: a Redis-locked daily job keeps the newest N image tags per app
  (REGISTRY_KEEP_VERSIONS, default 3) and reclaims disk via garbage-collect.
- Hardening: git tokens are delivered via a per-build Secret + git credential
  store instead of being embedded in the clone URL / Job manifest; build timeout
  is configurable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:58:58 +03:30
keyhan 49726f1dfd feat(billing): add percentage discount coupons
Admins can create coupon codes that discount specific services (app
runtimes, optional services, managed products, custom-domain addon, or
all) and restrict them to specific users or make them public, with total
and per-user usage caps and an active date window.

Coupons apply in deploy, renewal, and upgrade flows: cost-breakdown lines
are tagged with a service key, the eligible portion is discounted and
capped to the payable amount, the invoice records discountAmount/
discountCode, and the redemption is recorded once when the invoice is
fully paid (covering wallet, gateway, and mixed payments).

- Discount + DiscountRedemption entities; invoice discount columns
- DiscountService (CRUD, validation, redemption) + admin/validate API
- Idempotent schema bootstrap on init so production (synchronize off)
  provisions the tables/columns without a migration runner
- Admin discounts UI, coupon entry in deploy/renewal, invoice discount line
- fa/en strings; discount.service unit spec

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 11:48:45 +03:30
keyhan 4d64fda227 feat(invoices): Persian line items + elegant client-side PDF download
- Translate persisted English invoice line labels/descriptions/reasons to
  Persian at display time (new lib/invoice-labels.ts), covering both new and
  historical invoices without a data migration.
- Generate a styled, RTL Persian invoice PDF on the client (lib/invoice-pdf.ts)
  with the Abrban logo, line-item table and totals, via html2canvas + jsPDF.
- Wire both the user and admin invoice pages to the translator and new download.
- Add invoices.pdf dictionary keys (fa/en).
- Remove the now-dead ASCII-only backend PDF endpoints and generateInvoicePdf.
- Add frontend/.npmrc (npmmirror registry + high timeouts) for Iran-network installs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 19:59:22 +03:30
keyhan dbc169206f feat(deploy): remove cluster allocation section from app creation
Drop the admin-only "cluster assignment" UI from the resources &
config step of the deploy wizard and the related backend override.
App placement is now always decided automatically by the allocator.

- frontend: remove cluster/pool selection block, review-step cluster
  row, clusterMode state, public cluster/pool queries, and clusterId/
  poolId from CreateApplicationDto
- backend: drop clusterId/poolId override from the create DTO and
  simplify ApplicationsService.create to always auto-allocate; widen
  selectClusterForApplication param to keep the fallback path working

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:58:30 +03:30
keyhan f204d248fd docs(env): document MizbanSMS OTP vars and FRONTEND_URL in .env.example
Add the SMS_PROVIDER switch, the MizbanSMS credential/route/template vars
(now the default provider), and FRONTEND_URL (CORS + domain resolution),
so a fresh checkout has an accurate, complete env reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 19:43:53 +03:30
keyhan 8dc351ab21 fix(auth): enforce live role + active status from DB on every request
JwtStrategy read `role` straight from the JWT payload, so a role change
or deactivation stayed ineffective until the 1h access token expired:
after a promotion the menus showed (via /users/me) but admin endpoints
returned 403 because RolesGuard still saw the old token role; after a
demotion the old admin kept API access. Load the user from the DB in
validate() and use the current role; reject inactive users.

Frontend: poll /users/me in the dashboard layout (+ on window focus) so
the sidebar reflects role changes without a hard reload, and redirect
away from pages the new role can no longer reach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 18:55:24 +03:30
keyhan fd38f5659f feat(admin): login-as-user impersonation with audit log
Let super admins act as a user from the user detail dashboard for
support/debugging ("full with guardrails", audit-only).

Backend: AuthService.impersonate issues a short-lived token for the
target carrying an `act` claim (acting admin); refresh preserves it and
JwtStrategy surfaces `impersonatedBy`. Guardrails: cannot impersonate an
admin or a deactivated account; new ImpersonationGuard blocks sensitive
self-service (change own password/phone) while impersonating. New
AuditLog entity records impersonation start/stop (admin, target, ip,
time); admin endpoints POST users/:id/impersonate + .../impersonation/
stop and GET users/:id/audit.

Frontend: lib/impersonation swaps admin/impersonation tokens in
localStorage; persistent banner with exit; "Login as user" button and an
"Admin access log" tab on the detail page; logout clears impersonation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 00:49:02 +03:30
keyhan 7958d2fa72 feat(admin): super-admin user detail dashboard
Add a read-only User Detail dashboard for super admins, reachable by
clicking a user name in the admin users list.

Backend: new `admin` module aggregating existing domain services
(no new entities). ADMIN-only endpoints under /api/v1/admin:
overview (profile, account status, wallet balance, revenue, summary
counts), wallet transactions, applications (incl. deleted/docked with
restore eligibility), build/deploy errors, tickets with conversation,
and a composite activity timeline. Adds BillingService.getRevenueSummary
and guards against a wallet get-or-create race in the overview reads.

Frontend: tabbed detail page (overview/applications/activity/errors/
tickets) with lazy per-tab queries; user names in the admin list link to
it (admin only); fa/en i18n keys and response types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:52:21 +03:30
keyhan 7770c543fa fix(sms): unwrap MizbanSMS array-wrapped response to detect failures
The API returns its result inside a JSON array (e.g. "[1008]" for an
error, "[100002656565]" for a delivered message id). The previous parser
matched the raw string against the error map, so any array-wrapped error
slipped through as a false success and the caller got 201 with no SMS
sent. Unwrap the array (or bare/quoted value) before the error lookup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:05:51 +03:30
keyhan fc3d0a7e04 feat(auth): add MizbanSMS OTP provider with per-flow messages and signup recovery
- Add MizbanSMS as selectable SMS provider (SMS_PROVIDER), keep Kavenegar
- Distinct OTP wording per flow via OtpMessageKind (register/login/change-phone)
- register() resumes an unverified account instead of blocking re-registration
- Hourly cleanup of abandoned unverified accounts (>24h) + expired OTP codes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:44:58 +03:30
keyhan 7d7971744e fix(db): explicit TypeORM column types for nullable string/uuid columns
`string | null` / `uuid | null` columns reflect as Object and crash the
backend at metadata build (DataTypeNotSupportedError). Declare explicit
`type` on users.phone/email and cluster_allocation_logs FK id columns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 13:09:26 +03:30
keyhan a3b7e9055c feat(admin): create users by mobile number instead of email
Admin user management now creates accounts with a required mobile
number and an optional contact email, matching mobile-only auth.
Admin-created accounts are pre-verified (phoneVerified) so the user
can sign in by password immediately. The user list and search now
include phone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 12:34:43 +03:30
keyhan 95d0b162c5 feat(panel): serve authenticated app on dedicated panel subdomain
Separate the marketing landing site from the authenticated app by host.
Next.js middleware reads PANEL_HOST/LANDING_HOST at runtime and redirects
authenticated routes (/dashboard, /login, /register) from the landing host
to the panel host, and the landing root on the panel host to /dashboard.
Disabled (single-origin) when PANEL_HOST is unset, so local dev is unchanged.

Helm: add ingress.panel.host with a third ingress rule + TLS host routing to
the frontend service, pass LANDING_HOST/PANEL_HOST to the frontend, and append
the panel origin to the backend CORS list (frontend URL stays first so
PLATFORM_DOMAIN resolution is unaffected).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:33:19 +03:30
keyhan 37c103fa20 feat(auth): mobile-only register/login with OTP verification
- Register and login by mobile number; email is now an optional
  contact field only (never used to authenticate)
- After registration, the phone is verified via a 6-digit SMS code
- Login supports both password and one-time-code (OTP) methods
- Phone OTP delivered via Kavenegar (verify/lookup); API key in env
- Account page: edit name/optional email, change password, and
  change mobile number with OTP re-verification
- Codes are hashed, expire in 5m, capped at 5 attempts, rate-limited
- Seed gives the admin a verified phone so mobile login still works

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 16:40:08 +03:30
keyhan 643e8c5c61 Merge branch 'feat/live-build-logs-modal' 2026-06-16 02:08:03 +03:30
keyhan 6022739b80 feat(deploy): stream live build logs in build progress modal
Show real-time kaniko/init-container build output inside the build
progress modal during the building/deploying phases, alongside the
existing percentage. The build-logs endpoint now returns live pod logs
while a build is in progress (falling back to the persisted log once
finished), and the modal polls it every 2s with auto-scroll. Minimize
behaviour is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 01:50:14 +03:30
keyhan 901a20eb01 feat(deploy): configurable CPU/RAM/disk for app databases
Let users size the database workload from the deploy wizard the same way
Redis/RabbitMQ are configured: a resource card (CPU request/limit, memory
request/limit, storage) on the Resources step, stored in
optionalServiceResources.database.

- entity/DTO: add `database` to optionalServiceResources
- k8s: resolveDatabaseResources() applies user-selected resources on both
  the Helm and K8s-API deploy paths (was hardcoded 100m/256Mi→500m/512Mi)
- billing: bill database CPU/RAM as a separate line on top of the app's
  resources; merge it through the upgrade path too
- wizard: db resource card on the Resources step, disk moved into the card,
  cost preview + review summary include the database resources

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 01:20:33 +03:30
keyhan e53fc8e2ff fix(config): handle comma-separated FRONTEND_URL for domains and CORS
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>
2026-06-15 11:24:35 +03:30
keyhan 8b77656bb7 chore(deps): upgrade all dependencies to latest stable
Bring backend and frontend to the latest stable releases (no pre-releases),
including major upgrades that required code migration. Both projects pass
typecheck and production builds.

Backend
- NestJS 10 -> 11 (common/core/platform-express/jwt/passport/bull/cli/
  schematics/testing), @nestjs/config 3->4, @nestjs/swagger 7->11,
  @nestjs/typeorm 10->11
- @kubernetes/client-node 0.21 -> 1.4: migrate ~200+ call sites across 6
  services to the v1 single-object argument API, unwrapped responses, err.code,
  setHeaderOptions for patch content-type, applyToHTTPSOptions. Add regression
  spec k8s-client-v1-migration.spec.ts.
- typeorm 0.3 -> 1.0: relations/select string arrays -> object form
- uuid 9->14 (drops @types/uuid), multer 1->2, bcrypt 5->6, helmet 7->8,
  class-validator 0.14->0.15
- TypeScript 5->6, ESLint 8->9, @typescript-eslint 6->8, jest 29->30,
  @types/node 20->24; tsconfig: strictPropertyInitialization:false,
  ignoreDeprecations, rootDir, explicit types[]
- @nestjs/config 4: jwt.strategy uses getOrThrow; @types/express kept at 4
  (Nest 11 runs Express 4)

Frontend
- React 18->19, Next 14->16 (async params via official codemod),
  Tailwind 3->4 (@tailwindcss/postcss, @import + @config, inline custom @apply),
  framer-motion 11->12, zustand 4->5, three 0.169->0.184, @react-three/* majors
- TypeScript 5->6 (tsconfig target es5->ES2017), ESLint 8->9,
  eslint-config-next 14->16

Infra/docs
- Dockerfiles node:20-alpine -> node:24-alpine (require-esm for k8s client)
- Add UPGRADE.md / UPGRADE.en.md; refresh README tech-stack versions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:05:33 +03:30
keyhan dd1e70d80f chore: sync npm lockfile peer flags and frontend build cache.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 16:29:24 +03:30
keyhan 6df11b738f Restore the preview host and its SSL when a custom domain is removed.
Removing a custom domain left the Ingress with a stale custom-domain rule
and no preview host, so the platform preview URL 404'd. updateIngress now
resolves the app's stable preview number from its latest deployment and
re-emits the preview host (with cert-manager TLS) whenever no verified
custom domain is set, and removeCustomDomain re-applies the Ingress.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 16:25:43 +03:30
keyhan 0b08b995f0 Issue SSL for custom domains and show a public CNAME target.
Derive the CNAME target shown to users from the site's public root
domain (PREVIEW_BASE_DOMAIN, e.g. 3fase.ir) instead of the internal
platform domain, which is not a valid public suffix and cannot get a
Let's Encrypt cert. On DNS verification, re-apply the Ingress so Traefik
routes the custom domain and cert-manager issues a cert via HTTP-01,
mirroring the preview-domain flow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 16:19:53 +03:30
keyhan 4301277b48 Serve preview URLs over Traefik+TLS, stabilize them, and speed up builds.
Ingress / preview URLs:
- Default the app Ingress class and ACME HTTP-01 solver to Traefik
  (k3s default) via a new INGRESS_CLASS env, instead of hardcoding nginx —
  fixes 404s on clusters without ingress-nginx.
- Only put public, real-TLD hosts (custom domain + preview) in the TLS
  block; the internal *.apps.cloudhost.local host no longer poisons the
  Let's Encrypt order, so certs actually issue.
- Make the per-app preview number stable across redeploys so URLs stop
  breaking, and let PREVIEW_BASE_DOMAIN configure the base domain.

Registry pulls:
- Point the k3s registries.yaml mirror endpoint at the registry NodePort on
  loopback so node containerd never depends on cluster DNS (image pulls
  survive node restarts).

Builds:
- Pin the Kaniko image, use IfNotPresent pull policy, drop the dead build
  queue/processor, and retry transient Kubernetes API errors while polling
  build jobs.

Logs & apps list:
- fluent-bit reads log files from head so startup output reaches
  Elasticsearch.
- Order joined deployments newest-first so the apps list shows the latest
  deployment status.

Allocation:
- Reserve in-flight (pending/building) capacity and stop globally degrading
  the cluster on a single allocation failure, so concurrent deploys don't
  starve or wrongly report "no healthy cluster".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 15:44:08 +03:30
keyhan 786689e0fd Add per-cluster Tools Management and stop auto-installing side tools.
Introduce a catalog-driven Tools Management section under Clusters so
admins can install/uninstall infrastructure tools per cluster: cert-manager
(Helm/jetstack), ClusterIssuer (email + HTTP01 form, depends on cert-manager),
and central Elasticsearch. Cluster creation no longer auto-installs Elastic
or the cloudhost-node-cluster-dns DaemonSet; build bootstrap stays automatic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 16:55:51 +03:30
keyhan be2587dcf5 Use per-deploy preview hosts under the site root domain.
Build hosts as <userPrefix>-<deploymentNumber>-preview.<rootDomain> from FRONTEND_URL, wire them through ingress/TLS, and open them from the preview API.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 12:48:22 +03:30
keyhan 44ad1d63a0 Fix backend Elasticsearch connectivity for local dev and app logs.
Auto port-forward using the registered cluster kubeconfig, default to loopback outside Kubernetes, route log APIs by app cluster, and document platform env settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 11:51:51 +03:30
keyhan 3435eff256 Fix kubelet registry pulls via node cluster DNS and in-cluster mirrors.
Bootstrap configures systemd-resolved for *.cluster.local, installs k3s registries.yaml for the internal registry only, removes the legacy external-registry DaemonSet, and aligns Helm REGISTRY_PULL_URL with the in-cluster registry URL.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 11:36:07 +03:30
keyhan 20c4184cde Generate random platform subdomains for apps without custom domains.
Assign unpredictable subdomains on create when no custom domain is set, and update deploy UI CNAME hints to reference the app Platform Domain after creation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 19:55:44 +03:30