suthep 0.1.0-beta.1 → 0.2.0-beta.1

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.
Files changed (69) hide show
  1. package/README.md +167 -69
  2. package/dist/commands/down.js +179 -0
  3. package/dist/commands/down.js.map +1 -0
  4. package/dist/commands/redeploy.js +59 -0
  5. package/dist/commands/redeploy.js.map +1 -0
  6. package/dist/commands/up.js +213 -0
  7. package/dist/commands/up.js.map +1 -0
  8. package/dist/index.js +28 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/utils/deployment.js +10 -1
  11. package/dist/utils/deployment.js.map +1 -1
  12. package/dist/utils/docker.js +35 -0
  13. package/dist/utils/docker.js.map +1 -1
  14. package/dist/utils/nginx.js +10 -0
  15. package/dist/utils/nginx.js.map +1 -1
  16. package/docs/README.md +25 -82
  17. package/docs/english/01-introduction.md +84 -0
  18. package/docs/english/02-installation.md +200 -0
  19. package/docs/english/03-quick-start.md +256 -0
  20. package/docs/english/04-configuration.md +358 -0
  21. package/docs/english/05-commands.md +363 -0
  22. package/docs/english/06-examples.md +456 -0
  23. package/docs/english/07-troubleshooting.md +417 -0
  24. package/docs/english/08-advanced.md +411 -0
  25. package/docs/english/README.md +48 -0
  26. package/docs/thai/01-introduction.md +84 -0
  27. package/docs/thai/02-installation.md +200 -0
  28. package/docs/thai/03-quick-start.md +256 -0
  29. package/docs/thai/04-configuration.md +358 -0
  30. package/docs/thai/05-commands.md +363 -0
  31. package/docs/thai/06-examples.md +456 -0
  32. package/docs/thai/07-troubleshooting.md +417 -0
  33. package/docs/thai/08-advanced.md +411 -0
  34. package/docs/thai/README.md +48 -0
  35. package/example/README.md +282 -53
  36. package/example/suthep-complete.yml +103 -0
  37. package/example/suthep-docker-only.yml +71 -0
  38. package/example/suthep-no-docker.yml +51 -0
  39. package/example/{muacle.yml → suthep-path-routing.yml} +20 -5
  40. package/example/suthep.example.yml +89 -0
  41. package/package.json +1 -1
  42. package/src/commands/down.ts +240 -0
  43. package/src/commands/redeploy.ts +78 -0
  44. package/src/commands/up.ts +271 -0
  45. package/src/index.ts +53 -0
  46. package/suthep-0.1.0-beta.1.tgz +0 -0
  47. package/suthep.example.yml +5 -0
  48. package/suthep.yml +39 -0
  49. package/docs/TRANSLATIONS.md +0 -211
  50. package/docs/en/README.md +0 -76
  51. package/docs/en/api-reference.md +0 -545
  52. package/docs/en/architecture.md +0 -369
  53. package/docs/en/commands.md +0 -273
  54. package/docs/en/configuration.md +0 -347
  55. package/docs/en/developer-guide.md +0 -588
  56. package/docs/en/docker-ports-config.md +0 -333
  57. package/docs/en/examples.md +0 -537
  58. package/docs/en/getting-started.md +0 -202
  59. package/docs/en/port-binding.md +0 -268
  60. package/docs/en/troubleshooting.md +0 -441
  61. package/docs/th/README.md +0 -64
  62. package/docs/th/commands.md +0 -202
  63. package/docs/th/configuration.md +0 -325
  64. package/docs/th/getting-started.md +0 -203
  65. package/example/docker-compose.yml +0 -76
  66. package/example/docker-ports-example.yml +0 -81
  67. package/example/port-binding-example.yml +0 -45
  68. package/example/suthep.yml +0 -46
  69. package/example/suthep=1.yml +0 -46
