@layr-labs/ecloud-cli 0.4.3-dev → 0.5.0-dev.2

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 (64) hide show
  1. package/VERSION +2 -2
  2. package/dist/commands/auth/login.js +84 -54
  3. package/dist/commands/auth/login.js.map +1 -1
  4. package/dist/commands/auth/migrate.js +66 -30
  5. package/dist/commands/auth/migrate.js.map +1 -1
  6. package/dist/commands/auth/whoami.js.map +1 -1
  7. package/dist/commands/billing/__tests__/status.test.js +9 -0
  8. package/dist/commands/billing/__tests__/status.test.js.map +1 -1
  9. package/dist/commands/billing/__tests__/subscribe.test.js +9 -0
  10. package/dist/commands/billing/__tests__/subscribe.test.js.map +1 -1
  11. package/dist/commands/billing/__tests__/top-up.test.js +9 -0
  12. package/dist/commands/billing/__tests__/top-up.test.js.map +1 -1
  13. package/dist/commands/billing/cancel.js +9 -0
  14. package/dist/commands/billing/cancel.js.map +1 -1
  15. package/dist/commands/billing/status.js +9 -0
  16. package/dist/commands/billing/status.js.map +1 -1
  17. package/dist/commands/billing/subscribe.js +9 -0
  18. package/dist/commands/billing/subscribe.js.map +1 -1
  19. package/dist/commands/billing/top-up.js +9 -0
  20. package/dist/commands/billing/top-up.js.map +1 -1
  21. package/dist/commands/compute/app/configure/tls.js +138 -50
  22. package/dist/commands/compute/app/configure/tls.js.map +1 -1
  23. package/dist/commands/compute/app/create.js.map +1 -1
  24. package/dist/commands/compute/app/deploy.js +71 -18
  25. package/dist/commands/compute/app/deploy.js.map +1 -1
  26. package/dist/commands/compute/app/info.js +15 -8
  27. package/dist/commands/compute/app/info.js.map +1 -1
  28. package/dist/commands/compute/app/list.js +10 -1
  29. package/dist/commands/compute/app/list.js.map +1 -1
  30. package/dist/commands/compute/app/logs.js +15 -8
  31. package/dist/commands/compute/app/logs.js.map +1 -1
  32. package/dist/commands/compute/app/profile/set.js +16 -8
  33. package/dist/commands/compute/app/profile/set.js.map +1 -1
  34. package/dist/commands/compute/app/releases.js +15 -8
  35. package/dist/commands/compute/app/releases.js.map +1 -1
  36. package/dist/commands/compute/app/start.js +27 -11
  37. package/dist/commands/compute/app/start.js.map +1 -1
  38. package/dist/commands/compute/app/stop.js +27 -11
  39. package/dist/commands/compute/app/stop.js.map +1 -1
  40. package/dist/commands/compute/app/terminate.js +20 -8
  41. package/dist/commands/compute/app/terminate.js.map +1 -1
  42. package/dist/commands/compute/app/upgrade.js +110 -21
  43. package/dist/commands/compute/app/upgrade.js.map +1 -1
  44. package/dist/commands/compute/build/info.js +10 -1
  45. package/dist/commands/compute/build/info.js.map +1 -1
  46. package/dist/commands/compute/build/list.js +10 -1
  47. package/dist/commands/compute/build/list.js.map +1 -1
  48. package/dist/commands/compute/build/logs.js +10 -1
  49. package/dist/commands/compute/build/logs.js.map +1 -1
  50. package/dist/commands/compute/build/status.js +10 -1
  51. package/dist/commands/compute/build/status.js.map +1 -1
  52. package/dist/commands/compute/build/submit.js +11 -1
  53. package/dist/commands/compute/build/submit.js.map +1 -1
  54. package/dist/commands/compute/build/verify.js +10 -1
  55. package/dist/commands/compute/build/verify.js.map +1 -1
  56. package/dist/commands/compute/environment/set.js +8 -0
  57. package/dist/commands/compute/environment/set.js.map +1 -1
  58. package/dist/commands/compute/undelegate.js +10 -1
  59. package/dist/commands/compute/undelegate.js.map +1 -1
  60. package/dist/hooks/init/__tests__/version-check.test.js +1 -1
  61. package/dist/hooks/init/__tests__/version-check.test.js.map +1 -1
  62. package/dist/hooks/init/version-check.js +1 -1
  63. package/dist/hooks/init/version-check.js.map +1 -1
  64. package/package.json +26 -21
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/commands/compute/app/configure/tls.ts
4
- import { Command } from "@oclif/core";
4
+ import { Command, Flags } from "@oclif/core";
5
5
  import * as fs from "fs";
