quicklify 1.2.0 → 1.2.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 (76) hide show
  1. package/README.md +3 -3
  2. package/README.tr.md +1 -1
  3. package/SECURITY.md +1 -1
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +7 -9
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/doctor.js +16 -30
  9. package/dist/commands/doctor.js.map +1 -1
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +14 -378
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/constants.d.ts +28 -0
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +32 -1
  16. package/dist/constants.js.map +1 -1
  17. package/dist/core/backup.d.ts +2 -2
  18. package/dist/core/backup.d.ts.map +1 -1
  19. package/dist/core/backup.js +25 -6
  20. package/dist/core/backup.js.map +1 -1
  21. package/dist/core/deploy.d.ts +4 -0
  22. package/dist/core/deploy.d.ts.map +1 -0
  23. package/dist/core/deploy.js +360 -0
  24. package/dist/core/deploy.js.map +1 -0
  25. package/dist/core/manage.d.ts.map +1 -1
  26. package/dist/core/manage.js +3 -3
  27. package/dist/core/manage.js.map +1 -1
  28. package/dist/core/provision.js +2 -2
  29. package/dist/core/provision.js.map +1 -1
  30. package/dist/core/tokens.d.ts.map +1 -1
  31. package/dist/core/tokens.js +5 -8
  32. package/dist/core/tokens.js.map +1 -1
  33. package/dist/mcp/tools/serverInfo.d.ts +2 -1
  34. package/dist/mcp/tools/serverInfo.d.ts.map +1 -1
  35. package/dist/mcp/tools/serverInfo.js +2 -1
  36. package/dist/mcp/tools/serverInfo.js.map +1 -1
  37. package/dist/mcp/tools/serverManage.d.ts.map +1 -1
  38. package/dist/mcp/tools/serverManage.js +2 -1
  39. package/dist/mcp/tools/serverManage.js.map +1 -1
  40. package/dist/mcp/tools/serverProvision.d.ts +2 -1
  41. package/dist/mcp/tools/serverProvision.d.ts.map +1 -1
  42. package/dist/mcp/tools/serverProvision.js +2 -1
  43. package/dist/mcp/tools/serverProvision.js.map +1 -1
  44. package/dist/providers/base.d.ts +7 -0
  45. package/dist/providers/base.d.ts.map +1 -1
  46. package/dist/providers/base.js +56 -1
  47. package/dist/providers/base.js.map +1 -1
  48. package/dist/providers/digitalocean.d.ts +1 -1
  49. package/dist/providers/digitalocean.d.ts.map +1 -1
  50. package/dist/providers/digitalocean.js +1 -9
  51. package/dist/providers/digitalocean.js.map +1 -1
  52. package/dist/providers/hetzner.d.ts +1 -1
  53. package/dist/providers/hetzner.d.ts.map +1 -1
  54. package/dist/providers/hetzner.js +1 -9
  55. package/dist/providers/hetzner.js.map +1 -1
  56. package/dist/providers/linode.d.ts +1 -1
  57. package/dist/providers/linode.d.ts.map +1 -1
  58. package/dist/providers/linode.js +1 -9
  59. package/dist/providers/linode.js.map +1 -1
  60. package/dist/providers/vultr.d.ts +1 -1
  61. package/dist/providers/vultr.d.ts.map +1 -1
  62. package/dist/providers/vultr.js +1 -9
  63. package/dist/providers/vultr.js.map +1 -1
  64. package/dist/utils/defaults.d.ts.map +1 -1
  65. package/dist/utils/defaults.js +3 -2
  66. package/dist/utils/defaults.js.map +1 -1
  67. package/dist/utils/prompts.d.ts.map +1 -1
  68. package/dist/utils/prompts.js +5 -6
  69. package/dist/utils/prompts.js.map +1 -1
  70. package/dist/utils/serverSelect.d.ts.map +1 -1
  71. package/dist/utils/serverSelect.js +2 -7
  72. package/dist/utils/serverSelect.js.map +1 -1
  73. package/dist/utils/yamlConfig.d.ts.map +1 -1
  74. package/dist/utils/yamlConfig.js +4 -3
  75. package/dist/utils/yamlConfig.js.map +1 -1
  76. package/package.json +4 -4