@@ -1 +1 @@
1
- {"version":3,"file":"nginx.js","sources":["../../src/utils/nginx.ts"],"sourcesContent":["import { execa } from 'execa'\nimport fs from 'fs-extra'\nimport path from 'path'\nimport type { ServiceConfig } from '../types/config'\n\n/**\n * Generate Nginx server block configuration for a service\n */\nexport function generateNginxConfig(\n service: ServiceConfig,\n withHttps: boolean,\n portOverride?: number\n): string {\n const serverNames = service.domains.join(' ')\n // Use primary domain for upstream naming to ensure uniqueness\n const primaryDomain = service.domains[0]\n const domainSafe = primaryDomain.replace(/\\./g, '_').replace(/[^a-zA-Z0-9_]/g, '_')\n const upstreamName = `${domainSafe}_${service.name}`\n const servicePath = service.path || '/'\n const port = portOverride || service.port\n\n let config = `# Nginx configuration for ${service.name}\\n\\n`\n\n // Upstream configuration\n config += `upstream ${upstreamName} {\\n`\n config += ` server localhost:${port} max_fails=3 fail_timeout=30s;\\n`\n config += ` keepalive 32;\\n`\n config += `}\\n\\n`\n\n if (withHttps) {\n // HTTP server - redirect to HTTPS\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${serverNames};\\n\\n`\n config += ` # Redirect all HTTP to HTTPS\\n`\n config += ` return 301 https://$server_name$request_uri;\\n`\n config += `}\\n\\n`\n\n // HTTPS server\n config += `server {\\n`\n config += ` listen 443 ssl http2;\\n`\n config += ` listen [::]:443 ssl http2;\\n`\n config += ` server_name ${serverNames};\\n\\n`\n\n // SSL configuration\n const primaryDomain = service.domains[0]\n config += ` # SSL Configuration\\n`\n config += ` ssl_certificate /etc/letsencrypt/live/${primaryDomain}/fullchain.pem;\\n`\n config += ` ssl_certificate_key /etc/letsencrypt/live/${primaryDomain}/privkey.pem;\\n`\n config += ` ssl_protocols TLSv1.2 TLSv1.3;\\n`\n config += ` ssl_ciphers HIGH:!aNULL:!MD5;\\n`\n config += ` ssl_prefer_server_ciphers on;\\n\\n`\n } else {\n // HTTP only server\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${serverNames};\\n\\n`\n }\n\n // Logging\n config += ` # Logging\\n`\n config += ` access_log /var/log/nginx/${service.name}_access.log;\\n`\n config += ` error_log /var/log/nginx/${service.name}_error.log;\\n\\n`\n\n // Client settings\n config += ` # Client settings\\n`\n config += ` client_max_body_size 100M;\\n\\n`\n\n // Proxy settings\n config += ` # Proxy settings\\n`\n config += ` location ${servicePath} {\\n`\n config += ` proxy_pass http://${upstreamName};\\n`\n config += ` proxy_http_version 1.1;\\n`\n config += ` proxy_set_header Upgrade $http_upgrade;\\n`\n config += ` proxy_set_header Connection 'upgrade';\\n`\n config += ` proxy_set_header Host $host;\\n`\n config += ` proxy_set_header X-Real-IP $remote_addr;\\n`\n config += ` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n`\n config += ` proxy_set_header X-Forwarded-Proto $scheme;\\n`\n config += ` proxy_cache_bypass $http_upgrade;\\n`\n config += ` proxy_connect_timeout 60s;\\n`\n config += ` proxy_send_timeout 60s;\\n`\n config += ` proxy_read_timeout 60s;\\n`\n config += ` }\\n`\n\n // Health check endpoint (if configured)\n if (service.healthCheck) {\n config += `\\n # Health check endpoint\\n`\n config += ` location ${service.healthCheck.path} {\\n`\n config += ` proxy_pass http://${upstreamName};\\n`\n config += ` access_log off;\\n`\n config += ` }\\n`\n }\n\n config += `}\\n`\n\n return config\n}\n\n/**\n * Generate Nginx configuration for multiple services on the same domain\n * Groups services by domain and creates location blocks for each service path\n */\nexport function generateMultiServiceNginxConfig(\n services: ServiceConfig[],\n domain: string,\n withHttps: boolean,\n portOverrides?: Map<string, number>\n): string {\n const upstreams: string[] = []\n const locations: string[] = []\n const healthChecks: string[] = []\n\n // Sort services by path length (longest first) to ensure specific paths are matched before general ones\n const sortedServices = [...services].sort((a, b) => {\n const pathA = (a.path || '/').length\n const pathB = (b.path || '/').length\n return pathB - pathA\n })\n\n // Track upstream names to ensure uniqueness within the same domain config\n const usedUpstreamNames = new Set<string>()\n\n for (const service of sortedServices) {\n // Create unique upstream name: domain_service_name to avoid conflicts\n // Replace dots and special chars in domain for valid nginx upstream name\n const domainSafe = domain.replace(/\\./g, '_').replace(/[^a-zA-Z0-9_]/g, '_')\n let upstreamName = `${domainSafe}_${service.name}`\n\n // Ensure uniqueness (in case same service name appears multiple times)\n let counter = 1\n const originalUpstreamName = upstreamName\n while (usedUpstreamNames.has(upstreamName)) {\n upstreamName = `${originalUpstreamName}_${counter}`\n counter++\n }\n usedUpstreamNames.add(upstreamName)\n\n const servicePath = service.path || '/'\n const port = portOverrides?.get(service.name) || service.port\n\n // Generate upstream for each service on the same domain\n upstreams.push(`upstream ${upstreamName} {`)\n upstreams.push(` server localhost:${port} max_fails=3 fail_timeout=30s;`)\n upstreams.push(` keepalive 32;`)\n upstreams.push(`}`)\n\n // Generate location block\n if (servicePath === '/') {\n // Root path - use exact match or default\n locations.push(` # Service: ${service.name}`)\n locations.push(` location / {`)\n } else {\n // Specific path - use prefix match\n locations.push(` # Service: ${service.name}`)\n locations.push(` location ${servicePath} {`)\n }\n\n locations.push(` proxy_pass http://${upstreamName};`)\n locations.push(` proxy_http_version 1.1;`)\n locations.push(` proxy_set_header Upgrade $http_upgrade;`)\n locations.push(` proxy_set_header Connection 'upgrade';`)\n locations.push(` proxy_set_header Host $host;`)\n locations.push(` proxy_set_header X-Real-IP $remote_addr;`)\n locations.push(` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`)\n locations.push(` proxy_set_header X-Forwarded-Proto $scheme;`)\n locations.push(` proxy_cache_bypass $http_upgrade;`)\n locations.push(` proxy_connect_timeout 60s;`)\n locations.push(` proxy_send_timeout 60s;`)\n locations.push(` proxy_read_timeout 60s;`)\n locations.push(` }`)\n\n // Health check endpoint\n if (service.healthCheck) {\n healthChecks.push(` # Health check for ${service.name}`)\n healthChecks.push(` location ${service.healthCheck.path} {`)\n healthChecks.push(` proxy_pass http://${upstreamName};`)\n healthChecks.push(` access_log off;`)\n healthChecks.push(` }`)\n }\n }\n\n let config = `# Nginx configuration for ${domain}\\n`\n config += `# Multiple services on the same domain\\n\\n`\n\n // Add upstreams\n config += upstreams.join('\\n') + '\\n\\n'\n\n if (withHttps) {\n // HTTP server - redirect to HTTPS\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${domain};\\n\\n`\n config += ` # Redirect all HTTP to HTTPS\\n`\n config += ` return 301 https://$server_name$request_uri;\\n`\n config += `}\\n\\n`\n\n // HTTPS server\n config += `server {\\n`\n config += ` listen 443 ssl http2;\\n`\n config += ` listen [::]:443 ssl http2;\\n`\n config += ` server_name ${domain};\\n\\n`\n\n // SSL configuration\n config += ` # SSL Configuration\\n`\n config += ` ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;\\n`\n config += ` ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;\\n`\n config += ` ssl_protocols TLSv1.2 TLSv1.3;\\n`\n config += ` ssl_ciphers HIGH:!aNULL:!MD5;\\n`\n config += ` ssl_prefer_server_ciphers on;\\n\\n`\n } else {\n // HTTP only server\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${domain};\\n\\n`\n }\n\n // Logging\n config += ` # Logging\\n`\n config += ` access_log /var/log/nginx/${domain}_access.log;\\n`\n config += ` error_log /var/log/nginx/${domain}_error.log;\\n\\n`\n\n // Client settings\n config += ` # Client settings\\n`\n config += ` client_max_body_size 100M;\\n\\n`\n\n // Location blocks\n config += ` # Service locations\\n`\n config += locations.join('\\n') + '\\n\\n'\n\n // Health check endpoints\n if (healthChecks.length > 0) {\n config += ` # Health check endpoints\\n`\n config += healthChecks.join('\\n') + '\\n'\n }\n\n config += `}\\n`\n\n return config\n}\n\n/**\n * Check if an Nginx config file exists\n */\nexport async function configExists(configName: string, configPath: string): Promise<boolean> {\n const configFilePath = path.join(configPath, `${configName}.conf`)\n return await fs.pathExists(configFilePath)\n}\n\n/**\n * Write Nginx configuration file, deleting existing file if it exists and creating new one\n */\nexport async function writeNginxConfig(\n configName: string,\n configPath: string,\n configContent: string\n): Promise<boolean> {\n const configFilePath = path.join(configPath, `${configName}.conf`)\n const exists = await fs.pathExists(configFilePath)\n\n // If config file exists, delete it first\n if (exists) {\n await fs.remove(configFilePath)\n }\n\n // Create new config file\n await fs.writeFile(configFilePath, configContent)\n\n return exists // Return true if file existed (was deleted and recreated)\n}\n\n/**\n * Enable an Nginx site by creating a symbolic link\n */\nexport async function enableSite(siteName: string, configPath: string): Promise<void> {\n const availablePath = path.join(configPath, `${siteName}.conf`)\n const enabledPath = availablePath.replace('sites-available', 'sites-enabled')\n\n // Create sites-enabled directory if it doesn't exist\n await fs.ensureDir(path.dirname(enabledPath))\n\n // Remove existing symlink if present\n if (await fs.pathExists(enabledPath)) {\n await fs.remove(enabledPath)\n }\n\n // Create symlink\n await execa('sudo', ['ln', '-sf', availablePath, enabledPath])\n}\n\n/**\n * Test and reload Nginx configuration\n */\nexport async function reloadNginx(reloadCommand: string): Promise<void> {\n try {\n // Test configuration first\n await execa('sudo', ['nginx', '-t'])\n\n // Reload Nginx\n const parts = reloadCommand.split(' ')\n if (parts.length > 0) {\n // Simple execution of provided command\n await execa(parts[0], parts.slice(1), { shell: true })\n }\n } catch (error) {\n throw new Error(`Failed to reload Nginx: ${error instanceof Error ? error.message : error}`)\n }\n}\n\n/**\n * Disable an Nginx site\n */\nexport async function disableSite(siteName: string, configPath: string): Promise<void> {\n const enabledPath = path.join(\n configPath.replace('sites-available', 'sites-enabled'),\n `${siteName}.conf`\n )\n\n if (await fs.pathExists(enabledPath)) {\n await fs.remove(enabledPath)\n }\n}\n"],"names":["primaryDomain"],"mappings":"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"}
1
+ {"version":3,"file":"nginx.js","sources":["../../src/utils/nginx.ts"],"sourcesContent":["import { execa } from 'execa'\nimport fs from 'fs-extra'\nimport path from 'path'\nimport type { ServiceConfig } from '../types/config'\n\n/**\n * Generate Nginx server block configuration for a service\n */\nexport function generateNginxConfig(\n service: ServiceConfig,\n withHttps: boolean,\n portOverride?: number\n): string {\n const serverNames = service.domains.join(' ')\n // Use primary domain for upstream naming to ensure uniqueness\n const primaryDomain = service.domains[0]\n const domainSafe = primaryDomain.replace(/\\./g, '_').replace(/[^a-zA-Z0-9_]/g, '_')\n const upstreamName = `${domainSafe}_${service.name}`\n const servicePath = service.path || '/'\n const port = portOverride || service.port\n\n let config = `# Nginx configuration for ${service.name}\\n\\n`\n\n // Upstream configuration\n config += `upstream ${upstreamName} {\\n`\n config += ` server localhost:${port} max_fails=3 fail_timeout=30s;\\n`\n config += ` keepalive 32;\\n`\n config += `}\\n\\n`\n\n if (withHttps) {\n // HTTP server - redirect to HTTPS\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${serverNames};\\n\\n`\n config += ` # Redirect all HTTP to HTTPS\\n`\n config += ` return 301 https://$server_name$request_uri;\\n`\n config += `}\\n\\n`\n\n // HTTPS server\n config += `server {\\n`\n config += ` listen 443 ssl http2;\\n`\n config += ` listen [::]:443 ssl http2;\\n`\n config += ` server_name ${serverNames};\\n\\n`\n\n // SSL configuration\n const primaryDomain = service.domains[0]\n config += ` # SSL Configuration\\n`\n config += ` ssl_certificate /etc/letsencrypt/live/${primaryDomain}/fullchain.pem;\\n`\n config += ` ssl_certificate_key /etc/letsencrypt/live/${primaryDomain}/privkey.pem;\\n`\n config += ` ssl_protocols TLSv1.2 TLSv1.3;\\n`\n config += ` ssl_ciphers HIGH:!aNULL:!MD5;\\n`\n config += ` ssl_prefer_server_ciphers on;\\n\\n`\n } else {\n // HTTP only server\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${serverNames};\\n\\n`\n }\n\n // Logging\n config += ` # Logging\\n`\n config += ` access_log /var/log/nginx/${service.name}_access.log;\\n`\n config += ` error_log /var/log/nginx/${service.name}_error.log;\\n\\n`\n\n // Client settings\n config += ` # Client settings\\n`\n config += ` client_max_body_size 100M;\\n\\n`\n\n // Proxy settings\n config += ` # Proxy settings\\n`\n config += ` location ${servicePath} {\\n`\n config += ` proxy_pass http://${upstreamName};\\n`\n config += ` proxy_http_version 1.1;\\n`\n config += ` proxy_set_header Upgrade $http_upgrade;\\n`\n config += ` proxy_set_header Connection 'upgrade';\\n`\n config += ` proxy_set_header Host $host;\\n`\n config += ` proxy_set_header X-Real-IP $remote_addr;\\n`\n config += ` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n`\n config += ` proxy_set_header X-Forwarded-Proto $scheme;\\n`\n config += ` proxy_cache_bypass $http_upgrade;\\n`\n config += ` proxy_connect_timeout 60s;\\n`\n config += ` proxy_send_timeout 60s;\\n`\n config += ` proxy_read_timeout 60s;\\n`\n config += ` }\\n`\n\n // Health check endpoint (if configured)\n if (service.healthCheck) {\n config += `\\n # Health check endpoint\\n`\n config += ` location ${service.healthCheck.path} {\\n`\n config += ` proxy_pass http://${upstreamName};\\n`\n config += ` access_log off;\\n`\n config += ` }\\n`\n }\n\n config += `}\\n`\n\n return config\n}\n\n/**\n * Generate Nginx configuration for multiple services on the same domain\n * Groups services by domain and creates location blocks for each service path\n */\nexport function generateMultiServiceNginxConfig(\n services: ServiceConfig[],\n domain: string,\n withHttps: boolean,\n portOverrides?: Map<string, number>\n): string {\n const upstreams: string[] = []\n const locations: string[] = []\n const healthChecks: string[] = []\n\n // Sort services by path length (longest first) to ensure specific paths are matched before general ones\n const sortedServices = [...services].sort((a, b) => {\n const pathA = (a.path || '/').length\n const pathB = (b.path || '/').length\n return pathB - pathA\n })\n\n // Track upstream names to ensure uniqueness within the same domain config\n const usedUpstreamNames = new Set<string>()\n\n for (const service of sortedServices) {\n // Create unique upstream name: domain_service_name to avoid conflicts\n // Replace dots and special chars in domain for valid nginx upstream name\n const domainSafe = domain.replace(/\\./g, '_').replace(/[^a-zA-Z0-9_]/g, '_')\n let upstreamName = `${domainSafe}_${service.name}`\n\n // Ensure uniqueness (in case same service name appears multiple times)\n let counter = 1\n const originalUpstreamName = upstreamName\n while (usedUpstreamNames.has(upstreamName)) {\n upstreamName = `${originalUpstreamName}_${counter}`\n counter++\n }\n usedUpstreamNames.add(upstreamName)\n\n const servicePath = service.path || '/'\n const port = portOverrides?.get(service.name) || service.port\n\n // Generate upstream for each service on the same domain\n upstreams.push(`upstream ${upstreamName} {`)\n upstreams.push(` server localhost:${port} max_fails=3 fail_timeout=30s;`)\n upstreams.push(` keepalive 32;`)\n upstreams.push(`}`)\n\n // Generate location block\n if (servicePath === '/') {\n // Root path - use exact match or default\n locations.push(` # Service: ${service.name}`)\n locations.push(` location / {`)\n } else {\n // Specific path - use prefix match\n locations.push(` # Service: ${service.name}`)\n locations.push(` location ${servicePath} {`)\n }\n\n locations.push(` proxy_pass http://${upstreamName};`)\n locations.push(` proxy_http_version 1.1;`)\n locations.push(` proxy_set_header Upgrade $http_upgrade;`)\n locations.push(` proxy_set_header Connection 'upgrade';`)\n locations.push(` proxy_set_header Host $host;`)\n locations.push(` proxy_set_header X-Real-IP $remote_addr;`)\n locations.push(` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`)\n locations.push(` proxy_set_header X-Forwarded-Proto $scheme;`)\n locations.push(` proxy_cache_bypass $http_upgrade;`)\n locations.push(` proxy_connect_timeout 60s;`)\n locations.push(` proxy_send_timeout 60s;`)\n locations.push(` proxy_read_timeout 60s;`)\n locations.push(` }`)\n\n // Health check endpoint\n if (service.healthCheck) {\n healthChecks.push(` # Health check for ${service.name}`)\n healthChecks.push(` location ${service.healthCheck.path} {`)\n healthChecks.push(` proxy_pass http://${upstreamName};`)\n healthChecks.push(` access_log off;`)\n healthChecks.push(` }`)\n }\n }\n\n let config = `# Nginx configuration for ${domain}\\n`\n config += `# Multiple services on the same domain\\n\\n`\n\n // Add upstreams\n config += upstreams.join('\\n') + '\\n\\n'\n\n if (withHttps) {\n // HTTP server - redirect to HTTPS\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${domain};\\n\\n`\n config += ` # Redirect all HTTP to HTTPS\\n`\n config += ` return 301 https://$server_name$request_uri;\\n`\n config += `}\\n\\n`\n\n // HTTPS server\n config += `server {\\n`\n config += ` listen 443 ssl http2;\\n`\n config += ` listen [::]:443 ssl http2;\\n`\n config += ` server_name ${domain};\\n\\n`\n\n // SSL configuration\n config += ` # SSL Configuration\\n`\n config += ` ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;\\n`\n config += ` ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;\\n`\n config += ` ssl_protocols TLSv1.2 TLSv1.3;\\n`\n config += ` ssl_ciphers HIGH:!aNULL:!MD5;\\n`\n config += ` ssl_prefer_server_ciphers on;\\n\\n`\n } else {\n // HTTP only server\n config += `server {\\n`\n config += ` listen 80;\\n`\n config += ` listen [::]:80;\\n`\n config += ` server_name ${domain};\\n\\n`\n }\n\n // Logging\n config += ` # Logging\\n`\n config += ` access_log /var/log/nginx/${domain}_access.log;\\n`\n config += ` error_log /var/log/nginx/${domain}_error.log;\\n\\n`\n\n // Client settings\n config += ` # Client settings\\n`\n config += ` client_max_body_size 100M;\\n\\n`\n\n // Location blocks\n config += ` # Service locations\\n`\n config += locations.join('\\n') + '\\n\\n'\n\n // Health check endpoints\n if (healthChecks.length > 0) {\n config += ` # Health check endpoints\\n`\n config += healthChecks.join('\\n') + '\\n'\n }\n\n config += `}\\n`\n\n return config\n}\n\n/**\n * Check if an Nginx config file exists\n */\nexport async function configExists(configName: string, configPath: string): Promise<boolean> {\n const configFilePath = path.join(configPath, `${configName}.conf`)\n return await fs.pathExists(configFilePath)\n}\n\n/**\n * Write Nginx configuration file, deleting existing file if it exists and creating new one\n */\nexport async function writeNginxConfig(\n configName: string,\n configPath: string,\n configContent: string\n): Promise<boolean> {\n const configFilePath = path.join(configPath, `${configName}.conf`)\n const exists = await fs.pathExists(configFilePath)\n\n // If config file exists, delete it first\n if (exists) {\n await fs.remove(configFilePath)\n }\n\n // Create new config file\n await fs.writeFile(configFilePath, configContent)\n\n return exists // Return true if file existed (was deleted and recreated)\n}\n\n/**\n * Enable an Nginx site by creating a symbolic link\n */\nexport async function enableSite(siteName: string, configPath: string): Promise<void> {\n const availablePath = path.join(configPath, `${siteName}.conf`)\n const enabledPath = availablePath.replace('sites-available', 'sites-enabled')\n\n // Create sites-enabled directory if it doesn't exist\n await fs.ensureDir(path.dirname(enabledPath))\n\n // Remove existing symlink if present\n if (await fs.pathExists(enabledPath)) {\n await fs.remove(enabledPath)\n }\n\n // Create symlink\n await execa('sudo', ['ln', '-sf', availablePath, enabledPath])\n}\n\n/**\n * Test and reload Nginx configuration\n */\nexport async function reloadNginx(reloadCommand: string): Promise<void> {\n try {\n // Test configuration first\n await execa('sudo', ['nginx', '-t'])\n\n // Reload Nginx\n const parts = reloadCommand.split(' ')\n if (parts.length > 0) {\n // Simple execution of provided command\n await execa(parts[0], parts.slice(1), { shell: true })\n }\n } catch (error) {\n throw new Error(`Failed to reload Nginx: ${error instanceof Error ? error.message : error}`)\n }\n}\n\n/**\n * Disable an Nginx site\n */\nexport async function disableSite(siteName: string, configPath: string): Promise<void> {\n const enabledPath = path.join(\n configPath.replace('sites-available', 'sites-enabled'),\n `${siteName}.conf`\n )\n\n if (await fs.pathExists(enabledPath)) {\n await fs.remove(enabledPath)\n }\n}\n"],"names":["primaryDomain"],"mappings":"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"}
package/docs/README.md CHANGED
@@ -1,95 +1,38 @@
1
1
  # Suthep Documentation