6
6
  import * as path from "path";
7
7
  import chalk from "chalk";
8
+ import { input, confirm } from "@inquirer/prompts";
8
9
 
9
10
  // src/templates/tls/Caddyfile.tmpl
10
11
  var Caddyfile_default = `# Caddy configuration for automatic HTTPS
@@ -66,82 +67,169 @@ var Caddyfile_default = `# Caddy configuration for automatic HTTPS
66
67
  function getCaddyfileTemplate() {
67
68
  return Caddyfile_default;
68
69
  }
69
- var ENV_EXAMPLE_TLS = `# TLS Configuration
70
- # Set these variables to enable TLS for your application
71
-
72
- # Your domain name (required for TLS)
73
- DOMAIN=yourdomain.com
74
-
75
- # Port your application listens on
76
- APP_PORT=3000
77
-
78
- # Enable Caddy debug logs
79
- ENABLE_CADDY_LOGS=false
80
-
81
- # Use Let's Encrypt staging environment (for testing)
82
- # Set to true to avoid rate limits during development
83
- ACME_STAGING=false
84
-
85
- # Force certificate reissue even if a valid one exists
86
- # Useful when you need to update SANs or force a renewal
70
+ function getTlsEnvBlock(vars) {
71
+ return `
72
+ # TLS Configuration
73
+ DOMAIN=${vars.domain}
74
+ APP_PORT=${vars.appPort}
75
+ ENABLE_CADDY_LOGS=${vars.enableCaddyLogs}
76
+ ACME_STAGING=${vars.acmeStaging}
87
77
  ACME_FORCE_ISSUE=false
88
78
  `;
79
+ }
80
+ var TLS_ENV_EXAMPLE_BLOCK = `
81
+ # TLS Configuration
82
+ # DOMAIN=yourdomain.com
83
+ # APP_PORT=3000
84
+ # ENABLE_CADDY_LOGS=false
85
+ # ACME_STAGING=false
86
+ # ACME_FORCE_ISSUE=false
87
+ `;
89
88
 
90
89
  // src/commands/compute/app/configure/tls.ts
91
- var ConfigureTLS = class extends Command {
90
+ function envFileHasTlsConfig(filePath) {
91
+ if (!fs.existsSync(filePath)) return false;
92
+ const content = fs.readFileSync(filePath, "utf-8");
93
+ return /^DOMAIN=/m.test(content);
94
+ }
95
+ function validateDomain(value) {
96
+ const trimmed = value.trim();
97
+ if (!trimmed) return "Domain is required";
98
+ if (trimmed.toLowerCase() === "localhost") return "Domain cannot be localhost";
99
+ if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*\.)+[a-zA-Z]{2,}$/.test(trimmed))
100
+ return "Enter a valid domain (e.g. myapp.example.com)";
101
+ return true;
102
+ }
103
+ function validatePort(value) {
104
+ const num = Number(value.trim());
105
+ if (!Number.isInteger(num) || num < 1 || num > 65535) return "Enter a valid port (1-65535)";
106
+ return true;
107
+ }
108
+ var ConfigureTLS = class _ConfigureTLS extends Command {
92
109
  static description = "Configure TLS for your application";
93
- static summary = `Adds TLS configuration to your EigenCloud application.
110
+ static summary = `Configures TLS for your EigenCloud application.
94
111
 
95
- This command creates:
96
- - Caddyfile: Reverse proxy configuration for automatic HTTPS
97
- - .env.example.tls: Example environment variables for TLS
112
+ Prompts for domain and TLS settings (or accepts them via flags), then:
113
+ - Creates a Caddyfile for automatic HTTPS via Caddy reverse proxy
114
+ - Appends TLS variables to .env with your values
115
+ - Appends TLS placeholders to .env.example
98
116
 
99
117
  TLS certificates are automatically obtained via Let's Encrypt using the tls-keygen tool.`;
