@habeetat/cli 0.1.0-dev.20260323164114.3a5aac3
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/README.md +35 -0
- package/dist/bin.js +321 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.mts +107 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +592 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +568 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/templates/docker-compose.ts","../src/templates/nginx.ts","../src/utils/crypto.ts","../src/templates/env.ts","../src/utils/logger.ts","../src/utils/docker.ts","../src/utils/config.ts"],"names":["crypto","chalk","execaCommand","path","fs"],"mappings":"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","file":"index.js","sourcesContent":["export const CONFIG_FILE = 'habeetat.json';\nexport const ENV_FILE = '.env';\nexport const COMPOSE_FILE = 'docker-compose.yml';\n\nexport const DOCKER_REGISTRY = 'docker.io/capriisland';\n\nexport const IMAGES = {\n backend: 'capriisland/habeetat-backend',\n launcher: 'capriisland/habeetat-launcher',\n orgManager: 'capriisland/habeetat-org-manager',\n platformManager: 'capriisland/habeetat-platform-manager',\n sampleCrm: 'capriisland/habeetat-sample-crm',\n logtoConfig: 'capriisland/habeetat-logto-config',\n} as const;\n\nexport const THIRD_PARTY_IMAGES = {\n postgres: 'postgres:15-alpine',\n logto: 'svhd/logto:1.33',\n nginx: 'nginx:1.25-alpine',\n certbot: 'certbot/certbot',\n} as const;\n\nexport const DEFAULT_PORTS = {\n http: 80,\n https: 443,\n} as const;\n\nexport const SERVICES = [\n 'logto-db',\n 'platform-db',\n 'logto-core',\n 'backend',\n 'launcher',\n 'organization-manager',\n 'platform-manager',\n 'sample-crm',\n 'nginx',\n] as const;\n\nexport type ServiceName = (typeof SERVICES)[number];\n","import type { HabeetatConfig } from '../types.js';\nimport { THIRD_PARTY_IMAGES } from '../constants.js';\n\nexport function generateDockerCompose(config: HabeetatConfig): string {\n const tag = config.docker.imageTag;\n const registry = config.docker.registry;\n\n const services: Record<string, unknown> = {\n 'logto-db': {\n image: THIRD_PARTY_IMAGES.postgres,\n environment: {\n POSTGRES_USER: '${LOGTO_DB_USER}',\n POSTGRES_PASSWORD: '${LOGTO_DB_PASSWORD}',\n POSTGRES_DB: '${LOGTO_DB_NAME}',\n },\n volumes: ['logto-db-data:/var/lib/postgresql/data'],\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n healthcheck: {\n test: ['CMD-SHELL', 'pg_isready -U ${LOGTO_DB_USER} -d ${LOGTO_DB_NAME}'],\n interval: '5s',\n timeout: '3s',\n retries: 10,\n },\n },\n\n 'platform-db': {\n image: THIRD_PARTY_IMAGES.postgres,\n environment: {\n POSTGRES_USER: '${PLATFORM_DB_USER}',\n POSTGRES_PASSWORD: '${PLATFORM_DB_PASSWORD}',\n POSTGRES_DB: '${PLATFORM_DB_NAME}',\n },\n volumes: ['platform-db-data:/var/lib/postgresql/data'],\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n healthcheck: {\n test: ['CMD-SHELL', 'pg_isready -U ${PLATFORM_DB_USER} -d ${PLATFORM_DB_NAME}'],\n interval: '5s',\n timeout: '3s',\n retries: 10,\n },\n },\n\n 'logto-core': {\n image: THIRD_PARTY_IMAGES.logto,\n entrypoint: ['sh', '-c'],\n command: ['npm run cli -- db seed -- --swe || true\\nnpm run start'],\n depends_on: {\n 'logto-db': { condition: 'service_healthy' },\n },\n environment: {\n DB_URL: 'postgres://${LOGTO_DB_USER}:${LOGTO_DB_PASSWORD}@logto-db:5432/${LOGTO_DB_NAME}',\n ENDPOINT: '${LOGTO_ENDPOINT}',\n ADMIN_ENDPOINT: '${LOGTO_ADMIN_ENDPOINT}',\n ADMIN_CONSOLE_URL: '${LOGTO_ADMIN_ENDPOINT}',\n ADMIN_PORT: '3002',\n TRUST_PROXY_HEADER: '1',\n COOKIE_KEYS: '${ENCRYPTION_KEY}',\n ALLOWED_ORIGINS: '${LOGTO_ENDPOINT},${LOGTO_ADMIN_ENDPOINT},${LAUNCHER_URL},${ORG_MANAGER_URL},${PLATFORM_MANAGER_URL},${SAMPLE_CRM_URL},${BACKEND_API_URL}',\n CORS_ALLOWED_ORIGINS: '${LOGTO_ENDPOINT},${LOGTO_ADMIN_ENDPOINT},${LAUNCHER_URL},${ORG_MANAGER_URL},${PLATFORM_MANAGER_URL},${SAMPLE_CRM_URL},${BACKEND_API_URL}',\n ADMIN_CONSOLE_ALLOWED_ORIGINS: '${LOGTO_ADMIN_ENDPOINT}',\n },\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n healthcheck: {\n test: ['CMD-SHELL', 'wget -q --spider http://localhost:3001/oidc/.well-known/openid-configuration || exit 1'],\n interval: '10s',\n timeout: '5s',\n retries: 15,\n start_period: '90s',\n },\n },\n\n backend: {\n image: `${registry}/habeetat-backend:${tag}`,\n depends_on: {\n 'logto-core': { condition: 'service_healthy' },\n 'platform-db': { condition: 'service_healthy' },\n },\n environment: {\n NODE_ENV: 'production',\n DATABASE_URL: 'postgres://${PLATFORM_DB_USER}:${PLATFORM_DB_PASSWORD}@platform-db:5432/${PLATFORM_DB_NAME}',\n IAM_ISSUER_URL: '${LOGTO_ENDPOINT}/oidc',\n IAM_JWKS_URL: 'http://logto-core:3001',\n IAM_AUDIENCE: '${IAM_AUDIENCE}',\n LOGTO_ENDPOINT: 'http://logto-core:3001',\n LOGTO_EXTERNAL_ENDPOINT: '${LOGTO_ENDPOINT}',\n SDK_API_RESOURCE: '${IAM_AUDIENCE}',\n LOGTO_M2M_APP_ID: 'nhp-m2m-config',\n LOGTO_M2M_APP_SECRET: '${LOGTO_M2M_APP_SECRET}',\n ENCRYPTION_KEY: '${ENCRYPTION_KEY}',\n LAUNCHER_URL: '${LAUNCHER_URL}',\n CORS_ORIGINS: '${CORS_ORIGINS}',\n },\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n healthcheck: {\n test: ['CMD', 'curl', '-f', 'http://localhost:3001/platform/status'],\n interval: '30s',\n timeout: '10s',\n retries: 3,\n start_period: '60s',\n },\n },\n };\n\n if (config.services.launcher) {\n services['launcher'] = {\n image: `${registry}/habeetat-launcher:${tag}`,\n depends_on: ['backend'],\n environment: {\n VITE_IAM_LOGTO_ENDPOINT: '${LOGTO_ENDPOINT}',\n VITE_IAM_AUDIENCE: '${IAM_AUDIENCE}',\n VITE_IAM_APP_ID: 'nhp-launcher',\n VITE_APP_BASE_URL: '${LAUNCHER_URL}',\n VITE_PLATFORM_API_URL: '${BACKEND_API_URL}',\n },\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n };\n }\n\n if (config.services.organizationManager) {\n services['organization-manager'] = {\n image: `${registry}/habeetat-org-manager:${tag}`,\n depends_on: ['backend'],\n environment: {\n VITE_LOGTO_ENDPOINT: '${LOGTO_ENDPOINT}',\n VITE_LOGTO_APP_ID: 'nhp-org-manager',\n VITE_LOGTO_API_RESOURCE: '${IAM_AUDIENCE}',\n VITE_APP_BASE_URL: '${ORG_MANAGER_URL}',\n VITE_BACKEND_API_URL: '${BACKEND_API_URL}',\n },\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n };\n }\n\n if (config.services.platformManager) {\n services['platform-manager'] = {\n image: `${registry}/habeetat-platform-manager:${tag}`,\n depends_on: ['backend'],\n environment: {\n VITE_LOGTO_ENDPOINT: '${LOGTO_ENDPOINT}',\n VITE_LOGTO_APP_ID: 'nhp-plat-mgr',\n VITE_LOGTO_API_RESOURCE: '${IAM_AUDIENCE}',\n VITE_APP_BASE_URL: '${PLATFORM_MANAGER_URL}',\n VITE_BACKEND_API_URL: '${BACKEND_API_URL}',\n },\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n };\n }\n\n if (config.services.sampleCrm) {\n services['sample-crm'] = {\n image: `${registry}/habeetat-sample-crm:${tag}`,\n depends_on: ['backend'],\n environment: {\n VITE_LOGTO_ENDPOINT: '${LOGTO_ENDPOINT}',\n VITE_LOGTO_API_RESOURCE: '${IAM_AUDIENCE}',\n VITE_PLATFORM_API_URL: '${BACKEND_API_URL}',\n VITE_APP_BASE_URL: '${SAMPLE_CRM_URL}',\n },\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n };\n }\n\n // Nginx reverse proxy\n const nginxDependsOn = ['logto-core', 'backend'];\n if (config.services.launcher) nginxDependsOn.push('launcher');\n if (config.services.organizationManager) nginxDependsOn.push('organization-manager');\n if (config.services.platformManager) nginxDependsOn.push('platform-manager');\n if (config.services.sampleCrm) nginxDependsOn.push('sample-crm');\n\n const nginxVolumes = ['./nginx/platform.conf:/etc/nginx/conf.d/default.conf:ro'];\n const nginxPorts = ['80:80'];\n\n if (config.platform.protocol === 'https') {\n nginxVolumes.push('./certbot/conf:/etc/letsencrypt:ro');\n nginxVolumes.push('./certbot/www:/var/www/certbot:ro');\n nginxPorts.push('443:443');\n }\n\n services['nginx'] = {\n image: THIRD_PARTY_IMAGES.nginx,\n depends_on: nginxDependsOn,\n volumes: nginxVolumes,\n ports: nginxPorts,\n networks: ['habeetat-network'],\n restart: 'unless-stopped',\n command: `/bin/sh -c 'while :; do sleep 6h & wait $\\${!}; nginx -s reload; done & nginx -g \"daemon off;\"'`,\n };\n\n if (config.platform.protocol === 'https') {\n services['certbot'] = {\n image: THIRD_PARTY_IMAGES.certbot,\n volumes: [\n './certbot/conf:/etc/letsencrypt',\n './certbot/www:/var/www/certbot',\n ],\n entrypoint: `/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $\\${!}; done;'`,\n networks: ['habeetat-network'],\n };\n }\n\n const compose = {\n name: 'habeetat',\n services,\n volumes: {\n 'logto-db-data': null,\n 'platform-db-data': null,\n },\n networks: {\n 'habeetat-network': {\n driver: 'bridge',\n },\n },\n };\n\n return toYaml(compose);\n}\n\n// Minimal YAML serializer — avoids adding a dependency for simple structure\nfunction toYaml(obj: unknown, indent = 0): string {\n const pad = ' '.repeat(indent);\n\n if (obj === null || obj === undefined) return '';\n if (typeof obj === 'string') {\n if (obj.includes('\\n') || obj.includes(\"'\") || obj.includes('\"') || obj.includes(':') || obj.includes('#') || obj.includes('{') || obj.includes('}') || obj.includes('$')) {\n return `'${obj.replace(/'/g, \"''\")}'`;\n }\n return obj;\n }\n if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj);\n\n if (Array.isArray(obj)) {\n if (obj.length === 0) return '[]';\n // Check if all items are scalars\n const allScalar = obj.every(\n (item) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean',\n );\n if (allScalar) {\n return obj\n .map((item) => `\\n${pad}- ${toYaml(item, indent + 2)}`)\n .join('');\n }\n return obj\n .map((item) => {\n const val = toYaml(item, indent + 2);\n if (typeof item === 'object' && item !== null && !Array.isArray(item)) {\n // Object inside array — inline the first key after the dash\n const lines = val.split('\\n').filter((l) => l.trim());\n if (lines.length > 0) {\n const first = lines[0].trimStart();\n const rest = lines.slice(1).map((l) => `${pad} ${l.trimStart()}`).join('\\n');\n return `\\n${pad}- ${first}${rest ? '\\n' + rest : ''}`;\n }\n }\n return `\\n${pad}- ${val}`;\n })\n .join('');\n }\n\n if (typeof obj === 'object') {\n const entries = Object.entries(obj as Record<string, unknown>);\n if (entries.length === 0) return '{}';\n return entries\n .map(([key, value]) => {\n if (value === null || value === undefined) return `${pad}${key}:`;\n if (typeof value === 'object' && !Array.isArray(value)) {\n const nested = toYaml(value, indent + 2);\n return `${pad}${key}:\\n${nested}`;\n }\n if (Array.isArray(value)) {\n const nested = toYaml(value, indent + 2);\n return `${pad}${key}:${nested}`;\n }\n return `${pad}${key}: ${toYaml(value, indent)}`;\n })\n .join('\\n');\n }\n\n return String(obj);\n}\n","import type { HabeetatConfig } from '../types.js';\n\nexport function generateNginxConf(config: HabeetatConfig): string {\n const domain = config.platform.domain;\n const ssl = config.platform.protocol === 'https';\n\n const upstreams = [\n 'upstream logto { server logto-core:3001; }',\n 'upstream logto_admin { server logto-core:3002; }',\n 'upstream api { server backend:3001; }',\n ];\n\n if (config.services.launcher) upstreams.push('upstream launcher { server launcher:80; }');\n if (config.services.organizationManager) upstreams.push('upstream organization-manager { server organization-manager:80; }');\n if (config.services.platformManager) upstreams.push('upstream platform-manager { server platform-manager:80; }');\n if (config.services.sampleCrm) upstreams.push('upstream sample-crm { server sample-crm:80; }');\n\n const proxyHeaders = `\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;`;\n\n const sslBlock = ssl\n ? `\n ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;\n ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;\n include /etc/letsencrypt/options-ssl-nginx.conf;\n ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;`\n : '';\n\n const listenDirective = ssl ? 'listen 443 ssl' : 'listen 80';\n\n const serverBlocks: string[] = [];\n\n // HTTP → HTTPS redirect (only if SSL)\n if (ssl) {\n serverBlocks.push(`\nserver {\n listen 80;\n server_name ${domain} *.${domain};\n location /.well-known/acme-challenge/ { root /var/www/certbot; }\n location / { return 301 https://$host$request_uri; }\n}`);\n }\n\n // Logto IAM\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name iam.${domain};${sslBlock}\n location / {\n proxy_pass http://logto;${proxyHeaders}\n }\n}`);\n\n // Logto Admin Console\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name iam-console.${domain};${sslBlock}\n location / {\n proxy_pass http://logto_admin;${proxyHeaders}\n }\n}`);\n\n // Backend API\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name api.${domain};${sslBlock}\n location / {\n limit_req zone=api_limit burst=20 nodelay;\n proxy_pass http://api;${proxyHeaders}\n }\n}`);\n\n // Launcher\n if (config.services.launcher) {\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name launcher.${domain};${sslBlock}\n location / {\n proxy_pass http://launcher;${proxyHeaders}\n }\n}`);\n }\n\n // Organization Manager\n if (config.services.organizationManager) {\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name organization-manager.${domain};${sslBlock}\n location / {\n proxy_pass http://organization-manager;${proxyHeaders}\n }\n}`);\n }\n\n // Platform Manager\n if (config.services.platformManager) {\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name platform-manager.${domain};${sslBlock}\n location / {\n proxy_pass http://platform-manager;${proxyHeaders}\n }\n}`);\n }\n\n // Sample CRM\n if (config.services.sampleCrm) {\n serverBlocks.push(`\nserver {\n ${listenDirective};\n server_name sample-crm.${domain};${sslBlock}\n location / {\n proxy_pass http://sample-crm;${proxyHeaders}\n }\n}`);\n }\n\n return `# Habeetat Platform - Nginx Configuration\n# Auto-generated by @habeetat/cli for domain: ${domain}\n\n${upstreams.join('\\n')}\n\nlimit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;\n${serverBlocks.join('\\n')}\n`;\n}\n","import crypto from 'node:crypto';\n\nexport function randomHex(bytes: number): string {\n return crypto.randomBytes(bytes).toString('hex');\n}\n\nexport function generateSecrets() {\n return {\n logtoDbPassword: randomHex(16),\n platformDbPassword: randomHex(16),\n encryptionKey: randomHex(32),\n logtoM2mAppSecret: randomHex(32),\n };\n}\n","import type { CreateOptions } from '../types.js';\nimport { generateSecrets } from '../utils/crypto.js';\n\nexport function generateEnvFile(opts: CreateOptions): string {\n const protocol = opts.ssl ? 'https' : 'http';\n const domain = opts.domain;\n const secrets = generateSecrets();\n\n return `# Habeetat Platform - Environment Configuration\n# Generated by @habeetat/cli on ${new Date().toISOString()}\n\n# ============================================\n# Domain Configuration\n# ============================================\nDOMAIN=${domain}\nPROTOCOL=${protocol}\n\n# ============================================\n# Service URLs (External - Browser-facing)\n# ============================================\nLOGTO_ENDPOINT=${protocol}://iam.${domain}\nLOGTO_ADMIN_ENDPOINT=${protocol}://iam-console.${domain}\nLAUNCHER_URL=${protocol}://launcher.${domain}\nORG_MANAGER_URL=${protocol}://organization-manager.${domain}\nPLATFORM_MANAGER_URL=${protocol}://platform-manager.${domain}\nSAMPLE_CRM_URL=${protocol}://sample-crm.${domain}\nBACKEND_API_URL=${protocol}://api.${domain}\nCORS_ORIGINS=${protocol}://launcher.${domain},${protocol}://organization-manager.${domain},${protocol}://platform-manager.${domain},${protocol}://sample-crm.${domain}\n\n# ============================================\n# Database Configuration\n# ============================================\nLOGTO_DB_USER=logto\nLOGTO_DB_PASSWORD=${secrets.logtoDbPassword}\nLOGTO_DB_NAME=logto\n\nPLATFORM_DB_USER=habeetat\nPLATFORM_DB_PASSWORD=${secrets.platformDbPassword}\nPLATFORM_DB_NAME=habeetat_platform\n\n# ============================================\n# Logto IAM Configuration\n# ============================================\nLOGTO_ADMIN_USERNAME=${opts.adminEmail}\nLOGTO_ADMIN_PASSWORD=${opts.adminPassword}\n\nIAM_ISSUER_URL=${protocol}://iam.${domain}/oidc\nIAM_AUDIENCE=${protocol}://api.${domain}/api\nSDK_API_RESOURCE=${protocol}://api.${domain}/api\n\nLOGTO_M2M_APP_ID=nhp-m2m-config\nLOGTO_M2M_APP_SECRET=${secrets.logtoM2mAppSecret}\n\n# ============================================\n# Platform Configuration\n# ============================================\nPLATFORM_ADMIN_EMAIL=${opts.adminEmail}\nPLATFORM_ADMIN_PASSWORD=${opts.adminPassword}\nORGANIZATION_NAME=${opts.organizationName}\n\n# ============================================\n# Security\n# ============================================\nENCRYPTION_KEY=${secrets.encryptionKey}\n\n# ============================================\n# CORS Configuration\n# ============================================\nALLOWED_ORIGINS=${protocol}://iam-console.${domain},${protocol}://launcher.${domain},${protocol}://organization-manager.${domain},${protocol}://platform-manager.${domain}\nCORS_ALLOWED_ORIGINS=${protocol}://iam-console.${domain},${protocol}://launcher.${domain},${protocol}://organization-manager.${domain},${protocol}://platform-manager.${domain}\n`;\n}\n","import chalk from 'chalk';\n\nconst prefix = chalk.cyan('[habeetat]');\n\nexport const logger = {\n info: (msg: string) => console.log(`${prefix} ${msg}`),\n success: (msg: string) => console.log(`${prefix} ${chalk.green('✓')} ${msg}`),\n warn: (msg: string) => console.log(`${prefix} ${chalk.yellow('⚠')} ${msg}`),\n error: (msg: string) => console.error(`${prefix} ${chalk.red('✗')} ${msg}`),\n phase: (msg: string) => {\n console.log('');\n console.log(chalk.cyan('═'.repeat(56)));\n console.log(chalk.cyan(` ${msg}`));\n console.log(chalk.cyan('═'.repeat(56)));\n console.log('');\n },\n banner: () => {\n console.log('');\n console.log(chalk.cyan('╔══════════════════════════════════════════════╗'));\n console.log(chalk.cyan('║ ║'));\n console.log(chalk.cyan('║ Habeetat Platform CLI ║'));\n console.log(chalk.cyan('║ ║'));\n console.log(chalk.cyan('╚══════════════════════════════════════════════╝'));\n console.log('');\n },\n};\n","import { execaCommand } from 'execa';\nimport { logger } from './logger.js';\n\nexport async function checkDocker(): Promise<boolean> {\n try {\n const { stdout } = await execaCommand('docker --version');\n const match = stdout.match(/Docker version (\\d+)/);\n if (match && parseInt(match[1], 10) >= 20) {\n return true;\n }\n logger.error(`Docker >= 20.0 required. Found: ${stdout.trim()}`);\n return false;\n } catch {\n logger.error('Docker is not installed or not in PATH');\n logger.info('Install Docker: https://docs.docker.com/get-docker/');\n return false;\n }\n}\n\nexport async function checkDockerCompose(): Promise<boolean> {\n try {\n await execaCommand('docker compose version');\n return true;\n } catch {\n logger.error('Docker Compose V2 is not available');\n logger.info('Docker Compose V2 is included with Docker Desktop');\n return false;\n }\n}\n\nexport async function checkPrerequisites(): Promise<boolean> {\n const docker = await checkDocker();\n const compose = await checkDockerCompose();\n return docker && compose;\n}\n\nexport interface ComposeExecOptions {\n cwd: string;\n composeFile?: string;\n silent?: boolean;\n}\n\nexport async function composeUp(opts: ComposeExecOptions): Promise<void> {\n const file = opts.composeFile || 'docker-compose.yml';\n await execaCommand(`docker compose -f ${file} up -d`, {\n cwd: opts.cwd,\n stdio: opts.silent ? 'pipe' : 'inherit',\n });\n}\n\nexport async function composeDown(opts: ComposeExecOptions): Promise<void> {\n const file = opts.composeFile || 'docker-compose.yml';\n await execaCommand(`docker compose -f ${file} down`, {\n cwd: opts.cwd,\n stdio: opts.silent ? 'pipe' : 'inherit',\n });\n}\n\nexport async function composePs(opts: ComposeExecOptions): Promise<string> {\n const file = opts.composeFile || 'docker-compose.yml';\n const { stdout } = await execaCommand(`docker compose -f ${file} ps`, {\n cwd: opts.cwd,\n });\n return stdout;\n}\n\nexport async function composeLogs(\n opts: ComposeExecOptions & { service?: string; follow?: boolean; tail?: number },\n): Promise<void> {\n const file = opts.composeFile || 'docker-compose.yml';\n const parts = ['docker', 'compose', '-f', file, 'logs'];\n if (opts.follow) parts.push('-f');\n if (opts.tail) parts.push('--tail', String(opts.tail));\n if (opts.service) parts.push(opts.service);\n\n await execaCommand(parts.join(' '), {\n cwd: opts.cwd,\n stdio: 'inherit',\n });\n}\n\nexport async function composeRestart(\n opts: ComposeExecOptions & { service?: string },\n): Promise<void> {\n const file = opts.composeFile || 'docker-compose.yml';\n const cmd = opts.service\n ? `docker compose -f ${file} restart ${opts.service}`\n : `docker compose -f ${file} restart`;\n\n await execaCommand(cmd, {\n cwd: opts.cwd,\n stdio: opts.silent ? 'pipe' : 'inherit',\n });\n}\n\nexport async function composeExec(\n opts: ComposeExecOptions & { service: string; command: string },\n): Promise<string> {\n const file = opts.composeFile || 'docker-compose.yml';\n const { stdout } = await execaCommand(\n `docker compose -f ${file} exec -T ${opts.service} ${opts.command}`,\n { cwd: opts.cwd },\n );\n return stdout;\n}\n\nexport async function dockerPull(image: string): Promise<void> {\n await execaCommand(`docker pull ${image}`, { stdio: 'inherit' });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_FILE } from '../constants.js';\nimport type { HabeetatConfig } from '../types.js';\n\nexport function findProjectRoot(startDir: string = process.cwd()): string | null {\n let dir = startDir;\n while (dir !== path.dirname(dir)) {\n if (fs.existsSync(path.join(dir, CONFIG_FILE))) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n return null;\n}\n\nexport function loadConfig(projectDir?: string): HabeetatConfig {\n const dir = projectDir || findProjectRoot();\n if (!dir) {\n throw new Error(\n `Could not find ${CONFIG_FILE}. Are you inside a Habeetat project directory?`,\n );\n }\n const configPath = path.join(dir, CONFIG_FILE);\n const raw = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(raw) as HabeetatConfig;\n}\n\nexport function saveConfig(config: HabeetatConfig, projectDir: string): void {\n const configPath = path.join(projectDir, CONFIG_FILE);\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function getProjectDir(): string {\n const dir = findProjectRoot();\n if (!dir) {\n throw new Error(\n `Could not find ${CONFIG_FILE}. Are you inside a Habeetat project directory?`,\n );\n }\n return dir;\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { execaCommand } from 'execa';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
// src/constants.ts
|
|
8
|
+
var CONFIG_FILE = "habeetat.json";
|
|
9
|
+
var ENV_FILE = ".env";
|
|
10
|
+
var COMPOSE_FILE = "docker-compose.yml";
|
|
11
|
+
var DOCKER_REGISTRY = "docker.io/capriisland";
|
|
12
|
+
var IMAGES = {
|
|
13
|
+
backend: "capriisland/habeetat-backend",
|
|
14
|
+
launcher: "capriisland/habeetat-launcher",
|
|
15
|
+
orgManager: "capriisland/habeetat-org-manager",
|
|
16
|
+
platformManager: "capriisland/habeetat-platform-manager",
|
|
17
|
+
sampleCrm: "capriisland/habeetat-sample-crm",
|
|
18
|
+
logtoConfig: "capriisland/habeetat-logto-config"
|
|
19
|
+
};
|
|
20
|
+
var THIRD_PARTY_IMAGES = {
|
|
21
|
+
postgres: "postgres:15-alpine",
|
|
22
|
+
logto: "svhd/logto:1.33",
|
|
23
|
+
nginx: "nginx:1.25-alpine",
|
|
24
|
+
certbot: "certbot/certbot"
|
|
25
|
+
};
|
|
26
|
+
var SERVICES = [
|
|
27
|
+
"logto-db",
|
|
28
|
+
"platform-db",
|
|
29
|
+
"logto-core",
|
|
30
|
+
"backend",
|
|
31
|
+
"launcher",
|
|
32
|
+
"organization-manager",
|
|
33
|
+
"platform-manager",
|
|
34
|
+
"sample-crm",
|
|
35
|
+
"nginx"
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// src/templates/docker-compose.ts
|
|
39
|
+
function generateDockerCompose(config) {
|
|
40
|
+
const tag = config.docker.imageTag;
|
|
41
|
+
const registry = config.docker.registry;
|
|
42
|
+
const services = {
|
|
43
|
+
"logto-db": {
|
|
44
|
+
image: THIRD_PARTY_IMAGES.postgres,
|
|
45
|
+
environment: {
|
|
46
|
+
POSTGRES_USER: "${LOGTO_DB_USER}",
|
|
47
|
+
POSTGRES_PASSWORD: "${LOGTO_DB_PASSWORD}",
|
|
48
|
+
POSTGRES_DB: "${LOGTO_DB_NAME}"
|
|
49
|
+
},
|
|
50
|
+
volumes: ["logto-db-data:/var/lib/postgresql/data"],
|
|
51
|
+
networks: ["habeetat-network"],
|
|
52
|
+
restart: "unless-stopped",
|
|
53
|
+
healthcheck: {
|
|
54
|
+
test: ["CMD-SHELL", "pg_isready -U ${LOGTO_DB_USER} -d ${LOGTO_DB_NAME}"],
|
|
55
|
+
interval: "5s",
|
|
56
|
+
timeout: "3s",
|
|
57
|
+
retries: 10
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"platform-db": {
|
|
61
|
+
image: THIRD_PARTY_IMAGES.postgres,
|
|
62
|
+
environment: {
|
|
63
|
+
POSTGRES_USER: "${PLATFORM_DB_USER}",
|
|
64
|
+
POSTGRES_PASSWORD: "${PLATFORM_DB_PASSWORD}",
|
|
65
|
+
POSTGRES_DB: "${PLATFORM_DB_NAME}"
|
|
66
|
+
},
|
|
67
|
+
volumes: ["platform-db-data:/var/lib/postgresql/data"],
|
|
68
|
+
networks: ["habeetat-network"],
|
|
69
|
+
restart: "unless-stopped",
|
|
70
|
+
healthcheck: {
|
|
71
|
+
test: ["CMD-SHELL", "pg_isready -U ${PLATFORM_DB_USER} -d ${PLATFORM_DB_NAME}"],
|
|
72
|
+
interval: "5s",
|
|
73
|
+
timeout: "3s",
|
|
74
|
+
retries: 10
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"logto-core": {
|
|
78
|
+
image: THIRD_PARTY_IMAGES.logto,
|
|
79
|
+
entrypoint: ["sh", "-c"],
|
|
80
|
+
command: ["npm run cli -- db seed -- --swe || true\nnpm run start"],
|
|
81
|
+
depends_on: {
|
|
82
|
+
"logto-db": { condition: "service_healthy" }
|
|
83
|
+
},
|
|
84
|
+
environment: {
|
|
85
|
+
DB_URL: "postgres://${LOGTO_DB_USER}:${LOGTO_DB_PASSWORD}@logto-db:5432/${LOGTO_DB_NAME}",
|
|
86
|
+
ENDPOINT: "${LOGTO_ENDPOINT}",
|
|
87
|
+
ADMIN_ENDPOINT: "${LOGTO_ADMIN_ENDPOINT}",
|
|
88
|
+
ADMIN_CONSOLE_URL: "${LOGTO_ADMIN_ENDPOINT}",
|
|
89
|
+
ADMIN_PORT: "3002",
|
|
90
|
+
TRUST_PROXY_HEADER: "1",
|
|
91
|
+
COOKIE_KEYS: "${ENCRYPTION_KEY}",
|
|
92
|
+
ALLOWED_ORIGINS: "${LOGTO_ENDPOINT},${LOGTO_ADMIN_ENDPOINT},${LAUNCHER_URL},${ORG_MANAGER_URL},${PLATFORM_MANAGER_URL},${SAMPLE_CRM_URL},${BACKEND_API_URL}",
|
|
93
|
+
CORS_ALLOWED_ORIGINS: "${LOGTO_ENDPOINT},${LOGTO_ADMIN_ENDPOINT},${LAUNCHER_URL},${ORG_MANAGER_URL},${PLATFORM_MANAGER_URL},${SAMPLE_CRM_URL},${BACKEND_API_URL}",
|
|
94
|
+
ADMIN_CONSOLE_ALLOWED_ORIGINS: "${LOGTO_ADMIN_ENDPOINT}"
|
|
95
|
+
},
|
|
96
|
+
networks: ["habeetat-network"],
|
|
97
|
+
restart: "unless-stopped",
|
|
98
|
+
healthcheck: {
|
|
99
|
+
test: ["CMD-SHELL", "wget -q --spider http://localhost:3001/oidc/.well-known/openid-configuration || exit 1"],
|
|
100
|
+
interval: "10s",
|
|
101
|
+
timeout: "5s",
|
|
102
|
+
retries: 15,
|
|
103
|
+
start_period: "90s"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
backend: {
|
|
107
|
+
image: `${registry}/habeetat-backend:${tag}`,
|
|
108
|
+
depends_on: {
|
|
109
|
+
"logto-core": { condition: "service_healthy" },
|
|
110
|
+
"platform-db": { condition: "service_healthy" }
|
|
111
|
+
},
|
|
112
|
+
environment: {
|
|
113
|
+
NODE_ENV: "production",
|
|
114
|
+
DATABASE_URL: "postgres://${PLATFORM_DB_USER}:${PLATFORM_DB_PASSWORD}@platform-db:5432/${PLATFORM_DB_NAME}",
|
|
115
|
+
IAM_ISSUER_URL: "${LOGTO_ENDPOINT}/oidc",
|
|
116
|
+
IAM_JWKS_URL: "http://logto-core:3001",
|
|
117
|
+
IAM_AUDIENCE: "${IAM_AUDIENCE}",
|
|
118
|
+
LOGTO_ENDPOINT: "http://logto-core:3001",
|
|
119
|
+
LOGTO_EXTERNAL_ENDPOINT: "${LOGTO_ENDPOINT}",
|
|
120
|
+
SDK_API_RESOURCE: "${IAM_AUDIENCE}",
|
|
121
|
+
LOGTO_M2M_APP_ID: "nhp-m2m-config",
|
|
122
|
+
LOGTO_M2M_APP_SECRET: "${LOGTO_M2M_APP_SECRET}",
|
|
123
|
+
ENCRYPTION_KEY: "${ENCRYPTION_KEY}",
|
|
124
|
+
LAUNCHER_URL: "${LAUNCHER_URL}",
|
|
125
|
+
CORS_ORIGINS: "${CORS_ORIGINS}"
|
|
126
|
+
},
|
|
127
|
+
networks: ["habeetat-network"],
|
|
128
|
+
restart: "unless-stopped",
|
|
129
|
+
healthcheck: {
|
|
130
|
+
test: ["CMD", "curl", "-f", "http://localhost:3001/platform/status"],
|
|
131
|
+
interval: "30s",
|
|
132
|
+
timeout: "10s",
|
|
133
|
+
retries: 3,
|
|
134
|
+
start_period: "60s"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
if (config.services.launcher) {
|
|
139
|
+
services["launcher"] = {
|
|
140
|
+
image: `${registry}/habeetat-launcher:${tag}`,
|
|
141
|
+
depends_on: ["backend"],
|
|
142
|
+
environment: {
|
|
143
|
+
VITE_IAM_LOGTO_ENDPOINT: "${LOGTO_ENDPOINT}",
|
|
144
|
+
VITE_IAM_AUDIENCE: "${IAM_AUDIENCE}",
|
|
145
|
+
VITE_IAM_APP_ID: "nhp-launcher",
|
|
146
|
+
VITE_APP_BASE_URL: "${LAUNCHER_URL}",
|
|
147
|
+
VITE_PLATFORM_API_URL: "${BACKEND_API_URL}"
|
|
148
|
+
},
|
|
149
|
+
networks: ["habeetat-network"],
|
|
150
|
+
restart: "unless-stopped"
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (config.services.organizationManager) {
|
|
154
|
+
services["organization-manager"] = {
|
|
155
|
+
image: `${registry}/habeetat-org-manager:${tag}`,
|
|
156
|
+
depends_on: ["backend"],
|
|
157
|
+
environment: {
|
|
158
|
+
VITE_LOGTO_ENDPOINT: "${LOGTO_ENDPOINT}",
|
|
159
|
+
VITE_LOGTO_APP_ID: "nhp-org-manager",
|
|
160
|
+
VITE_LOGTO_API_RESOURCE: "${IAM_AUDIENCE}",
|
|
161
|
+
VITE_APP_BASE_URL: "${ORG_MANAGER_URL}",
|
|
162
|
+
VITE_BACKEND_API_URL: "${BACKEND_API_URL}"
|
|
163
|
+
},
|
|
164
|
+
networks: ["habeetat-network"],
|
|
165
|
+
restart: "unless-stopped"
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (config.services.platformManager) {
|
|
169
|
+
services["platform-manager"] = {
|
|
170
|
+
image: `${registry}/habeetat-platform-manager:${tag}`,
|
|
171
|
+
depends_on: ["backend"],
|
|
172
|
+
environment: {
|
|
173
|
+
VITE_LOGTO_ENDPOINT: "${LOGTO_ENDPOINT}",
|
|
174
|
+
VITE_LOGTO_APP_ID: "nhp-plat-mgr",
|
|
175
|
+
VITE_LOGTO_API_RESOURCE: "${IAM_AUDIENCE}",
|
|
176
|
+
VITE_APP_BASE_URL: "${PLATFORM_MANAGER_URL}",
|
|
177
|
+
VITE_BACKEND_API_URL: "${BACKEND_API_URL}"
|
|
178
|
+
},
|
|
179
|
+
networks: ["habeetat-network"],
|
|
180
|
+
restart: "unless-stopped"
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (config.services.sampleCrm) {
|
|
184
|
+
services["sample-crm"] = {
|
|
185
|
+
image: `${registry}/habeetat-sample-crm:${tag}`,
|
|
186
|
+
depends_on: ["backend"],
|
|
187
|
+
environment: {
|
|
188
|
+
VITE_LOGTO_ENDPOINT: "${LOGTO_ENDPOINT}",
|
|
189
|
+
VITE_LOGTO_API_RESOURCE: "${IAM_AUDIENCE}",
|
|
190
|
+
VITE_PLATFORM_API_URL: "${BACKEND_API_URL}",
|
|
191
|
+
VITE_APP_BASE_URL: "${SAMPLE_CRM_URL}"
|
|
192
|
+
},
|
|
193
|
+
networks: ["habeetat-network"],
|
|
194
|
+
restart: "unless-stopped"
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const nginxDependsOn = ["logto-core", "backend"];
|
|
198
|
+
if (config.services.launcher) nginxDependsOn.push("launcher");
|
|
199
|
+
if (config.services.organizationManager) nginxDependsOn.push("organization-manager");
|
|
200
|
+
if (config.services.platformManager) nginxDependsOn.push("platform-manager");
|
|
201
|
+
if (config.services.sampleCrm) nginxDependsOn.push("sample-crm");
|
|
202
|
+
const nginxVolumes = ["./nginx/platform.conf:/etc/nginx/conf.d/default.conf:ro"];
|
|
203
|
+
const nginxPorts = ["80:80"];
|
|
204
|
+
if (config.platform.protocol === "https") {
|
|
205
|
+
nginxVolumes.push("./certbot/conf:/etc/letsencrypt:ro");
|
|
206
|
+
nginxVolumes.push("./certbot/www:/var/www/certbot:ro");
|
|
207
|
+
nginxPorts.push("443:443");
|
|
208
|
+
}
|
|
209
|
+
services["nginx"] = {
|
|
210
|
+
image: THIRD_PARTY_IMAGES.nginx,
|
|
211
|
+
depends_on: nginxDependsOn,
|
|
212
|
+
volumes: nginxVolumes,
|
|
213
|
+
ports: nginxPorts,
|
|
214
|
+
networks: ["habeetat-network"],
|
|
215
|
+
restart: "unless-stopped",
|
|
216
|
+
command: `/bin/sh -c 'while :; do sleep 6h & wait $\${!}; nginx -s reload; done & nginx -g "daemon off;"'`
|
|
217
|
+
};
|
|
218
|
+
if (config.platform.protocol === "https") {
|
|
219
|
+
services["certbot"] = {
|
|
220
|
+
image: THIRD_PARTY_IMAGES.certbot,
|
|
221
|
+
volumes: [
|
|
222
|
+
"./certbot/conf:/etc/letsencrypt",
|
|
223
|
+
"./certbot/www:/var/www/certbot"
|
|
224
|
+
],
|
|
225
|
+
entrypoint: `/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $\${!}; done;'`,
|
|
226
|
+
networks: ["habeetat-network"]
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const compose = {
|
|
230
|
+
name: "habeetat",
|
|
231
|
+
services,
|
|
232
|
+
volumes: {
|
|
233
|
+
"logto-db-data": null,
|
|
234
|
+
"platform-db-data": null
|
|
235
|
+
},
|
|
236
|
+
networks: {
|
|
237
|
+
"habeetat-network": {
|
|
238
|
+
driver: "bridge"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
return toYaml(compose);
|
|
243
|
+
}
|
|
244
|
+
function toYaml(obj, indent = 0) {
|
|
245
|
+
const pad = " ".repeat(indent);
|
|
246
|
+
if (obj === null || obj === void 0) return "";
|
|
247
|
+
if (typeof obj === "string") {
|
|
248
|
+
if (obj.includes("\n") || obj.includes("'") || obj.includes('"') || obj.includes(":") || obj.includes("#") || obj.includes("{") || obj.includes("}") || obj.includes("$")) {
|
|
249
|
+
return `'${obj.replace(/'/g, "''")}'`;
|
|
250
|
+
}
|
|
251
|
+
return obj;
|
|
252
|
+
}
|
|
253
|
+
if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
|
|
254
|
+
if (Array.isArray(obj)) {
|
|
255
|
+
if (obj.length === 0) return "[]";
|
|
256
|
+
const allScalar = obj.every(
|
|
257
|
+
(item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean"
|
|
258
|
+
);
|
|
259
|
+
if (allScalar) {
|
|
260
|
+
return obj.map((item) => `
|
|
261
|
+
${pad}- ${toYaml(item, indent + 2)}`).join("");
|
|
262
|
+
}
|
|
263
|
+
return obj.map((item) => {
|
|
264
|
+
const val = toYaml(item, indent + 2);
|
|
265
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
266
|
+
const lines = val.split("\n").filter((l) => l.trim());
|
|
267
|
+
if (lines.length > 0) {
|
|
268
|
+
const first = lines[0].trimStart();
|
|
269
|
+
const rest = lines.slice(1).map((l) => `${pad} ${l.trimStart()}`).join("\n");
|
|
270
|
+
return `
|
|
271
|
+
${pad}- ${first}${rest ? "\n" + rest : ""}`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return `
|
|
275
|
+
${pad}- ${val}`;
|
|
276
|
+
}).join("");
|
|
277
|
+
}
|
|
278
|
+
if (typeof obj === "object") {
|
|
279
|
+
const entries = Object.entries(obj);
|
|
280
|
+
if (entries.length === 0) return "{}";
|
|
281
|
+
return entries.map(([key, value]) => {
|
|
282
|
+
if (value === null || value === void 0) return `${pad}${key}:`;
|
|
283
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
284
|
+
const nested = toYaml(value, indent + 2);
|
|
285
|
+
return `${pad}${key}:
|
|
286
|
+
${nested}`;
|
|
287
|
+
}
|
|
288
|
+
if (Array.isArray(value)) {
|
|
289
|
+
const nested = toYaml(value, indent + 2);
|
|
290
|
+
return `${pad}${key}:${nested}`;
|
|
291
|
+
}
|
|
292
|
+
return `${pad}${key}: ${toYaml(value, indent)}`;
|
|
293
|
+
}).join("\n");
|
|
294
|
+
}
|
|
295
|
+
return String(obj);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/templates/nginx.ts
|
|
299
|
+
function generateNginxConf(config) {
|
|
300
|
+
const domain = config.platform.domain;
|
|
301
|
+
const ssl = config.platform.protocol === "https";
|
|
302
|
+
const upstreams = [
|
|
303
|
+
"upstream logto { server logto-core:3001; }",
|
|
304
|
+
"upstream logto_admin { server logto-core:3002; }",
|
|
305
|
+
"upstream api { server backend:3001; }"
|
|
306
|
+
];
|
|
307
|
+
if (config.services.launcher) upstreams.push("upstream launcher { server launcher:80; }");
|
|
308
|
+
if (config.services.organizationManager) upstreams.push("upstream organization-manager { server organization-manager:80; }");
|
|
309
|
+
if (config.services.platformManager) upstreams.push("upstream platform-manager { server platform-manager:80; }");
|
|
310
|
+
if (config.services.sampleCrm) upstreams.push("upstream sample-crm { server sample-crm:80; }");
|
|
311
|
+
const proxyHeaders = `
|
|
312
|
+
proxy_set_header Host $host;
|
|
313
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
314
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
315
|
+
proxy_set_header X-Forwarded-Proto $scheme;`;
|
|
316
|
+
const sslBlock = ssl ? `
|
|
317
|
+
ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;
|
|
318
|
+
ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;
|
|
319
|
+
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
320
|
+
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;` : "";
|
|
321
|
+
const listenDirective = ssl ? "listen 443 ssl" : "listen 80";
|
|
322
|
+
const serverBlocks = [];
|
|
323
|
+
if (ssl) {
|
|
324
|
+
serverBlocks.push(`
|
|
325
|
+
server {
|
|
326
|
+
listen 80;
|
|
327
|
+
server_name ${domain} *.${domain};
|
|
328
|
+
location /.well-known/acme-challenge/ { root /var/www/certbot; }
|
|
329
|
+
location / { return 301 https://$host$request_uri; }
|
|
330
|
+
}`);
|
|
331
|
+
}
|
|
332
|
+
serverBlocks.push(`
|
|
333
|
+
server {
|
|
334
|
+
${listenDirective};
|
|
335
|
+
server_name iam.${domain};${sslBlock}
|
|
336
|
+
location / {
|
|
337
|
+
proxy_pass http://logto;${proxyHeaders}
|
|
338
|
+
}
|
|
339
|
+
}`);
|
|
340
|
+
serverBlocks.push(`
|
|
341
|
+
server {
|
|
342
|
+
${listenDirective};
|
|
343
|
+
server_name iam-console.${domain};${sslBlock}
|
|
344
|
+
location / {
|
|
345
|
+
proxy_pass http://logto_admin;${proxyHeaders}
|
|
346
|
+
}
|
|
347
|
+
}`);
|
|
348
|
+
serverBlocks.push(`
|
|
349
|
+
server {
|
|
350
|
+
${listenDirective};
|
|
351
|
+
server_name api.${domain};${sslBlock}
|
|
352
|
+
location / {
|
|
353
|
+
limit_req zone=api_limit burst=20 nodelay;
|
|
354
|
+
proxy_pass http://api;${proxyHeaders}
|
|
355
|
+
}
|
|
356
|
+
}`);
|
|
357
|
+
if (config.services.launcher) {
|
|
358
|
+
serverBlocks.push(`
|
|
359
|
+
server {
|
|
360
|
+
${listenDirective};
|
|
361
|
+
server_name launcher.${domain};${sslBlock}
|
|
362
|
+
location / {
|
|
363
|
+
proxy_pass http://launcher;${proxyHeaders}
|
|
364
|
+
}
|
|
365
|
+
}`);
|
|
366
|
+
}
|
|
367
|
+
if (config.services.organizationManager) {
|
|
368
|
+
serverBlocks.push(`
|
|
369
|
+
server {
|
|
370
|
+
${listenDirective};
|
|
371
|
+
server_name organization-manager.${domain};${sslBlock}
|
|
372
|
+
location / {
|
|
373
|
+
proxy_pass http://organization-manager;${proxyHeaders}
|
|
374
|
+
}
|
|
375
|
+
}`);
|
|
376
|
+
}
|
|
377
|
+
if (config.services.platformManager) {
|
|
378
|
+
serverBlocks.push(`
|
|
379
|
+
server {
|
|
380
|
+
${listenDirective};
|
|
381
|
+
server_name platform-manager.${domain};${sslBlock}
|
|
382
|
+
location / {
|
|
383
|
+
proxy_pass http://platform-manager;${proxyHeaders}
|
|
384
|
+
}
|
|
385
|
+
}`);
|
|
386
|
+
}
|
|
387
|
+
if (config.services.sampleCrm) {
|
|
388
|
+
serverBlocks.push(`
|
|
389
|
+
server {
|
|
390
|
+
${listenDirective};
|
|
391
|
+
server_name sample-crm.${domain};${sslBlock}
|
|
392
|
+
location / {
|
|
393
|
+
proxy_pass http://sample-crm;${proxyHeaders}
|
|
394
|
+
}
|
|
395
|
+
}`);
|
|
396
|
+
}
|
|
397
|
+
return `# Habeetat Platform - Nginx Configuration
|
|
398
|
+
# Auto-generated by @habeetat/cli for domain: ${domain}
|
|
399
|
+
|
|
400
|
+
${upstreams.join("\n")}
|
|
401
|
+
|
|
402
|
+
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
403
|
+
${serverBlocks.join("\n")}
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
406
|
+
function randomHex(bytes) {
|
|
407
|
+
return crypto.randomBytes(bytes).toString("hex");
|
|
408
|
+
}
|
|
409
|
+
function generateSecrets() {
|
|
410
|
+
return {
|
|
411
|
+
logtoDbPassword: randomHex(16),
|
|
412
|
+
platformDbPassword: randomHex(16),
|
|
413
|
+
encryptionKey: randomHex(32),
|
|
414
|
+
logtoM2mAppSecret: randomHex(32)
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/templates/env.ts
|
|
419
|
+
function generateEnvFile(opts) {
|
|
420
|
+
const protocol = opts.ssl ? "https" : "http";
|
|
421
|
+
const domain = opts.domain;
|
|
422
|
+
const secrets = generateSecrets();
|
|
423
|
+
return `# Habeetat Platform - Environment Configuration
|
|
424
|
+
# Generated by @habeetat/cli on ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
425
|
+
|
|
426
|
+
# ============================================
|
|
427
|
+
# Domain Configuration
|
|
428
|
+
# ============================================
|
|
429
|
+
DOMAIN=${domain}
|
|
430
|
+
PROTOCOL=${protocol}
|
|
431
|
+
|
|
432
|
+
# ============================================
|
|
433
|
+
# Service URLs (External - Browser-facing)
|
|
434
|
+
# ============================================
|
|
435
|
+
LOGTO_ENDPOINT=${protocol}://iam.${domain}
|
|
436
|
+
LOGTO_ADMIN_ENDPOINT=${protocol}://iam-console.${domain}
|
|
437
|
+
LAUNCHER_URL=${protocol}://launcher.${domain}
|
|
438
|
+
ORG_MANAGER_URL=${protocol}://organization-manager.${domain}
|
|
439
|
+
PLATFORM_MANAGER_URL=${protocol}://platform-manager.${domain}
|
|
440
|
+
SAMPLE_CRM_URL=${protocol}://sample-crm.${domain}
|
|
441
|
+
BACKEND_API_URL=${protocol}://api.${domain}
|
|
442
|
+
CORS_ORIGINS=${protocol}://launcher.${domain},${protocol}://organization-manager.${domain},${protocol}://platform-manager.${domain},${protocol}://sample-crm.${domain}
|
|
443
|
+
|
|
444
|
+
# ============================================
|
|
445
|
+
# Database Configuration
|
|
446
|
+
# ============================================
|
|
447
|
+
LOGTO_DB_USER=logto
|
|
448
|
+
LOGTO_DB_PASSWORD=${secrets.logtoDbPassword}
|
|
449
|
+
LOGTO_DB_NAME=logto
|
|
450
|
+
|
|
451
|
+
PLATFORM_DB_USER=habeetat
|
|
452
|
+
PLATFORM_DB_PASSWORD=${secrets.platformDbPassword}
|
|
453
|
+
PLATFORM_DB_NAME=habeetat_platform
|
|
454
|
+
|
|
455
|
+
# ============================================
|
|
456
|
+
# Logto IAM Configuration
|
|
457
|
+
# ============================================
|
|
458
|
+
LOGTO_ADMIN_USERNAME=${opts.adminEmail}
|
|
459
|
+
LOGTO_ADMIN_PASSWORD=${opts.adminPassword}
|
|
460
|
+
|
|
461
|
+
IAM_ISSUER_URL=${protocol}://iam.${domain}/oidc
|
|
462
|
+
IAM_AUDIENCE=${protocol}://api.${domain}/api
|
|
463
|
+
SDK_API_RESOURCE=${protocol}://api.${domain}/api
|
|
464
|
+
|
|
465
|
+
LOGTO_M2M_APP_ID=nhp-m2m-config
|
|
466
|
+
LOGTO_M2M_APP_SECRET=${secrets.logtoM2mAppSecret}
|
|
467
|
+
|
|
468
|
+
# ============================================
|
|
469
|
+
# Platform Configuration
|
|
470
|
+
# ============================================
|
|
471
|
+
PLATFORM_ADMIN_EMAIL=${opts.adminEmail}
|
|
472
|
+
PLATFORM_ADMIN_PASSWORD=${opts.adminPassword}
|
|
473
|
+
ORGANIZATION_NAME=${opts.organizationName}
|
|
474
|
+
|
|
475
|
+
# ============================================
|
|
476
|
+
# Security
|
|
477
|
+
# ============================================
|
|
478
|
+
ENCRYPTION_KEY=${secrets.encryptionKey}
|
|
479
|
+
|
|
480
|
+
# ============================================
|
|
481
|
+
# CORS Configuration
|
|
482
|
+
# ============================================
|
|
483
|
+
ALLOWED_ORIGINS=${protocol}://iam-console.${domain},${protocol}://launcher.${domain},${protocol}://organization-manager.${domain},${protocol}://platform-manager.${domain}
|
|
484
|
+
CORS_ALLOWED_ORIGINS=${protocol}://iam-console.${domain},${protocol}://launcher.${domain},${protocol}://organization-manager.${domain},${protocol}://platform-manager.${domain}
|
|
485
|
+
`;
|
|
486
|
+
}
|
|
487
|
+
var prefix = chalk.cyan("[habeetat]");
|
|
488
|
+
var logger = {
|
|
489
|
+
info: (msg) => console.log(`${prefix} ${msg}`),
|
|
490
|
+
success: (msg) => console.log(`${prefix} ${chalk.green("\u2713")} ${msg}`),
|
|
491
|
+
warn: (msg) => console.log(`${prefix} ${chalk.yellow("\u26A0")} ${msg}`),
|
|
492
|
+
error: (msg) => console.error(`${prefix} ${chalk.red("\u2717")} ${msg}`),
|
|
493
|
+
phase: (msg) => {
|
|
494
|
+
console.log("");
|
|
495
|
+
console.log(chalk.cyan("\u2550".repeat(56)));
|
|
496
|
+
console.log(chalk.cyan(` ${msg}`));
|
|
497
|
+
console.log(chalk.cyan("\u2550".repeat(56)));
|
|
498
|
+
console.log("");
|
|
499
|
+
},
|
|
500
|
+
banner: () => {
|
|
501
|
+
console.log("");
|
|
502
|
+
console.log(chalk.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
503
|
+
console.log(chalk.cyan("\u2551 \u2551"));
|
|
504
|
+
console.log(chalk.cyan("\u2551 Habeetat Platform CLI \u2551"));
|
|
505
|
+
console.log(chalk.cyan("\u2551 \u2551"));
|
|
506
|
+
console.log(chalk.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
507
|
+
console.log("");
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
async function checkDocker() {
|
|
511
|
+
try {
|
|
512
|
+
const { stdout } = await execaCommand("docker --version");
|
|
513
|
+
const match = stdout.match(/Docker version (\d+)/);
|
|
514
|
+
if (match && parseInt(match[1], 10) >= 20) {
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
logger.error(`Docker >= 20.0 required. Found: ${stdout.trim()}`);
|
|
518
|
+
return false;
|
|
519
|
+
} catch {
|
|
520
|
+
logger.error("Docker is not installed or not in PATH");
|
|
521
|
+
logger.info("Install Docker: https://docs.docker.com/get-docker/");
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async function checkDockerCompose() {
|
|
526
|
+
try {
|
|
527
|
+
await execaCommand("docker compose version");
|
|
528
|
+
return true;
|
|
529
|
+
} catch {
|
|
530
|
+
logger.error("Docker Compose V2 is not available");
|
|
531
|
+
logger.info("Docker Compose V2 is included with Docker Desktop");
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function checkPrerequisites() {
|
|
536
|
+
const docker = await checkDocker();
|
|
537
|
+
const compose = await checkDockerCompose();
|
|
538
|
+
return docker && compose;
|
|
539
|
+
}
|
|
540
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
541
|
+
let dir = startDir;
|
|
542
|
+
while (dir !== path.dirname(dir)) {
|
|
543
|
+
if (fs.existsSync(path.join(dir, CONFIG_FILE))) {
|
|
544
|
+
return dir;
|
|
545
|
+
}
|
|
546
|
+
dir = path.dirname(dir);
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
function loadConfig(projectDir) {
|
|
551
|
+
const dir = projectDir || findProjectRoot();
|
|
552
|
+
if (!dir) {
|
|
553
|
+
throw new Error(
|
|
554
|
+
`Could not find ${CONFIG_FILE}. Are you inside a Habeetat project directory?`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
const configPath = path.join(dir, CONFIG_FILE);
|
|
558
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
559
|
+
return JSON.parse(raw);
|
|
560
|
+
}
|
|
561
|
+
function saveConfig(config, projectDir) {
|
|
562
|
+
const configPath = path.join(projectDir, CONFIG_FILE);
|
|
563
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export { COMPOSE_FILE, CONFIG_FILE, DOCKER_REGISTRY, ENV_FILE, IMAGES, SERVICES, THIRD_PARTY_IMAGES, checkPrerequisites, findProjectRoot, generateDockerCompose, generateEnvFile, generateNginxConf, generateSecrets, loadConfig, logger, saveConfig };
|
|
567
|
+
//# sourceMappingURL=index.mjs.map
|
|
568
|
+
//# sourceMappingURL=index.mjs.map
|