2
2
 
3
- Welcome to the Suthep documentation! This guide will help you understand and use Suthep, a powerful CLI tool for deploying projects with automatic Nginx reverse proxy setup, HTTPS with Certbot, and zero-downtime deployments.
3
+ Welcome to the Suthep documentation! Choose your preferred language:
4
4
 
5
- ## Choose Your Language / เลือกภาษาของคุณ
5
+ ## Available Languages
6
6
 
7
- - 🇺🇸 [English Documentation](./en/) - English documentation
8
- - 🇹🇭 [เอกสารภาษาไทย](./th/) - Thai documentation
9
-
10
- ## Quick Navigation
11
-
12
- ### English / ภาษาอังกฤษ
13
-
14
- - [Getting Started](./en/getting-started.md) - Installation and quick start guide
15
- - [Configuration Reference](./en/configuration.md) - Complete configuration file reference
16
- - [Commands Reference](./en/commands.md) - Detailed command documentation
17
- - [Examples](./en/examples.md) - Real-world deployment examples
18
- - [Troubleshooting](./en/troubleshooting.md) - Common issues and solutions
19
- - [Developer Guide](./en/developer-guide.md) - Complete guide for developers
20
-
21
- ### ไทย / Thai
22
-
23
- - [เริ่มต้นใช้งาน](./th/getting-started.md) - คู่มือการติดตั้งและเริ่มต้นใช้งาน
24
- - [คู่มือการตั้งค่า](./th/configuration.md) - คู่มืออ้างอิงไฟล์ configuration ฉบับสมบูรณ์
25
- - [คู่มือคำสั่ง](./th/commands.md) - เอกสารคำสั่งแบบละเอียด
26
-
27
- ## What is Suthep?
28
-
29
- Suthep is a deployment automation tool that simplifies the process of deploying web services with:
30
-
31
- - ✅ **Automatic Nginx reverse proxy setup** - No manual Nginx configuration needed
32
- - ✅ **Automatic HTTPS with Certbot** - Free SSL certificates from Let's Encrypt
33
- - ✅ **Zero-downtime deployment** - Rolling and blue-green deployment strategies
34
- - ✅ **Docker container support** - Deploy containerized applications easily
35
- - ✅ **Multiple domain/subdomain support** - One service, multiple domains
36
- - ✅ **Health check integration** - Ensure services are healthy before going live
37
- - ✅ **YAML-based configuration** - Simple, declarative configuration
38
-
39
- ## Quick Start
40
-
41
- ```bash
42
- # Install dependencies
43
- npm install
44
- npm link
45
-
46
- # Initialize configuration
47
- suthep init
48
-
49
- # Setup prerequisites
50
- suthep setup
51
-
52
- # Deploy your services
53
- suthep deploy
54
- ```
55
-
56
- ## Requirements
57
-
58
- - Node.js 16+
59
- - Nginx (installed via `suthep setup`)
60
- - Certbot (installed via `suthep setup`)
61
- - Docker (optional, for Docker-based services)
62
- - sudo access (for Nginx and Certbot operations)
7
+ - 🇬🇧 [English Documentation](./english/README.md) - English user guide
8
+ - 🇹🇭 [Thai Documentation](./thai/README.md) - คู่มือผู้ใช้ภาษาไทย
63
9
 