package/README.md CHANGED
@@ -182,7 +182,7 @@ quicklify init --template production --provider hetzner
182
182
 
183
183
  ## Security
184
184
 
185
- Quicklify is built with security as a priority — **2,047 tests** across 76 suites, including dedicated security test suites.
185
+ Quicklify is built with security as a priority — **2,099 tests** across 78 suites, including dedicated security test suites.
186
186
 
187
187
  - API tokens are never stored on disk — prompted at runtime or via environment variables
188
188
  - SSH keys are auto-generated if needed (Ed25519)
@@ -213,8 +213,8 @@ Requires Node.js 20 or later.
213
213
  **Server creation fails?**
214
214
  Run `quicklify doctor --check-tokens` to verify your API token and local environment.
215
215
 
216
- **Coolify not responding?**
217
- Use `quicklify status my-server --autostart` to check and auto-restart if needed.
216
+ **Server not responding?**
217
+ Use `quicklify status my-server --autostart` for Coolify servers, or `quicklify health` to check all servers at once.
218
218
 
219
219
  **Need to start fresh?**
220
220
  `quicklify destroy my-server` removes the cloud server entirely.
package/README.tr.md CHANGED
@@ -182,7 +182,7 @@ quicklify init --template production --provider hetzner
182
182
 
183
183
  ## Güvenlik
184
184
 
185
- Quicklify güvenlik öncelikli olarak geliştirilmektedir — 76 test suite'inde **2.047 test**, özel güvenlik test suite'leri dahil.
185
+ Quicklify güvenlik öncelikli olarak geliştirilmektedir — 78 test suite'inde **2.099 test**, özel güvenlik test suite'leri dahil.
186
186
 
187
187
  - API token'ları asla diske kaydedilmez — çalışma zamanında sorulur veya ortam değişkenlerinden alınır
188
188
  - SSH anahtarları gerekirse otomatik oluşturulur (Ed25519)
package/SECURITY.md CHANGED
@@ -28,7 +28,7 @@ Response time: Within 48 hours
28
28
  - Supported env vars: `HETZNER_TOKEN`, `DIGITALOCEAN_TOKEN`, `VULTR_TOKEN`, `LINODE_TOKEN`
29
29
  - API tokens collected via interactive secure prompts (masked input) when env vars are not set
30
30
  - `sanitizedEnv()` strips all keys containing TOKEN, SECRET, PASSWORD, CREDENTIAL from child process environments before every `spawn`/`spawnSync`/`exec` call
31
- - Provider errors sanitized via `stripSensitiveData()` — removes Authorization headers from axios errors before they reach error messages
31
+ - Provider errors sanitized via `stripSensitiveData()` — removes Authorization headers, request data, response headers, and non-whitelisted response body fields from axios errors before they propagate via error cause chains
32
32
 
33
33
  ### Input Validation (A1 — Injection)
34
34
  - All shell execution uses `spawn`/`spawnSync` with array arguments (never string interpolation into shell commands)
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAQA,UAAU,UAAU;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,UAAU,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6HxE"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AASA,UAAU,UAAU;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,UAAU,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA0HxE"}
@@ -4,6 +4,7 @@ import chalk from "chalk";
4
4
  import { promptApiToken } from "../utils/serverSelect.js";
5
5
  import { addServerRecord } from "../core/manage.js";
6
6
  import { logger } from "../utils/logger.js";