118
+ static examples = [
119
+ "<%= config.bin %> compute app configure tls",
120
+ "<%= config.bin %> compute app configure tls --domain myapp.example.com",
121
+ "<%= config.bin %> compute app configure tls --domain myapp.example.com --app-port 8080",
122
+ "<%= config.bin %> compute app configure tls --domain myapp.example.com --no-acme-staging"
123
+ ];
124
+ static flags = {
125
+ domain: Flags.string({
126
+ description: "Domain name for TLS certificate"
127
+ }),
128
+ "app-port": Flags.string({
129
+ description: "Port your application listens on",
130
+ default: "3000"
131
+ }),
132
+ "acme-staging": Flags.boolean({
133
+ description: "Use Let's Encrypt staging environment",
134
+ default: true,
135
+ allowNo: true
136
+ }),
137
+ "caddy-logs": Flags.boolean({
138
+ description: "Enable Caddy debug logs",
139
+ default: false,
140
+ allowNo: true
141
+ })
142
+ };
100
143
  async run() {
144
+ const { flags } = await this.parse(_ConfigureTLS);
101
145
  const cwd = process.cwd();
146
+ const envPath = path.join(cwd, ".env");
147
+ if (envFileHasTlsConfig(envPath)) {
148
+ this.warn("TLS is already configured in .env (DOMAIN is set). Skipping.");
149
+ return;
150
+ }
102
151
  const caddyfilePath = path.join(cwd, "Caddyfile");
103
152
  if (fs.existsSync(caddyfilePath)) {
104
- this.warn("Caddyfile already exists. Skipping...");
153
+ this.log("Caddyfile already exists, keeping existing file.");
105
154
  } else {
106
155
  const caddyfileContent = getCaddyfileTemplate();
107
156
  fs.writeFileSync(caddyfilePath, caddyfileContent, { mode: 420 });
108
157
  this.log("Created Caddyfile");
109
158
  }
110
- const envTLSPath = path.join(cwd, ".env.example.tls");
111
- if (fs.existsSync(envTLSPath)) {
112
- this.warn(".env.example.tls already exists. Skipping...");
159
+ this.log("");
160
+ let domain = flags.domain;
161
+ if (!domain) {
162
+ domain = await input({
163
+ message: "Domain name:",
164
+ validate: validateDomain
165
+ });
113
166
  } else {
114
- fs.writeFileSync(envTLSPath, ENV_EXAMPLE_TLS, { mode: 420 });
115
- this.log("Created .env.example.tls");
167
+ const result = validateDomain(domain);
168
+ if (result !== true) this.error(result);
116
169
  }
170
+ let appPort = flags["app-port"];
171
+ const portResult = validatePort(appPort);
172
+ if (portResult !== true) this.error(portResult);
173
+ const acmeStaging = flags["acme-staging"] !== void 0 ? flags["acme-staging"] : await confirm({
174
+ message: "Use Let's Encrypt staging? (recommended for first deploy to avoid rate limits)",
175
+ default: true
176
+ });
177
+ const enableCaddyLogs = flags["caddy-logs"] !== void 0 ? flags["caddy-logs"] : await confirm({
178
+ message: "Enable Caddy debug logs?",
179
+ default: false
180
+ });
117
181
  this.log("");
118
- this.log(chalk.green("TLS configuration added successfully"));
119
- this.log("");
120
- this.log("Created:");
121
- this.log(" - Caddyfile");
122
- this.log(" - .env.example.tls");
182
+ this.log(chalk.bold("TLS Configuration:"));
183
+ this.log(` Domain: ${domain.trim()}`);
184
+ this.log(` App port: ${appPort.trim()}`);
185
+ this.log(` ACME staging: ${acmeStaging}`);
186
+ this.log(` Caddy logs: ${enableCaddyLogs}`);
123
187
  this.log("");
124
- this.log("To enable TLS:");
125
- this.log("");
126
- this.log("1. Add TLS variables to .env:");
127
- this.log(" cat .env.example.tls >> .env");
188
+ if (!flags.domain) {
189
+ const confirmed = await confirm({
190
+ message: "Write these settings to .env?",
191
+ default: true
192
+ });
193
+ if (!confirmed) {
194
+ this.log("Cancelled.");
195
+ return;
196
+ }
197
+ }
198
+ const vars = {
199
+ domain: domain.trim(),
200
+ appPort: appPort.trim(),
201
+ acmeStaging,
202
+ enableCaddyLogs
203
+ };
204
+ const envBlock = getTlsEnvBlock(vars);
205
+ fs.appendFileSync(envPath, envBlock, { mode: 420 });
206
+ this.log(`Updated .env`);
207
+ const envExamplePath = path.join(cwd, ".env.example");
208
+ if (!envFileHasTlsConfig(envExamplePath)) {
209
+ fs.appendFileSync(envExamplePath, TLS_ENV_EXAMPLE_BLOCK, { mode: 420 });
210
+ this.log(`Updated .env.example`);
211
+ }
128
212
  this.log("");
129
- this.log("2. Configure required variables:");
130
- this.log(" DOMAIN=yourdomain.com");
213
+ this.log(chalk.green("TLS configured successfully"));
131
214
  this.log("");
132
- this.log(" For first deployment (recommended):");
133
- this.log(" ENABLE_CADDY_LOGS=true");
134
- this.log(" ACME_STAGING=true");
215
+ this.log("Next steps:");
135
216
  this.log("");
136
- this.log("3. Set up DNS A record pointing to instance IP");
217
+ this.log("1. Set up DNS A record pointing to your instance IP");
137
218
  this.log(" Run 'ecloud compute app list' to get IP address");
138
219
  this.log("");
139
- this.log("4. Upgrade:");
140
- this.log(" ecloud compute app upgrade");
220
+ this.log("2. Deploy or upgrade:");
221
+ this.log(" ecloud compute app deploy # new app");
222
+ this.log(" ecloud compute app upgrade # existing app");
141
223
  this.log("");
142
- this.log("Note: Let's Encrypt rate limit is 5 certificates/week per domain");
143
- this.log(" To switch staging -> production: set ACME_STAGING=false");
144
- this.log(" If cert exists, use ACME_FORCE_ISSUE=true once to replace");
224
+ if (acmeStaging) {
225
+ this.log(chalk.yellow("Note: ACME_STAGING is enabled (recommended for first deploy)"));
226
+ this.log("Once verified, switch to production certs:");
227
+ this.log(" 1. Set ACME_STAGING=false in .env");
228
+ this.log(" 2. Set ACME_FORCE_ISSUE=true in .env (one-time)");
229
+ this.log(" 3. Run: ecloud compute app upgrade");
230
+ this.log("");
231
+ }
232
+ this.log("Let's Encrypt rate limit: 5 certificates/week per domain");
145
233
  }
146
234
  };