64
10
  ## Documentation Structure
65
11
 
66
- ```
67
- docs/
68
- ├── README.md (this file)
69
- ├── en/ # English documentation
70
- │ ├── README.md
71
- │ ├── getting-started.md
72
- │ ├── configuration.md
73
- │ ├── commands.md
74
- │ └── ...
75
- └── th/ # Thai documentation
76
- ├── README.md
77
- ├── getting-started.md
78
- ├── configuration.md
79
- └── commands.md
80
- ```
12
+ Both language versions include:
13
+
14
+ 1. **Introduction** - Overview of Suthep and its features
15
+ 2. **Installation** - Step-by-step installation guide
16
+ 3. **Quick Start** - Get started in minutes
17
+ 4. **Configuration Guide** - Complete configuration reference
18
+ 5. **Commands Reference** - All available commands
19
+ 6. **Examples** - Real-world deployment examples
20
+ 7. **Troubleshooting** - Common issues and solutions
21
+ 8. **Advanced Topics** - Advanced configuration and optimization
81
22
 
82
- ## Contributing Translations
23
+ ## Quick Links
83
24
 
84
- We welcome contributions to translate documentation into more languages!
25
+ ### English
26
+ - [Start Here](./english/README.md)
27
+ - [Quick Start Guide](./english/03-quick-start.md)
28
+ - [Configuration Reference](./english/04-configuration.md)
85
29
 