7
+ import { SUPPORTED_PROVIDERS, PROVIDER_DISPLAY_NAMES, invalidProviderError } from "../constants.js";
7
8
  export async function addCommand(options = {}) {
8
9
  // Step 1: Select provider
9
10
  let providerName = options.provider;
@@ -13,19 +14,16 @@ export async function addCommand(options = {}) {
13
14
  type: "list",
14
15
  name: "provider",
15
16
  message: "Select cloud provider:",
16
- choices: [
17
- { name: "Hetzner Cloud", value: "hetzner" },
18
- { name: "DigitalOcean", value: "digitalocean" },
19
- { name: "Vultr", value: "vultr" },
20
- { name: "Linode (Akamai)", value: "linode" },
21
- ],
17
+ choices: SUPPORTED_PROVIDERS.map((p) => ({
18
+ name: PROVIDER_DISPLAY_NAMES[p],
19
+ value: p,
20
+ })),
22
21
  },
23
22
  ]);
24
23
  providerName = provider;
25
24
  }
26
- const validProviders = ["hetzner", "digitalocean", "vultr", "linode"];
27
- if (!validProviders.includes(providerName)) {
28
- logger.error(`Invalid provider: ${providerName}. Use: ${validProviders.join(", ")}`);
25
+ if (!SUPPORTED_PROVIDERS.includes(providerName)) {
26
+ logger.error(invalidProviderError(providerName));
29
27
  process.exit(1);
30
28
  return;
31
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAmDA,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmCzD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AA0DD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAS/D;AAED,wBAAsB,aAAa,CACjC,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAkCA,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmCzD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AA0DD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAS/D;AAED,wBAAsB,aAAa,CACjC,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
@@ -5,35 +5,21 @@ import { getServers } from "../utils/config.js";
5
5
  import { checkSshAvailable } from "../utils/ssh.js";
6
6
  import { logger } from "../utils/logger.js";
7
7
  import { CONFIG_DIR } from "../utils/config.js";
8
- const PROVIDER_CONFIG = {
9
- hetzner: {
10
- envVar: "HETZNER_TOKEN",
11
- displayName: "Hetzner",
12
- validateUrl: "https://api.hetzner.cloud/v1/servers?per_page=1",
13
- },
14
- digitalocean: {
15
- envVar: "DIGITALOCEAN_TOKEN",
16
- displayName: "DigitalOcean",
17
- validateUrl: "https://api.digitalocean.com/v2/account",
18
- },
19
- vultr: {
20
- envVar: "VULTR_TOKEN",
21
- displayName: "Vultr",
22
- validateUrl: "https://api.vultr.com/v2/account",
23
- },
24
- linode: {
25
- envVar: "LINODE_TOKEN",
26
- displayName: "Linode",
27
- validateUrl: "https://api.linode.com/v4/profile",
28
- },
8
+ import { PROVIDER_REGISTRY } from "../constants.js";
9
+ // Validation endpoints differ from base API URLs (provider-specific paths)
10
+ const DOCTOR_VALIDATE_URLS = {
11
+ hetzner: "https://api.hetzner.cloud/v1/servers?per_page=1",
12
+ digitalocean: "https://api.digitalocean.com/v2/account",
13
+ vultr: "https://api.vultr.com/v2/account",
14
+ linode: "https://api.linode.com/v4/profile",
29
15
  };
30
16
  async function validateToken(provider, token) {
31
- const config = PROVIDER_CONFIG[provider];
32
- if (!config) {
17
+ const validateUrl = DOCTOR_VALIDATE_URLS[provider];
18
+ if (!validateUrl) {
33
19
  return false;
34
20
  }
35
21
  try {
36
- await axios.get(config.validateUrl, {
22
+ await axios.get(validateUrl, {
37
23
  headers: { Authorization: `Bearer ${token}` },
38
24
  timeout: 10000,
39
25
  });
@@ -54,22 +40,22 @@ export async function checkProviderTokens() {
54
40
  console.log();
55
41
  logger.title("Provider Token Validation");
56
42
  for (const provider of providers) {
57
- const config = PROVIDER_CONFIG[provider];
58
- if (!config) {
43
+ const registryEntry = PROVIDER_REGISTRY[provider];
44
+ if (!registryEntry) {
59
45
  logger.warning(`${provider}: Unknown provider, skipping token check`);
60
46
  continue;
61
47
  }
62
- const token = process.env[config.envVar];
48
+ const token = process.env[registryEntry.envKey];
63
49
  if (!token) {
64
- logger.warning(`${config.displayName}: ${config.envVar} not set in environment`);
50
+ logger.warning(`${registryEntry.displayName}: ${registryEntry.envKey} not set in environment`);
65
51
  continue;
66
52
  }
67
53
  const isValid = await validateToken(provider, token);
68
54
  if (isValid) {
69
- logger.success(`${config.displayName}: Token is valid`);
55
+ logger.success(`${registryEntry.displayName}: Token is valid`);
70
56
  }
71
57
  else {
72
- logger.error(`${config.displayName}: Token is invalid or expired`);
58
+ logger.error(`${registryEntry.displayName}: Token is invalid or expired`);
73
59
  }
74
60
  }
75
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AA0BjE,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuO1E"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkBrD,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+N1E"}
@@ -1,20 +1,11 @@
1
- import { spawnSync } from "child_process";
2
1
  import { createProvider, createProviderWithToken } from "../utils/providerFactory.js";
3
- import { saveServer } from "../utils/config.js";
4
- import { waitForCoolify } from "../utils/healthCheck.js";
5
2
  import { BACK_SIGNAL, getProviderConfig, getDeploymentConfig, getLocationConfig, getServerTypeConfig, getServerNameConfig, confirmDeployment, } from "../utils/prompts.js";
6
- import { getCoolifyCloudInit, getBareCloudInit } from "../utils/cloudInit.js";
7
3
  import { logger, createSpinner } from "../utils/logger.js";
8
- import { getErrorMessage, mapProviderError } from "../utils/errorMapper.js";
9
- import { openBrowser } from "../utils/openBrowser.js";
10
- import { assertValidIp, sanitizedEnv, sshExec } from "../utils/ssh.js";
11
- import { findLocalSshKey, generateSshKey, getSshKeyName } from "../utils/sshKey.js";
12
- import { firewallSetup } from "./firewall.js";
13
- import { secureSetup } from "./secure.js";
14
4
  import { loadYamlConfig } from "../utils/yamlConfig.js";
15
5
  import { mergeConfig } from "../utils/configMerge.js";
16
6
  import { getTemplate, getTemplateDefaults, VALID_TEMPLATE_NAMES } from "../utils/templates.js";
17
- import { IP_WAIT, COOLIFY_MIN_WAIT } from "../constants.js";
7
+ import { SUPPORTED_PROVIDERS, PROVIDER_ENV_KEYS, invalidProviderError } from "../constants.js";
8
+ import { deployServer } from "../core/deploy.js";
18
9
  export async function initCommand(options = {}) {
19
10
  // Load YAML config if --config flag provided
20
11
  if (options.config) {
@@ -69,8 +60,8 @@ export async function initCommand(options = {}) {
69
60
  let serverName;
70
61
  // Step 1: Select cloud provider
71
62
  if (options.provider) {
72
- if (!["hetzner", "digitalocean", "vultr", "linode"].includes(options.provider)) {
73
- logger.error(`Invalid provider: ${options.provider}. Use "hetzner", "digitalocean", "vultr", or "linode".`);
63
+ if (!SUPPORTED_PROVIDERS.includes(options.provider)) {
64
+ logger.error(invalidProviderError(options.provider));
74
65
  process.exit(1);
75
66
  return;
76
67
  }
@@ -99,26 +90,17 @@ export async function initCommand(options = {}) {
99
90
  logger.warning("Token passed via --token flag is visible in shell history. Use environment variables instead: export HETZNER_TOKEN=...");
100
91
  process.title = "quicklify";
101
92
  }
102
- else if (providerChoice === "hetzner" && process.env.HETZNER_TOKEN) {
103
- apiToken = process.env.HETZNER_TOKEN;
104
- tokenSource = "HETZNER_TOKEN env var";
105
- }
106
- else if (providerChoice === "digitalocean" && process.env.DIGITALOCEAN_TOKEN) {
107
- apiToken = process.env.DIGITALOCEAN_TOKEN;
108
- tokenSource = "DIGITALOCEAN_TOKEN env var";
109
- }
110
- else if (providerChoice === "vultr" && process.env.VULTR_TOKEN) {
111
- apiToken = process.env.VULTR_TOKEN;
112
- tokenSource = "VULTR_TOKEN env var";
113
- }
114
- else if (providerChoice === "linode" && process.env.LINODE_TOKEN) {
115
- apiToken = process.env.LINODE_TOKEN;
116
- tokenSource = "LINODE_TOKEN env var";
117
- }
118
93
  else {
119
- const config = await getDeploymentConfig(provider);
120
- apiToken = config.apiToken;
121
- tokenSource = "interactive prompt";
94
+ const envKey = PROVIDER_ENV_KEYS[providerChoice];
95
+ if (envKey && process.env[envKey]) {
96
+ apiToken = process.env[envKey];
97
+ tokenSource = `${envKey} env var`;
98
+ }
99
+ else {
100
+ const config = await getDeploymentConfig(provider);
101
+ apiToken = config.apiToken;
102
+ tokenSource = "interactive prompt";
103
+ }
122
104
  }
123
105
  // Step 3: Validate API token
124
106
  const providerWithToken = createProviderWithToken(providerChoice, apiToken);
@@ -229,350 +211,4 @@ export async function initCommand(options = {}) {
229
211
  }
230
212
  return deployServer(providerChoice, providerWithToken, region, serverSize, serverName, options.fullSetup, options.noOpen, options.mode);
231
213
  }
232
- async function uploadSshKeyToProvider(provider) {
233
- let publicKey = findLocalSshKey();
234
- if (!publicKey) {
235
- logger.info("No SSH key found. Generating one...");
236
- publicKey = generateSshKey();
237
- if (publicKey) {
238
- logger.success("SSH key generated (~/.ssh/id_ed25519)");
239
- }
240
- else {
241
- logger.warning("Could not generate SSH key — falling back to password auth");
242
- logger.info("Server will require password change on first SSH login");
243
- logger.warning("Run 'quicklify secure setup' after deployment to harden SSH access");
244
- return [];
245
- }
246
- }
247
- const spinner = createSpinner("Uploading SSH key to provider...");
248
- spinner.start();
249
- try {
250
- const keyId = await provider.uploadSshKey(getSshKeyName(), publicKey);
251
- spinner.succeed("SSH key uploaded — password-free access enabled");
252
- return [keyId];
253
- }
254
- catch (error) {
255
- spinner.fail("SSH key upload failed — falling back to password auth");
256
- logger.warning(getErrorMessage(error));
257
- logger.warning("Run 'quicklify secure setup' after deployment to harden SSH access");
258
- return [];
259
- }
260
- }
261
- async function deployServer(providerChoice, providerWithToken, region, serverSize, serverName, fullSetup, noOpen, mode) {
262
- try {
263
- // Upload SSH key before creating server
264
- const sshKeyIds = await uploadSshKeyToProvider(providerWithToken);
265
- const isBare = mode === "bare";
266
- const cloudInit = isBare ? getBareCloudInit(serverName) : getCoolifyCloudInit(serverName);
267
- let server;
268
- let retries = 0;
269
- const maxRetries = 2;
270
- const failedTypes = [];
271
- const failedLocations = [];
272
- while (!server && retries <= maxRetries) {
273
- const serverSpinner = createSpinner("Creating VPS server...");
274
- serverSpinner.start();
275
- try {
276
- server = await providerWithToken.createServer({
277
- name: serverName,
278
- region,
279
- size: serverSize,
280
- cloudInit,
281
- sshKeyIds,
282
- });
283
- serverSpinner.succeed(`Server created (ID: ${server.id})`);
284
- }
285
- catch (createError) {
286
- serverSpinner.fail("Server creation failed");
287
- const errorMsg = getErrorMessage(createError);
288
- if (errorMsg.includes("already") && errorMsg.includes("used")) {
289
- logger.warning(`Server name "${serverName}" is already in use`);
290
- logger.info("Please choose a different name:");
291
- let newName = BACK_SIGNAL;
292
- while (newName === BACK_SIGNAL) {
293
- newName = await getServerNameConfig();
294
- }
295
- serverName = newName;
296
- retries++;
297
- }
298
- else if (errorMsg.includes("location disabled")) {
299
- failedLocations.push(region);
300
- logger.warning(`Location "${region}" is currently disabled for new servers`);
301
- logger.info("Please select a different region and server type:");
302
- let pickedSize = false;
303
- while (!pickedSize) {
304
- let newRegion = BACK_SIGNAL;
305
- while (newRegion === BACK_SIGNAL) {
306
- newRegion = await getLocationConfig(providerWithToken, failedLocations);
307
- }
308
- region = newRegion;
309
- const newSize = await getServerTypeConfig(providerWithToken, region, [], mode);
310
- if (newSize === BACK_SIGNAL)
311
- continue;
312
- serverSize = newSize;
313
- pickedSize = true;
314
- }
315
- retries++;
316
- }
317
- else if (errorMsg.includes("unavailable") ||
318
- errorMsg.includes("not available") ||
319
- errorMsg.includes("sold out") ||
320
- errorMsg.includes("unsupported")) {
321
- if (retries < maxRetries) {
322
- failedTypes.push(serverSize);
323
- logger.warning(`Server type "${serverSize}" is not available in this location`);
324
- logger.info("Please select a different server type:");
325
- let newSize = BACK_SIGNAL;
326
- while (newSize === BACK_SIGNAL) {
327
- newSize = await getServerTypeConfig(providerWithToken, region, failedTypes, mode);
328
- }
329
- serverSize = newSize;
330
- retries++;
331
- }
332
- else {
333
- throw createError;
334
- }
335
- }
336
- else {
337
- throw createError;
338
- }
339
- }
340
- }
341
- /* istanbul ignore next */
342
- if (!server) {
343
- logger.error("Could not create server after multiple attempts");
344
- process.exit(1);
345
- }
346
- // Wait for server to be running
347
- const statusSpinner = createSpinner("Waiting for server to boot...");
348
- statusSpinner.start();
349
- let status = await providerWithToken.getServerStatus(server.id);
350
- let attempts = 0;
351
- const maxAttempts = 30;
352
- while (status !== "running" && attempts < maxAttempts) {
353
- await new Promise((resolve) => setTimeout(resolve, 1000));
354
- status = await providerWithToken.getServerStatus(server.id);
355
- attempts++;
356
- }
357
- if (status !== "running") {
358
- statusSpinner.fail("Server failed to start");
359
- logger.error("Please check your cloud provider dashboard");
360
- return;
361
- }
362
- statusSpinner.succeed("Server is running");
363
- // Refresh server details to get final IP (DO/Vultr/Linode assign IP after boot)
364
- if (server.ip === "pending" || server.ip === "0.0.0.0" || server.ip === "") {
365
- const ipConfig = IP_WAIT[providerChoice] || { attempts: 10, interval: 3000 };
366
- const ipSpinner = createSpinner("Waiting for IP address assignment...");
367
- ipSpinner.start();
368
- let refreshAttempts = 0;
369
- while (refreshAttempts < ipConfig.attempts) {
370
- const details = await providerWithToken.getServerDetails(server.id);
371
- if (details.ip && details.ip !== "0.0.0.0" && details.ip !== "pending" && details.ip !== "") {
372
- try {
373
- assertValidIp(details.ip);
374
- server.ip = details.ip;
375
- break;
376
- }
377
- catch {
378
- // Invalid IP format from API — skip and retry
379
- }
380
- }
381
- refreshAttempts++;
382
- await new Promise((resolve) => setTimeout(resolve, ipConfig.interval));
383
- }
384
- if (server.ip === "pending" || server.ip === "0.0.0.0" || server.ip === "") {
385
- ipSpinner.fail("Could not obtain server IP address");
386
- logger.warning("The server was created but IP assignment timed out.");
387
- logger.info(`Check IP later with: quicklify status ${server.id}`);
388
- }
389
- else {
390
- ipSpinner.succeed(`IP address assigned: ${server.ip}`);
391
- }
392
- }
393
- // Health check polling instead of blind wait (skip if no valid IP or bare mode)
394
- const hasValidIp = server.ip !== "0.0.0.0" && server.ip !== "pending" && server.ip !== "";
395
- const minWait = COOLIFY_MIN_WAIT[providerChoice] || 60000;
396
- const ready = !isBare && hasValidIp ? await waitForCoolify(server.ip, minWait) : false;
397
- // Save server record to config
398
- saveServer({
399
- id: server.id,
400
- name: serverName,
401
- provider: providerChoice,
402
- ip: server.ip,
403
- region,
404
- size: serverSize,
405
- createdAt: new Date().toISOString(),
406
- ...(isBare ? { mode: "bare" } : { mode: "coolify" }),
407
- });
408
- // Bare mode: cloud-init wait + optional full setup + show SSH info
409
- if (isBare) {
410
- // Wait for SSH + cloud-init to finish (BUG-5)
411
- if (hasValidIp) {
412
- const cloudInitSpinner = createSpinner("Waiting for server to accept SSH...");
413
- cloudInitSpinner.start();
414
- // Step 1: Wait for SSH to become available (retry up to 60 attempts, 5s apart = 5 min max)
415
- let sshReady = false;
416
- for (let attempt = 1; attempt <= 60; attempt++) {
417
- try {
418
- await sshExec(server.ip, "echo ok");
419
- sshReady = true;
420
- break;
421
- }
422
- catch {
423
- cloudInitSpinner.text = `Waiting for server to accept SSH... (attempt ${attempt}/60)`;
424
- await new Promise((r) => setTimeout(r, 5000));
425
- }
426
- }
427
- if (sshReady) {
428
- // Step 2: Wait for cloud-init to finish
429
- cloudInitSpinner.text = "SSH ready — waiting for cloud-init to finish...";
430
- try {
431
- const ciResult = await sshExec(server.ip, "cloud-init status --wait");
432
- if (ciResult.code === 0) {
433
- cloudInitSpinner.succeed("Cloud-init completed");
434
- }
435
- else {
436
- cloudInitSpinner.warn("Cloud-init may not have finished — continuing anyway");
437
- }
438
- }
439
- catch {
440
- cloudInitSpinner.warn("Could not check cloud-init status — continuing anyway");
441
- }
442
- }
443
- else {
444
- cloudInitSpinner.warn("SSH not available after 5 min — continuing anyway");
445
- }
446
- }
447
- // Full setup: firewall + secure (BUG-1)
448
- if (fullSetup && hasValidIp) {
449
- try {
450
- assertValidIp(server.ip); // Defense-in-depth: IP validated before ssh-keygen
451
- spawnSync("ssh-keygen", ["-R", server.ip], { stdio: "ignore", env: sanitizedEnv() });
452
- }
453
- catch {
454
- // ssh-keygen not available or no entry — harmless
455
- }
456
- logger.title("Running full setup (firewall + security)...");
457
- try {
458
- await firewallSetup(server.ip, serverName, false, true);
459
- }
460
- catch (error) {
461
- logger.warning(`Firewall setup failed: ${getErrorMessage(error)}`);
462
- }
463
- try {
464
- await secureSetup(server.ip, serverName, undefined, false, true);
465
- }
466
- catch (error) {
467
- logger.warning(`Security setup failed: ${getErrorMessage(error)}`);
468
- }
469
- }
470
- else if (fullSetup && !hasValidIp) {
471
- logger.warning("Skipping full setup: server IP not available.");
472
- }
473
- // Show bare server info
474
- logger.title("Bare Server Ready!");
475
- console.log();
476
- logger.success(`Bare server ready!`);
477
- logger.info(`SSH: ssh root@${server.ip}`);
478
- logger.info(`IP: ${server.ip}`);
479
- logger.info("Mode: bare (no platform installed)");
480
- console.log();
481
- if (!fullSetup) {
482
- logger.info(" Secure your server:");
483
- logger.step(` quicklify firewall setup ${serverName}`);
484
- logger.step(` quicklify secure setup ${serverName}`);
485
- console.log();
486
- }
487
- logger.info(" Server saved to local config. Use 'quicklify list' to see all servers.");
488
- console.log();
489
- return;
490
- }
491
- // Full setup: auto-configure firewall + SSH hardening
492
- if (fullSetup && ready) {
493
- // Clear stale known_hosts entry (cloud providers reuse IPs)
494
- try {
495
- assertValidIp(server.ip); // Defense-in-depth: IP validated before ssh-keygen
496
- spawnSync("ssh-keygen", ["-R", server.ip], { stdio: "ignore", env: sanitizedEnv() });
497
- }
498
- catch {
499
- // ssh-keygen not available or no entry — harmless
500
- }
501
- logger.title("Running full setup (firewall + security)...");
502
- try {
503
- await firewallSetup(server.ip, serverName, false);
504
- }
505
- catch (error) {
506
- logger.warning(`Firewall setup failed: ${getErrorMessage(error)}`);
507
- }
508
- try {
509
- await secureSetup(server.ip, serverName, undefined, false, true);
510
- }
511
- catch (error) {
512
- logger.warning(`Security setup failed: ${getErrorMessage(error)}`);
513
- }
514
- }
515
- else if (fullSetup && !ready) {
516
- logger.warning("Skipping full setup: Coolify is not ready yet.");
517
- logger.info("Run manually later: quicklify firewall setup && quicklify secure setup");
518
- }
519
- // Success message
520
- logger.title("Deployment Successful!");
521
- console.log();
522
- logger.success(`Server IP: ${server.ip}`);
523
- logger.success(`Access Coolify: http://${server.ip}:8000`);
524
- console.log();
525
- if (ready) {
526
- logger.info("Coolify is ready! Open the URL above to get started.");
527
- if (!noOpen && hasValidIp) {
528
- openBrowser(`http://${server.ip}:8000`);
529
- }
530
- }
531
- else {
532
- logger.warning("Coolify did not respond yet. Please check in a few minutes.");
533
- logger.info(`You can check status later with: quicklify status ${server.ip}`);
534
- }
535
- // Onboarding: next steps
536
- console.log();
537
- logger.title("What's Next?");
538
- if (!fullSetup) {
539
- logger.info(" 1. Secure your server:");
540
- logger.step(` quicklify firewall setup ${serverName}`);
541
- logger.step(` quicklify secure setup ${serverName}`);
542
- console.log();
543
- logger.info(" 2. Add a domain with SSL:");
544
- logger.step(` quicklify domain add ${serverName} --domain example.com`);
545
- console.log();
546
- logger.info(" 3. Create your first backup:");
547
- logger.step(` quicklify backup ${serverName}`);
548
- console.log();
549
- logger.info(" Tip: Do steps 1-3 automatically next time:");
550
- logger.step(" quicklify init --full-setup");
551
- }
552
- else {
553
- logger.info(" 1. Add a domain with SSL:");
554
- logger.step(` quicklify domain add ${serverName} --domain example.com`);
555
- console.log();
556
- logger.info(" 2. Create your first backup:");
557
- logger.step(` quicklify backup ${serverName}`);
558
- }
559
- console.log();
560
- logger.info(" Check your environment anytime:");
561
- logger.step(" quicklify doctor");
562
- console.log();
563
- logger.info(" Server saved to local config. Use 'quicklify list' to see all servers.");
564
- console.log();
565
- logger.info(" Docs: https://github.com/omrfc/quicklify");
566
- console.log(" \u2b50 Love Quicklify? Give us a star: https://github.com/omrfc/quicklify \u2b50");
567
- console.log();
568
- }
569
- catch (error) {
570
- logger.error(`Deployment failed: ${getErrorMessage(error)}`);
571
- const hint = mapProviderError(error, providerChoice);
572
- if (hint) {
573
- logger.info(hint);
574
- }
575
- process.exit(1);
576
- }
577
- }
578
214
  //# sourceMappingURL=init.js.map