147
235
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/commands/compute/app/configure/tls.ts","../../../../../src/templates/tls/Caddyfile.tmpl","../../../../../src/templates/tls/templates.ts"],"sourcesContent":["import { Command } from \"@oclif/core\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport chalk from \"chalk\";\nimport { getCaddyfileTemplate, ENV_EXAMPLE_TLS } from \"../../../../templates/tls/templates.js\";\n\nexport default class ConfigureTLS extends Command {\n static description = \"Configure TLS for your application\";\n\n static summary = `Adds TLS configuration to your EigenCloud application.\n\nThis command creates:\n- Caddyfile: Reverse proxy configuration for automatic HTTPS\n- .env.example.tls: Example environment variables for TLS\n\nTLS certificates are automatically obtained via Let's Encrypt using the tls-keygen tool.`;\n\n async run() {\n const cwd = process.cwd();\n\n // Write Caddyfile\n const caddyfilePath = path.join(cwd, \"Caddyfile\");\n if (fs.existsSync(caddyfilePath)) {\n this.warn(\"Caddyfile already exists. Skipping...\");\n } else {\n const caddyfileContent = getCaddyfileTemplate();\n fs.writeFileSync(caddyfilePath, caddyfileContent, { mode: 0o644 });\n this.log(\"Created Caddyfile\");\n }\n\n // Write .env.example.tls\n const envTLSPath = path.join(cwd, \".env.example.tls\");\n if (fs.existsSync(envTLSPath)) {\n this.warn(\".env.example.tls already exists. Skipping...\");\n } else {\n fs.writeFileSync(envTLSPath, ENV_EXAMPLE_TLS, { mode: 0o644 });\n this.log(\"Created .env.example.tls\");\n }\n\n // Print success message and instructions\n this.log(\"\");\n this.log(chalk.green(\"TLS configuration added successfully\"));\n this.log(\"\");\n this.log(\"Created:\");\n this.log(\" - Caddyfile\");\n this.log(\" - .env.example.tls\");\n this.log(\"\");\n\n this.log(\"To enable TLS:\");\n this.log(\"\");\n this.log(\"1. Add TLS variables to .env:\");\n this.log(\" cat .env.example.tls >> .env\");\n this.log(\"\");\n\n this.log(\"2. Configure required variables:\");\n this.log(\" DOMAIN=yourdomain.com\");\n this.log(\"\");\n this.log(\" For first deployment (recommended):\");\n this.log(\" ENABLE_CADDY_LOGS=true\");\n this.log(\" ACME_STAGING=true\");\n this.log(\"\");\n\n this.log(\"3. Set up DNS A record pointing to instance IP\");\n this.log(\" Run 'ecloud compute app list' to get IP address\");\n this.log(\"\");\n\n this.log(\"4. Upgrade:\");\n this.log(\" ecloud compute app upgrade\");\n this.log(\"\");\n\n this.log(\"Note: Let's Encrypt rate limit is 5 certificates/week per domain\");\n this.log(\" To switch staging -> production: set ACME_STAGING=false\");\n this.log(\" If cert exists, use ACME_FORCE_ISSUE=true once to replace\");\n }\n}\n","# Caddy configuration for automatic HTTPS\n# The DOMAIN environment variable will be injected at runtime\n\n{$DOMAIN:localhost} {\n # TLS configuration - always use provided certificates generated by tls-keygen\n tls /run/tls/fullchain.pem /run/tls/privkey.pem\n\n # Reverse proxy to your Node.js application\n # Modify the port to match your application (default: 3000)\n reverse_proxy localhost:{$APP_PORT:3000} {\n # Health check configuration\n health_uri /health\n health_interval 30s\n health_timeout 5s\n health_status 200\n }\n\n # Custom headers\n header {\n # Security headers\n X-Content-Type-Options \"nosniff\"\n X-Frame-Options \"DENY\"\n X-XSS-Protection \"1; mode=block\"\n Referrer-Policy \"strict-origin-when-cross-origin\"\n\n # Remove server header\n -Server\n }\n\n # Logging\n log {\n output stdout\n format console\n level INFO\n }\n\n # Request size limits\n request_body {\n max_size 10MB\n }\n}\n\n# HTTP endpoint (optional, for health checks or redirects)\n:80 {\n # Redirect to HTTPS only when host isn't localhost\n @for_domain expression {host} != \"localhost\"\n redir @for_domain https://{host}{uri} permanent\n\n # Health check endpoint (always available via HTTP)\n handle /health {\n respond \"OK\" 200\n }\n}\n","/**\n * TLS configuration templates\n */\n\nimport caddyfileTemplate from \"./Caddyfile.tmpl\";\n\n/**\n * Get the Caddyfile template\n */\nexport function getCaddyfileTemplate(): string {\n return caddyfileTemplate;\n}\n\n/**\n * Embedded .env.example.tls content\n * (embedded directly since .env files are gitignored)\n */\nexport const ENV_EXAMPLE_TLS = `# TLS Configuration\n# Set these variables to enable TLS for your application\n\n# Your domain name (required for TLS)\nDOMAIN=yourdomain.com\n\n# Port your application listens on\nAPP_PORT=3000\n\n# Enable Caddy debug logs\nENABLE_CADDY_LOGS=false\n\n# Use Let's Encrypt staging environment (for testing)\n# Set to true to avoid rate limits during development\nACME_STAGING=false\n\n# Force certificate reissue even if a valid one exists\n# Useful when you need to update SANs or force a renewal\nACME_FORCE_ISSUE=false\n`;\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,OAAO,WAAW;;;ACHlB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSO,SAAS,uBAA+B;AAC7C,SAAO;AACT;AAMO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AFX/B,IAAqB,eAArB,cAA0C,QAAQ;AAAA,EAChD,OAAO,cAAc;AAAA,EAErB,OAAO,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQjB,MAAM,MAAM;AACV,UAAM,MAAM,QAAQ,IAAI;AAGxB,UAAM,gBAAqB,UAAK,KAAK,WAAW;AAChD,QAAO,cAAW,aAAa,GAAG;AAChC,WAAK,KAAK,uCAAuC;AAAA,IACnD,OAAO;AACL,YAAM,mBAAmB,qBAAqB;AAC9C,MAAG,iBAAc,eAAe,kBAAkB,EAAE,MAAM,IAAM,CAAC;AACjE,WAAK,IAAI,mBAAmB;AAAA,IAC9B;AAGA,UAAM,aAAkB,UAAK,KAAK,kBAAkB;AACpD,QAAO,cAAW,UAAU,GAAG;AAC7B,WAAK,KAAK,8CAA8C;AAAA,IAC1D,OAAO;AACL,MAAG,iBAAc,YAAY,iBAAiB,EAAE,MAAM,IAAM,CAAC;AAC7D,WAAK,IAAI,0BAA0B;AAAA,IACrC;AAGA,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,MAAM,MAAM,sCAAsC,CAAC;AAC5D,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,UAAU;AACnB,SAAK,IAAI,eAAe;AACxB,SAAK,IAAI,sBAAsB;AAC/B,SAAK,IAAI,EAAE;AAEX,SAAK,IAAI,gBAAgB;AACzB,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,+BAA+B;AACxC,SAAK,IAAI,iCAAiC;AAC1C,SAAK,IAAI,EAAE;AAEX,SAAK,IAAI,kCAAkC;AAC3C,SAAK,IAAI,0BAA0B;AACnC,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,wCAAwC;AACjD,SAAK,IAAI,2BAA2B;AACpC,SAAK,IAAI,sBAAsB;AAC/B,SAAK,IAAI,EAAE;AAEX,SAAK,IAAI,gDAAgD;AACzD,SAAK,IAAI,oDAAoD;AAC7D,SAAK,IAAI,EAAE;AAEX,SAAK,IAAI,aAAa;AACtB,SAAK,IAAI,+BAA+B;AACxC,SAAK,IAAI,EAAE;AAEX,SAAK,IAAI,kEAAkE;AAC3E,SAAK,IAAI,+DAA+D;AACxE,SAAK,IAAI,iEAAiE;AAAA,EAC5E;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/commands/compute/app/configure/tls.ts","../../../../../src/templates/tls/Caddyfile.tmpl","../../../../../src/templates/tls/templates.ts"],"sourcesContent":["import { Command, Flags } from \"@oclif/core\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport chalk from \"chalk\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport {\n getCaddyfileTemplate,\n getTlsEnvBlock,\n TLS_ENV_EXAMPLE_BLOCK,\n} from \"../../../../templates/tls/templates.js\";\n\nfunction envFileHasTlsConfig(filePath: string): boolean {\n if (!fs.existsSync(filePath)) return false;\n const content = fs.readFileSync(filePath, \"utf-8\");\n return /^DOMAIN=/m.test(content);\n}\n\nfunction validateDomain(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) return \"Domain is required\";\n if (trimmed.toLowerCase() === \"localhost\") return \"Domain cannot be localhost\";\n if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*\\.)+[a-zA-Z]{2,}$/.test(trimmed))\n return \"Enter a valid domain (e.g. myapp.example.com)\";\n return true;\n}\n\nfunction validatePort(value: string): true | string {\n const num = Number(value.trim());\n if (!Number.isInteger(num) || num < 1 || num > 65535) return \"Enter a valid port (1-65535)\";\n return true;\n}\n\nexport default class ConfigureTLS extends Command {\n static description = \"Configure TLS for your application\";\n\n static summary = `Configures TLS for your EigenCloud application.\n\nPrompts for domain and TLS settings (or accepts them via flags), then:\n- Creates a Caddyfile for automatic HTTPS via Caddy reverse proxy\n- Appends TLS variables to .env with your values\n- Appends TLS placeholders to .env.example\n\nTLS certificates are automatically obtained via Let's Encrypt using the tls-keygen tool.`;\n\n static examples = [\n \"<%= config.bin %> compute app configure tls\",\n \"<%= config.bin %> compute app configure tls --domain myapp.example.com\",\n \"<%= config.bin %> compute app configure tls --domain myapp.example.com --app-port 8080\",\n \"<%= config.bin %> compute app configure tls --domain myapp.example.com --no-acme-staging\",\n ];\n\n static flags = {\n domain: Flags.string({\n description: \"Domain name for TLS certificate\",\n }),\n \"app-port\": Flags.string({\n description: \"Port your application listens on\",\n default: \"3000\",\n }),\n \"acme-staging\": Flags.boolean({\n description: \"Use Let's Encrypt staging environment\",\n default: true,\n allowNo: true,\n }),\n \"caddy-logs\": Flags.boolean({\n description: \"Enable Caddy debug logs\",\n default: false,\n allowNo: true,\n }),\n };\n\n async run() {\n const { flags } = await this.parse(ConfigureTLS);\n const cwd = process.cwd();\n\n // Check if TLS is already configured in .env\n const envPath = path.join(cwd, \".env\");\n if (envFileHasTlsConfig(envPath)) {\n this.warn(\"TLS is already configured in .env (DOMAIN is set). Skipping.\");\n return;\n }\n\n // Write Caddyfile\n const caddyfilePath = path.join(cwd, \"Caddyfile\");\n if (fs.existsSync(caddyfilePath)) {\n this.log(\"Caddyfile already exists, keeping existing file.\");\n } else {\n const caddyfileContent = getCaddyfileTemplate();\n fs.writeFileSync(caddyfilePath, caddyfileContent, { mode: 0o644 });\n this.log(\"Created Caddyfile\");\n }\n\n this.log(\"\");\n\n // Resolve values: use flags if provided, otherwise prompt\n let domain = flags.domain;\n if (!domain) {\n domain = await input({\n message: \"Domain name:\",\n validate: validateDomain,\n });\n } else {\n const result = validateDomain(domain);\n if (result !== true) this.error(result);\n }\n\n let appPort = flags[\"app-port\"];\n // Only prompt if the user didn't pass --app-port at all (default is \"3000\")\n // Since oclif always provides the default, we use the default directly\n const portResult = validatePort(appPort);\n if (portResult !== true) this.error(portResult);\n\n const acmeStaging =\n flags[\"acme-staging\"] !== undefined\n ? flags[\"acme-staging\"]\n : await confirm({\n message:\n \"Use Let's Encrypt staging? (recommended for first deploy to avoid rate limits)\",\n default: true,\n });\n\n const enableCaddyLogs =\n flags[\"caddy-logs\"] !== undefined\n ? flags[\"caddy-logs\"]\n : await confirm({\n message: \"Enable Caddy debug logs?\",\n default: false,\n });\n\n // Show summary\n this.log(\"\");\n this.log(chalk.bold(\"TLS Configuration:\"));\n this.log(` Domain: ${domain.trim()}`);\n this.log(` App port: ${appPort.trim()}`);\n this.log(` ACME staging: ${acmeStaging}`);\n this.log(` Caddy logs: ${enableCaddyLogs}`);\n this.log(\"\");\n\n // Only ask for confirmation in interactive mode (no --domain flag)\n if (!flags.domain) {\n const confirmed = await confirm({\n message: \"Write these settings to .env?\",\n default: true,\n });\n\n if (!confirmed) {\n this.log(\"Cancelled.\");\n return;\n }\n }\n\n const vars = {\n domain: domain.trim(),\n appPort: appPort.trim(),\n acmeStaging,\n enableCaddyLogs,\n };\n\n // Append to .env\n const envBlock = getTlsEnvBlock(vars);\n fs.appendFileSync(envPath, envBlock, { mode: 0o644 });\n this.log(`Updated .env`);\n\n // Append to .env.example (with placeholders, skip if already has DOMAIN)\n const envExamplePath = path.join(cwd, \".env.example\");\n if (!envFileHasTlsConfig(envExamplePath)) {\n fs.appendFileSync(envExamplePath, TLS_ENV_EXAMPLE_BLOCK, { mode: 0o644 });\n this.log(`Updated .env.example`);\n }\n\n // Print next steps\n this.log(\"\");\n this.log(chalk.green(\"TLS configured successfully\"));\n this.log(\"\");\n this.log(\"Next steps:\");\n this.log(\"\");\n this.log(\"1. Set up DNS A record pointing to your instance IP\");\n this.log(\" Run 'ecloud compute app list' to get IP address\");\n this.log(\"\");\n this.log(\"2. Deploy or upgrade:\");\n this.log(\" ecloud compute app deploy # new app\");\n this.log(\" ecloud compute app upgrade # existing app\");\n this.log(\"\");\n\n if (acmeStaging) {\n this.log(chalk.yellow(\"Note: ACME_STAGING is enabled (recommended for first deploy)\"));\n this.log(\"Once verified, switch to production certs:\");\n this.log(\" 1. Set ACME_STAGING=false in .env\");\n this.log(\" 2. Set ACME_FORCE_ISSUE=true in .env (one-time)\");\n this.log(\" 3. Run: ecloud compute app upgrade\");\n this.log(\"\");\n }\n\n this.log(\"Let's Encrypt rate limit: 5 certificates/week per domain\");\n }\n}\n","# Caddy configuration for automatic HTTPS\n# The DOMAIN environment variable will be injected at runtime\n\n{$DOMAIN:localhost} {\n # TLS configuration - always use provided certificates generated by tls-keygen\n tls /run/tls/fullchain.pem /run/tls/privkey.pem\n\n # Reverse proxy to your Node.js application\n # Modify the port to match your application (default: 3000)\n reverse_proxy localhost:{$APP_PORT:3000} {\n # Health check configuration\n health_uri /health\n health_interval 30s\n health_timeout 5s\n health_status 200\n }\n\n # Custom headers\n header {\n # Security headers\n X-Content-Type-Options \"nosniff\"\n X-Frame-Options \"DENY\"\n X-XSS-Protection \"1; mode=block\"\n Referrer-Policy \"strict-origin-when-cross-origin\"\n\n # Remove server header\n -Server\n }\n\n # Logging\n log {\n output stdout\n format console\n level INFO\n }\n\n # Request size limits\n request_body {\n max_size 10MB\n }\n}\n\n# HTTP endpoint (optional, for health checks or redirects)\n:80 {\n # Redirect to HTTPS only when host isn't localhost\n @for_domain expression {host} != \"localhost\"\n redir @for_domain https://{host}{uri} permanent\n\n # Health check endpoint (always available via HTTP)\n handle /health {\n respond \"OK\" 200\n }\n}\n","/**\n * TLS configuration templates\n */\n\nimport caddyfileTemplate from \"./Caddyfile.tmpl\";\n\n/**\n * Get the Caddyfile template\n */\nexport function getCaddyfileTemplate(): string {\n return caddyfileTemplate;\n}\n\nexport interface TlsEnvVars {\n domain: string;\n appPort: string;\n acmeStaging: boolean;\n enableCaddyLogs: boolean;\n}\n\n/**\n * Generate the TLS env block with user-provided values for .env\n */\nexport function getTlsEnvBlock(vars: TlsEnvVars): string {\n return `\n# TLS Configuration\nDOMAIN=${vars.domain}\nAPP_PORT=${vars.appPort}\nENABLE_CADDY_LOGS=${vars.enableCaddyLogs}\nACME_STAGING=${vars.acmeStaging}\nACME_FORCE_ISSUE=false\n`;\n}\n\n/**\n * Placeholder TLS block for .env.example\n */\nexport const TLS_ENV_EXAMPLE_BLOCK = `\n# TLS Configuration\n# DOMAIN=yourdomain.com\n# APP_PORT=3000\n# ENABLE_CADDY_LOGS=false\n# ACME_STAGING=false\n# ACME_FORCE_ISSUE=false\n`;\n"],"mappings":"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","names":[]}