86
- To contribute:
87
- 1. Create a new language folder (e.g., `ja/` for Japanese)
88
- 2. Translate the documentation files
89
- 3. Update this README with links to the new language
90
- 4. Submit a pull request
30
+ ### Thai
31
+ - [เริ่มต้นที่นี่](./thai/README.md)
32
+ - [คู่มือเริ่มต้นใช้งาน](./thai/03-quick-start.md)
33
+ - [คู่มือการตั้งค่า](./thai/04-configuration.md)
91
34
 
92
- ## License
35
+ ---
93
36
 
94
- [MIT](../LICENSE)
37
+ For the main project README, see [../README.md](../README.md)
95
38
 
@@ -0,0 +1,84 @@
1
+ # Introduction to Suthep
2
+
3
+ ## What is Suthep?
4
+
5
+ Suthep is a command-line tool designed to simplify the deployment of web applications and services. It automates the complex process of setting up reverse proxies, SSL certificates, and managing deployments with zero downtime.
6
+
7
+ ## Why Use Suthep?
8
+
9
+ ### Simplified Deployment
10
+
11
+ Traditional deployment processes require manual configuration of:
12
+ - Nginx reverse proxy rules
13
+ - SSL certificate management
14
+ - Docker container orchestration
15
+ - Health check monitoring
16
+ - Zero-downtime deployment strategies
17
+
18
+ Suthep handles all of this automatically with a simple YAML configuration file.
19
+
20
+ ### Key Benefits
21
+
22
+ 1. **Time Savings** - Deploy in minutes instead of hours
23
+ 2. **Reduced Errors** - Automated configuration reduces human error
24
+ 3. **Zero Downtime** - Rolling deployments ensure continuous service availability
25
+ 4. **Easy Management** - Simple commands to deploy, update, and manage services
26
+ 5. **Cost Effective** - Run multiple services on a single server efficiently
27
+
28
+ ## How It Works
29
+
30
+ Suthep follows a simple workflow:
31
+
32
+ 1. **Configure** - Create a `suthep.yml` configuration file
33
+ 2. **Setup** - Install prerequisites (Nginx, Certbot) with `suthep setup`
34
+ 3. **Deploy** - Deploy your services with `suthep deploy`
35
+
36
+ Behind the scenes, Suthep:
37
+ - Generates Nginx configuration files
38
+ - Obtains SSL certificates from Let's Encrypt
39
+ - Manages Docker containers
40
+ - Performs health checks
41
+ - Handles zero-downtime deployments
42
+
43
+ ## Use Cases
44
+
45
+ Suthep is ideal for:
46
+
47
+ - **Small to Medium Applications** - Deploy multiple services on a single server
48
+ - **Microservices** - Manage multiple services with different domains
49
+ - **Docker Applications** - Deploy containerized applications easily
50
+ - **API Services** - Set up reverse proxies for API endpoints
51
+ - **Web Applications** - Deploy web apps with automatic HTTPS
52
+
53
+ ## What You'll Learn
54
+
55
+ In this guide, you'll learn:
56
+
57
+ - How to install and set up Suthep
58
+ - How to create and configure deployment files
59
+ - How to use all available commands
60
+ - How to deploy different types of services
61
+ - How to troubleshoot common issues
62
+ - Advanced configuration options
63
+
64
+ ## Prerequisites
65
+
66
+ Before using Suthep, you should have:
67
+
68
+ - **Node.js 16+** installed
69
+ - **sudo/administrator access** on your server
70
+ - **Domain names** pointing to your server (for HTTPS)
71
+ - **Basic knowledge** of YAML configuration files
72
+ - **Docker** (optional, only if deploying Docker containers)
73
+
74
+ ## Next Steps
75
+
76
+ Ready to get started? Continue to:
77
+
78
+ - [Installation Guide](./02-installation.md) - Install Suthep on your system
79
+ - [Quick Start Guide](./03-quick-start.md) - Deploy your first service
80
+
81
+ ---
82
+
83
+ **Previous:** [README](./README.md) | **Next:** [Installation →](./02-installation.md)
84
+
@@ -0,0 +1,200 @@
1
+ # Installation Guide
2
+
3
+ This guide will walk you through installing Suthep on your system.
4
+
5
+ ## System Requirements
6
+
7
+ Before installing Suthep, ensure your system meets these requirements:
8
+
9
+ - **Node.js** version 16 or higher
10
+ - **npm**, **yarn**, or **pnpm** package manager
11
+ - **sudo/administrator privileges** (required for Nginx and Certbot setup)
12
+ - **Linux** or **macOS** operating system
13
+
14
+ ## Installing Suthep
15
+
16
+ Suthep can be installed globally using any Node.js package manager.
17
+
18
+ ### Using npm
19
+
20
+ ```bash
21
+ npm install -g suthep
22
+ ```
23
+
24
+ ### Using yarn
25
+
26
+ ```bash
27
+ yarn global add suthep
28
+ ```
29
+
30
+ ### Using pnpm
31
+
32
+ ```bash
33
+ pnpm add -g suthep
34
+ ```
35
+
36
+ ## Verify Installation
37
+
38
+ After installation, verify that Suthep is installed correctly:
39
+
40
+ ```bash
41
+ suthep --version
42
+ ```
43
+
44
+ You should see the version number (e.g., `0.1.0-beta.1`).
45
+
46
+ You can also check the help menu:
47
+
48
+ ```bash
49
+ suthep --help
50
+ ```
51
+
52
+ ## Installing Prerequisites
53
+
54
+ Suthep requires Nginx and Certbot to function. You can install these automatically using the `setup` command:
55
+
56
+ ```bash
57
+ suthep setup
58
+ ```
59
+
60
+ This command will:
61
+ - Install Nginx (if not already installed)
62
+ - Install Certbot (if not already installed)
63
+ - Configure system dependencies
64
+
65
+ ### Manual Installation (Optional)
66
+
67
+ If you prefer to install prerequisites manually:
68
+
69
+ #### Nginx Installation
70
+
71
+ **Ubuntu/Debian:**
72
+ ```bash
73
+ sudo apt-get update
74
+ sudo apt-get install -y nginx
75
+ ```
76
+
77
+ **CentOS/RHEL:**
78
+ ```bash
79
+ sudo yum install -y nginx
80
+ ```
81
+
82
+ **macOS:**
83
+ ```bash
84
+ brew install nginx
85
+ ```
86
+
87
+ #### Certbot Installation
88
+
89
+ **Ubuntu/Debian:**
90
+ ```bash
91
+ sudo apt-get update
92
+ sudo apt-get install -y certbot python3-certbot-nginx
93
+ ```
94
+
95
+ **CentOS/RHEL:**
96
+ ```bash
97
+ sudo yum install -y certbot python3-certbot-nginx
98
+ ```
99
+
100
+ **macOS:**
101
+ ```bash
102
+ brew install certbot
103
+ ```
104
+
105
+ ## Docker (Optional)
106
+
107
+ If you plan to deploy Docker containers, install Docker:
108
+
109
+ **Ubuntu/Debian:**
110
+ ```bash
111
+ sudo apt-get update
112
+ sudo apt-get install -y docker.io
113
+ sudo systemctl start docker
114
+ sudo systemctl enable docker
115
+ ```
116
+
117
+ **macOS:**
118
+ ```bash
119
+ brew install docker
120
+ ```
121
+
122
+ Or download Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop).
123
+
124
+ ## Post-Installation
125
+
126
+ After installation, you're ready to:
127
+
128
+ 1. **Initialize your first configuration:**
129
+ ```bash
130
+ suthep init
131
+ ```
132
+
133
+ 2. **Or continue to the Quick Start guide:**
134
+ See [Quick Start Guide](./03-quick-start.md)
135
+
136
+ ## Troubleshooting Installation
137
+
138
+ ### Command Not Found
139
+
140
+ If you get a "command not found" error:
141
+
142
+ 1. **Check Node.js installation:**
143
+ ```bash
144
+ node --version
145
+ npm --version
146
+ ```
147
+
148
+ 2. **Verify global bin path:**
149
+ ```bash
150
+ npm config get prefix
151
+ ```
152
+
153
+ 3. **Add npm global bin to PATH** (if needed):
154
+ ```bash
155
+ export PATH="$PATH:$(npm config get prefix)/bin"
156
+ ```
157
+
158
+ ### Permission Errors
159
+
160
+ If you encounter permission errors:
161
+
162
+ 1. **Use sudo for global installation:**
163
+ ```bash
164
+ sudo npm install -g suthep
165
+ ```
166
+
167
+ 2. **Or configure npm to use a different directory:**
168
+ ```bash
169
+ mkdir ~/.npm-global
170
+ npm config set prefix '~/.npm-global'
171
+ export PATH=~/.npm-global/bin:$PATH
172
+ ```
173
+
174
+ ### Nginx/Certbot Installation Issues
175
+
176
+ If `suthep setup` fails:
177
+
178
+ 1. **Check your package manager:**
179
+ - Ubuntu/Debian: Ensure `apt-get` is available
180
+ - CentOS/RHEL: Ensure `yum` is available
181
+ - macOS: Ensure Homebrew is installed
182
+
183
+ 2. **Run setup with verbose output:**
184
+ ```bash
185
+ suthep setup --verbose
186
+ ```
187
+
188
+ 3. **Install prerequisites manually** (see Manual Installation above)
189
+
190
+ ## Next Steps
191
+
192
+ Now that Suthep is installed:
193
+
194
+ - [Quick Start Guide](./03-quick-start.md) - Deploy your first service
195
+ - [Configuration Guide](./04-configuration.md) - Learn about configuration options
196
+
197
+ ---
198
+
199
+ **Previous:** [Introduction](./01-introduction.md) | **Next:** [Quick Start →](./03-quick-start.md)
200
+