clever-tools 3.8.3 → 3.9.0

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 (79) hide show
  1. package/bin/clever.js +177 -427
  2. package/package.json +10 -3
  3. package/src/command-options.js +4 -10
  4. package/src/command-promise-handler.js +5 -7
  5. package/src/commands/accesslogs.js +9 -13
  6. package/src/commands/activity.js +10 -14
  7. package/src/commands/addon.js +68 -38
  8. package/src/commands/applications.js +9 -16
  9. package/src/commands/cancel-deploy.js +5 -9
  10. package/src/commands/config.js +7 -11
  11. package/src/commands/console.js +5 -9
  12. package/src/commands/create.js +5 -9
  13. package/src/commands/curl.js +7 -11
  14. package/src/commands/database.js +13 -16
  15. package/src/commands/delete.js +4 -8
  16. package/src/commands/deploy.js +10 -14
  17. package/src/commands/diag.js +9 -11
  18. package/src/commands/domain.js +348 -17
  19. package/src/commands/drain.js +10 -20
  20. package/src/commands/env.js +13 -17
  21. package/src/commands/link.js +3 -7
  22. package/src/commands/login.js +13 -15
  23. package/src/commands/logout.js +3 -7
  24. package/src/commands/logs.js +9 -13
  25. package/src/commands/makeDefault.js +3 -7
  26. package/src/commands/notify-email.js +9 -19
  27. package/src/commands/open.js +5 -9
  28. package/src/commands/profile.js +21 -12
  29. package/src/commands/published-config.js +11 -15
  30. package/src/commands/restart.js +7 -11
  31. package/src/commands/scale.js +3 -7
  32. package/src/commands/service.js +8 -18
  33. package/src/commands/ssh.js +4 -8
  34. package/src/commands/status.js +7 -11
  35. package/src/commands/stop.js +5 -9
  36. package/src/commands/tcp-redirs.js +11 -15
  37. package/src/commands/unlink.js +4 -8
  38. package/src/commands/version.js +4 -6
  39. package/src/commands/webhooks.js +8 -16
  40. package/src/format-table.js +4 -8
  41. package/src/initial-setup.js +49 -0
  42. package/src/load-package-json.cjs +5 -0
  43. package/src/logger.js +5 -8
  44. package/src/models/activity.js +3 -7
  45. package/src/models/addon.js +37 -56
  46. package/src/models/app_configuration.js +18 -31
  47. package/src/models/application.js +37 -61
  48. package/src/models/application_configuration.js +11 -15
  49. package/src/models/configuration.js +11 -15
  50. package/src/models/deployments.js +6 -10
  51. package/src/models/domain.js +6 -10
  52. package/src/models/drain.js +5 -13
  53. package/src/models/exit-strategy-option.js +4 -8
  54. package/src/models/fs-utils.js +3 -7
  55. package/src/models/git.js +19 -34
  56. package/src/models/ids-resolver.js +6 -12
  57. package/src/models/interact.js +2 -6
  58. package/src/models/isomorphic-http-with-agent.js +3 -9
  59. package/src/models/json-array.js +1 -3
  60. package/src/models/log-v4.js +10 -12
  61. package/src/models/log.js +11 -15
  62. package/src/models/namespaces.js +7 -13
  63. package/src/models/node-dns-resolver.js +43 -0
  64. package/src/models/notification.js +8 -17
  65. package/src/models/organisation.js +4 -10
  66. package/src/models/send-to-api.js +17 -13
  67. package/src/models/user.js +16 -9
  68. package/src/models/utils.js +2 -6
  69. package/src/models/variables.js +4 -10
  70. package/src/parsers.js +21 -71
  71. package/src/commands/networkgroups/commands.js +0 -7
  72. package/src/commands/networkgroups/index.js +0 -67
  73. package/src/commands/networkgroups/members.js +0 -80
  74. package/src/commands/networkgroups/peers.js +0 -83
  75. package/src/models/format-ng-table.js +0 -115
  76. package/src/models/format-string.js +0 -44
  77. package/src/models/networkgroup.js +0 -64
  78. package/src/models/wireguard-conf.js +0 -95
  79. package/src/models/wireguard.js +0 -75
@@ -1,19 +1,17 @@
1
- 'use strict';
1
+ import colors from 'colors/safe.js';
2
2
 
3
- const colors = require('colors/safe');
4
-
5
- const AppConfig = require('../models/app_configuration.js');
6
- const Application = require('../models/application.js');
7
- const git = require('../models/git.js');
8
- const Log = require('../models/log-v4.js');
9
- const Logger = require('../logger.js');
10
- const { getAllDeployments } = require('@clevercloud/client/cjs/api/v2/application.js');
11
- const { sendToApi } = require('../models/send-to-api.js');
12
- const ExitStrategy = require('../models/exit-strategy-option.js');
3
+ import * as AppConfig from '../models/app_configuration.js';
4
+ import * as Application from '../models/application.js';
5
+ import * as git from '../models/git.js';
6
+ import * as Log from '../models/log-v4.js';
7
+ import { Logger } from '../logger.js';
8
+ import { getAllDeployments } from '@clevercloud/client/esm/api/v2/application.js';
9
+ import { sendToApi } from '../models/send-to-api.js';
10
+ import * as ExitStrategy from '../models/exit-strategy-option.js';
13
11
 
14
12
  // Once the API call to redeploy() has been triggered successfully,
15
13
  // the rest (waiting for deployment state to evolve and displaying logs) is done with auto retry (resilient to network failures)
16
- async function deploy (params) {
14
+ export async function deploy (params) {
17
15
  const { alias, branch: branchName, tag: tagName, quiet, force, follow, 'same-commit-policy': sameCommitPolicy, 'exit-on': exitOnDeploy } = params.options;
18
16
 
19
17
  const exitStrategy = ExitStrategy.get(follow, exitOnDeploy);
@@ -105,5 +103,3 @@ async function getBranchToDeploy (branchName, tagName) {
105
103
  return await git.getFullBranch(branchName);
106
104
  }
107
105
  }
108
-
109
- module.exports = { deploy };
@@ -1,16 +1,16 @@
1
- 'use strict';
1
+ import os from 'node:os';
2
2
 
3
- const os = require('os');
3
+ import { releaseInfo as getLinuxInfos } from 'linux-release-info';
4
+ import colors from 'colors/safe.js';
4
5
 
5
- const { releaseInfo: getLinuxInfos } = require('linux-release-info');
6
- const colors = require('colors/safe');
6
+ import { Logger } from '../logger.js';
7
+ import { getPackageJson } from '../load-package-json.cjs';
8
+ import * as User from '../models/user.js';
9
+ import { conf, loadOAuthConf } from '../models/configuration.js';
7
10
 
8
- const Logger = require('../logger.js');
9
- const pkg = require('../../package.json');
10
- const User = require('../models/user.js');
11
- const { conf, loadOAuthConf } = require('../models/configuration.js');
11
+ const pkg = getPackageJson();
12
12
 
13
- async function diag (params) {
13
+ export async function diag (params) {
14
14
  const { format } = params.options;
15
15
 
16
16
  /** @type {string} */
@@ -93,5 +93,3 @@ async function diag (params) {
93
93
  }
94
94
  }
95
95
  }
96
-
97
- module.exports = { diag };
@@ -1,16 +1,28 @@
1
- 'use strict';
2
-
3
- const Application = require('../models/application.js');
4
- const Logger = require('../logger.js');
5
- const {
6
- get: getApp,
1
+ import * as Application from '../models/application.js';
2
+ import { Logger } from '../logger.js';
3
+ import {
7
4
  addDomain,
8
- getFavouriteDomain: getFavouriteDomainWithError,
5
+ get as getApp,
6
+ getAllDomains,
7
+ getFavouriteDomain as getFavouriteDomainWithError,
9
8
  markFavouriteDomain,
10
- unmarkFavouriteDomain,
11
9
  removeDomain,
12
- } = require('@clevercloud/client/cjs/api/v2/application.js');
13
- const { sendToApi } = require('../models/send-to-api.js');
10
+ unmarkFavouriteDomain,
11
+ } from '@clevercloud/client/esm/api/v2/application.js';
12
+ import { getSummary } from '@clevercloud/client/cjs/api/v2/user.js';
13
+ import { sendToApi } from '../models/send-to-api.js';
14
+ import colors from 'colors/safe.js';
15
+ import { parse as parseDomain } from 'tldts';
16
+ import { diagDomainConfig } from '@clevercloud/client/esm/utils/diag-domain-config.js';
17
+ import { sortDomains } from '@clevercloud/client/esm/utils/domains.js';
18
+ import { DnsResolver } from '../models/node-dns-resolver.js';
19
+ import _ from 'lodash';
20
+
21
+ /**
22
+ * @typedef {import('@clevercloud/client/esm/utils/diag-domain-config.types.js').DomainInfo} DomainInfo
23
+ * @typedef {import('@clevercloud/client/esm/utils/diag-domain-config.types.js').ResolveDnsResult} ResolveDnsResult
24
+ * @typedef {import('@clevercloud/client/esm/utils/diag-domain-config.types.js').DomainDiag} DomainDiag
25
+ */
14
26
 
15
27
  function getFavouriteDomain ({ ownerId, appId }) {
16
28
  return getFavouriteDomainWithError({ id: ownerId, appId })
@@ -25,7 +37,7 @@ function getFavouriteDomain ({ ownerId, appId }) {
25
37
  });
26
38
  }
27
39
 
28
- async function list (params) {
40
+ export async function list (params) {
29
41
  const { alias, app: appIdOrName } = params.options;
30
42
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
31
43
 
@@ -39,7 +51,7 @@ async function list (params) {
39
51
  });
40
52
  }
41
53
 
42
- async function add (params) {
54
+ export async function add (params) {
43
55
  const [fqdn] = params.args;
44
56
  const { alias, app: appIdOrName } = params.options;
45
57
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
@@ -49,7 +61,7 @@ async function add (params) {
49
61
  Logger.println('Your domain has been successfully saved');
50
62
  }
51
63
 
52
- async function getFavourite (params) {
64
+ export async function getFavourite (params) {
53
65
  const { alias, app: appIdOrName } = params.options;
54
66
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
55
67
 
@@ -62,7 +74,7 @@ async function getFavourite (params) {
62
74
  return Logger.println(favouriteDomain);
63
75
  }
64
76
 
65
- async function setFavourite (params) {
77
+ export async function setFavourite (params) {
66
78
  const [fqdn] = params.args;
67
79
  const { alias, app: appIdOrName } = params.options;
68
80
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
@@ -71,7 +83,7 @@ async function setFavourite (params) {
71
83
  Logger.println('Your favourite domain has been successfully set');
72
84
  }
73
85
 
74
- async function unsetFavourite (params) {
86
+ export async function unsetFavourite (params) {
75
87
  const { alias, app: appIdOrName } = params.options;
76
88
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
77
89
 
@@ -79,7 +91,7 @@ async function unsetFavourite (params) {
79
91
  Logger.println('Favourite domain has been successfully unset');
80
92
  }
81
93
 
82
- async function rm (params) {
94
+ export async function rm (params) {
83
95
  const [fqdn] = params.args;
84
96
  const { alias, app: appIdOrName } = params.options;
85
97
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
@@ -89,4 +101,323 @@ async function rm (params) {
89
101
  Logger.println('Your domain has been successfully removed');
90
102
  }
91
103
 
92
- module.exports = { list, add, getFavourite, setFavourite, unsetFavourite, rm };
104
+ export async function diagApplication (params) {
105
+ const dnsResolver = new DnsResolver();
106
+ const allDomainDiagnostics = [];
107
+ const { alias, app: appIdOrName, format, filter } = params.options;
108
+ const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
109
+ const hasDomainFilter = filter.length > 0;
110
+ let hasError = false;
111
+
112
+ const app = await getApp({ id: ownerId, appId }).then(sendToApi);
113
+ const expectedDnsForPublicLoadBalancer = await getDefaultLoadBalancersDnsInfo({ id: ownerId, appId }).then(sendToApi);
114
+
115
+ const loadBalancerDnsConfig = {
116
+ aRecords: expectedDnsForPublicLoadBalancer[0].dns.a,
117
+ cnameRecord: expectedDnsForPublicLoadBalancer[0].dns.cname,
118
+ };
119
+
120
+ const filteredDomains = app.vhosts.filter(({ fqdn }) => fqdn.includes(filter));
121
+ const domains = getParsedDomains(filteredDomains);
122
+ const sortedDomains = domains.sort(sortDomains);
123
+
124
+ for (const { hostname, pathPrefix, isApex } of sortedDomains) {
125
+ const resolvedDnsConfig = {
126
+ aRecords: await dnsResolver.resolveA(hostname),
127
+ cnameRecords: await dnsResolver.resolveCname(hostname) ?? [],
128
+ };
129
+
130
+ const diagnosticResults = diagDomainConfig({
131
+ hostname,
132
+ pathPrefix,
133
+ isApex,
134
+ }, resolvedDnsConfig, loadBalancerDnsConfig);
135
+ allDomainDiagnostics.push({ ...diagnosticResults, resolvedDnsConfig });
136
+
137
+ if (diagnosticResults.diagSummary === 'invalid' || diagnosticResults.diagSummary === 'no-config') {
138
+ hasError = true;
139
+ }
140
+ }
141
+
142
+ switch (format) {
143
+ case 'json':
144
+ Logger.printJson(allDomainDiagnostics);
145
+ break;
146
+ default: {
147
+ allDomainDiagnostics.forEach((diag) => reportDomainDiagnostics(diag));
148
+
149
+ if (allDomainDiagnostics.length === 0 && !hasDomainFilter) {
150
+ Logger.println(`\nNo domain associated to the app "${app.name}" (${app.id})`);
151
+ }
152
+
153
+ if (allDomainDiagnostics.length === 0 && hasDomainFilter) {
154
+ Logger.println(`\nNo domain matches "${filter}" for the app "${app.name}" (${app.id})`);
155
+ }
156
+ }
157
+ }
158
+
159
+ if (hasError) {
160
+ throw new Error('At least one of the domains is misconfigured');
161
+ }
162
+ }
163
+
164
+ export async function overview (params) {
165
+
166
+ const { format, filter } = params.options;
167
+
168
+ const summary = await getSummary().then(sendToApi);
169
+ const consoleUrl = summary.user.partnerConsoleUrl;
170
+
171
+ const applications = [
172
+ ...summary.user.applications.map((app) => {
173
+ return { ownerName: summary.user.name, ownerId: summary.user.id, ...app };
174
+ }),
175
+ ...summary.organisations.flatMap((o) => {
176
+ return o.applications.map((app) => {
177
+ return { ownerName: o.name, ownerId: o.id, ...app };
178
+ });
179
+ }),
180
+ ];
181
+
182
+ const applicationsWithDomains = await Promise.all(
183
+ applications.map(async (app) => {
184
+ const domains = await getAllDomains({ id: app.ownerId, appId: app.id }).then(sendToApi);
185
+ return { app, domains };
186
+ }),
187
+ );
188
+
189
+ const applicationsWithParsedDomain = applicationsWithDomains
190
+ .flatMap(({ app, domains }) => {
191
+ return domains
192
+ .filter((domain) => filter == null || domain.fqdn.includes(filter))
193
+ .map((domain) => {
194
+
195
+ const parsedDomain = parseDomain(domain.fqdn);
196
+ const pathname = new URL('https://' + domain.fqdn).pathname;
197
+ const subdomains = parsedDomain.subdomain !== '' ? parsedDomain.subdomain.split('.') : [];
198
+
199
+ // We're trying to create a propertyPath for lodash to create a tree structure object,
200
+ // the propertyPath for `aaa.bbb.ccc.example.com/the-path` would be:
201
+ // ["example.com", "example.com.ccc", "example.com.ccc.bbb", "example.com.ccc.bbb.aaa", "/path-aaa"]",
202
+
203
+ const sortSegments = [parsedDomain.domain, ...subdomains.reverse()];
204
+ const propertyPath = sortSegments.map((item, i, all) => {
205
+ return all.slice(0, i + 1).reverse().join('.');
206
+ });
207
+ propertyPath.push(pathname);
208
+
209
+ return {
210
+ ownerId: app.ownerId,
211
+ ownerName: app.ownerName,
212
+ appId: app.id,
213
+ appName: app.name,
214
+ appConsoleUrl: `${consoleUrl}/goto/${app.id}`,
215
+ appVariantSlug: app.variantSlug,
216
+ domain: domain.fqdn,
217
+ propetyPath: propertyPath,
218
+ };
219
+ });
220
+ });
221
+
222
+ const applicationsWithParsedDomainAsTree = {};
223
+ for (const { propetyPath, ...appWithDomain } of applicationsWithParsedDomain) {
224
+ _.set(applicationsWithParsedDomainAsTree, propetyPath, appWithDomain);
225
+ }
226
+
227
+ const applicationsWithParsedDomainAsSortedTree = recursiveSort(applicationsWithParsedDomainAsTree);
228
+
229
+ switch (format) {
230
+ case 'json':
231
+ Logger.printJson(applicationsWithParsedDomainAsSortedTree);
232
+ break;
233
+ case 'human':
234
+ default:
235
+ if (Object.keys(applicationsWithParsedDomainAsSortedTree).length === 0) {
236
+ if (filter?.length > 0) {
237
+ Logger.println(`No matches for filter "${filter}"`);
238
+ }
239
+ else {
240
+ Logger.println('No domains');
241
+ }
242
+ }
243
+ else {
244
+ recursiveDisplay(applicationsWithParsedDomainAsSortedTree);
245
+ }
246
+ break;
247
+ }
248
+ }
249
+
250
+ /** @param {DomainDiag & { resolvedDnsConfig: ResolveDnsResult }} domainDiag */
251
+ function reportDomainDiagnostics ({ hostname, pathPrefix, resolvedDnsConfig, diagDetails, diagSummary }) {
252
+
253
+ const validDiags = diagDetails.filter((diag) => diag.code === 'valid-a');
254
+ const unknownDiags = diagDetails.filter((diag) => diag.code === 'unknown-a');
255
+ const missingDiags = diagDetails.filter((diag) => diag.code === 'missing-a');
256
+ const suggestedCname = diagDetails.find((diag) => diag.code === 'suggested-cname');
257
+ const missingCname = diagDetails.find((diag) => diag.code === 'missing-cname');
258
+ const unknownCname = diagDetails.find((diag) => diag.code === 'unknown-cname');
259
+
260
+ const hasARecords = resolvedDnsConfig.aRecords.length > 0;
261
+ const hasCnameRecord = resolvedDnsConfig.cnameRecords.length > 0;
262
+
263
+ Logger.println('\n' + colors.blue(hostname) + colors.yellow(pathPrefix) + '\n');
264
+
265
+ switch (diagSummary) {
266
+ case 'managed':
267
+ printlnWithIndent(colors.green('✔ Managed by Clever Cloud'), 2);
268
+ printlnWithIndent('ⓘ cleverapps.io domains should only be used for testing purposes', 2);
269
+ break;
270
+ case 'no-config':
271
+ printlnWithIndent(colors.red('✘ No DNS configuration found'), 2);
272
+ break;
273
+ case 'valid':
274
+ printlnWithIndent(colors.green('✔ Your configuration is valid'), 2);
275
+ break;
276
+ case 'invalid':
277
+ printlnWithIndent(colors.red('✘ Something is wrong with your configuration'), 2);
278
+ break;
279
+ case 'incomplete':
280
+ printlnWithIndent(colors.yellow('⚠ Your configuration is incomplete'), 2);
281
+ break;
282
+ }
283
+
284
+ // Valid records
285
+ if (validDiags.length > 0) {
286
+ Logger.println('');
287
+ validDiags.forEach((diag) => {
288
+ const source = hasCnameRecord ? `(from CNAME ${resolvedDnsConfig.cnameRecords[0]}.)` : '';
289
+ printlnWithIndent(`${diag.record.value} ${colors.green('✔ A Record OK')} ${source}`, 2);
290
+ });
291
+ }
292
+
293
+ // Replace A with CNAME
294
+ if (diagSummary === 'valid' && suggestedCname != null) {
295
+ Logger.println('');
296
+ printlnWithIndent('ⓘ You can replace your A records with this CNAME:', 2);
297
+ printlnWithIndent(suggestedCname.record.value, 6);
298
+ }
299
+
300
+ // Replace A Records with CNAME
301
+ if (missingCname != null && unknownCname == null && (diagSummary === 'invalid' || diagSummary === 'incomplete')) {
302
+ const cnameToUse = suggestedCname != null ? suggestedCname.record.value : missingCname.record.value;
303
+
304
+ Logger.println('');
305
+ printlnWithIndent('⇄ Replace your A records with this CNAME:', 2);
306
+ printlnWithIndent(cnameToUse, 6);
307
+ Logger.println('');
308
+ printlnWithIndent('or:', 2);
309
+ }
310
+
311
+ // Replace unknown CNAME with missing CNAME
312
+ if ((diagSummary === 'invalid') && missingCname != null && unknownCname != null) {
313
+ Logger.println('');
314
+ printlnWithIndent('➖Remove this CNAME record:', 2);
315
+ printlnWithIndent(unknownCname.record.value + '. ', 6);
316
+ Logger.println('');
317
+ printlnWithIndent('➕Add this CNAME record instead:', 2);
318
+ printlnWithIndent(missingCname.record.value, 6);
319
+
320
+ if (hasARecords) {
321
+ Logger.println('');
322
+ printlnWithIndent('or:', 2);
323
+ }
324
+ }
325
+
326
+ // Add CNAME
327
+ if (diagSummary === 'no-config' && missingCname != null) {
328
+ Logger.println('');
329
+ printlnWithIndent('➕Add this CNAME record:', 2);
330
+ printlnWithIndent(missingCname.record.value, 6);
331
+ }
332
+
333
+ // Remove Unknown records
334
+ if (unknownDiags.length > 0) {
335
+ Logger.println('');
336
+ printlnWithIndent('➖Remove these A records:', 2);
337
+
338
+ unknownDiags.forEach((diag) => {
339
+ const source = hasCnameRecord ? `(from CNAME ${resolvedDnsConfig.cnameRecords[0]}.)` : '';
340
+ printlnWithIndent(diag.record.value + source, 6);
341
+ });
342
+ }
343
+
344
+ // Add missing A records
345
+ if (missingDiags.length > 0) {
346
+ Logger.println('');
347
+ printlnWithIndent('➕Add these A records:', 2);
348
+
349
+ missingDiags.forEach((missingDiag) => printlnWithIndent(missingDiag.record.value, 6));
350
+ }
351
+ }
352
+
353
+ // TODO: put in clever client
354
+ function getDefaultLoadBalancersDnsInfo (params) {
355
+ return Promise.resolve({
356
+ method: 'get',
357
+ url: `/v4/load-balancers/organisations/${params.id}/applications/${params.appId}/load-balancers/default`,
358
+ headers: {
359
+ Accept: 'application/json',
360
+ },
361
+ // no query params
362
+ // no body
363
+ });
364
+ }
365
+
366
+ /**
367
+ * Parses domain information from vhosts array
368
+ *
369
+ * @param {Array<{ fqdn: string }>} vhosts
370
+ * @returns {Array<Partial<DomainInfo>>} Array of parsed domain objects
371
+ */
372
+ function getParsedDomains (vhosts) {
373
+ return vhosts.map(({ fqdn }) => {
374
+ const { hostname, pathname } = new URL('https://' + fqdn);
375
+ const { subdomain } = parseDomain(fqdn);
376
+
377
+ return { hostname, pathPrefix: pathname, isApex: subdomain === '' };
378
+ });
379
+ }
380
+
381
+ /**
382
+ * Prints a line of text with specified indentation.
383
+ *
384
+ * @param {string} text - The text to be printed.
385
+ * @param {number} indentLevel - The number of spaces to indent the text.
386
+ */
387
+ function printlnWithIndent (text, indentLevel) {
388
+ Logger.println(' '.repeat(indentLevel) + text);
389
+ }
390
+
391
+ function recursiveSort (obj) {
392
+
393
+ if (typeof obj === 'object' && obj.appId != null) {
394
+ return obj;
395
+ }
396
+
397
+ const sortedObj = {};
398
+ Object.keys(obj).sort((a, b) => a.localeCompare(b)).forEach((key) => {
399
+ sortedObj[key] = recursiveSort(obj[key]);
400
+ });
401
+
402
+ return sortedObj;
403
+ }
404
+
405
+ function recursiveDisplay (obj, indentLevel = 0) {
406
+
407
+ if (typeof obj === 'object' && obj.appId != null) {
408
+ printlnWithIndent(`${obj.ownerName} | ${obj.appName} (${obj.appVariantSlug})`, indentLevel);
409
+ printlnWithIndent(colors.blue(obj.appConsoleUrl), indentLevel);
410
+ return;
411
+ }
412
+
413
+ for (const [propertyPath, subObj] of Object.entries(obj)) {
414
+ if (propertyPath !== '/') {
415
+ Logger.println('');
416
+ printlnWithIndent(colors.yellow(propertyPath), indentLevel);
417
+ recursiveDisplay(subObj, indentLevel + 2);
418
+ }
419
+ else {
420
+ recursiveDisplay(subObj, indentLevel);
421
+ }
422
+ }
423
+ }
@@ -1,11 +1,9 @@
1
- 'use strict';
1
+ import * as Application from '../models/application.js';
2
+ import { createDrainBody } from '../models/drain.js';
3
+ import { Logger } from '../logger.js';
2
4
 
3
- const Application = require('../models/application.js');
4
- const { createDrainBody } = require('../models/drain.js');
5
- const Logger = require('../logger.js');
6
-
7
- const { getDrains, createDrain, deleteDrain, updateDrainState } = require('@clevercloud/client/cjs/api/v2/log.js');
8
- const { sendToApi } = require('../models/send-to-api.js');
5
+ import { getDrains, createDrain, deleteDrain, updateDrainState } from '@clevercloud/client/esm/api/v2/log.js';
6
+ import { sendToApi } from '../models/send-to-api.js';
9
7
 
10
8
  // TODO: This could be useful in other commands
11
9
  async function getAppOrAddonId ({ alias, appIdOrName, addonId }) {
@@ -14,7 +12,7 @@ async function getAppOrAddonId ({ alias, appIdOrName, addonId }) {
14
12
  : await Application.resolveId(appIdOrName, alias).then(({ appId }) => appId);
15
13
  }
16
14
 
17
- async function list (params) {
15
+ export async function list (params) {
18
16
  const { alias, app: appIdOrName, addon: addonId, format } = params.options;
19
17
 
20
18
  const appIdOrAddonId = await getAppOrAddonId({ alias, appIdOrName, addonId });
@@ -54,7 +52,7 @@ async function list (params) {
54
52
  }
55
53
  }
56
54
 
57
- async function create (params) {
55
+ export async function create (params) {
58
56
  const [drainTargetType, drainTargetURL] = params.args;
59
57
  const { alias, app: appIdOrName, addon: addonId, username, password, 'api-key': apiKey, 'index-prefix': indexPrefix, 'sd-params': structuredDataParameters } = params.options;
60
58
  const drainTargetCredentials = { username, password };
@@ -67,7 +65,7 @@ async function create (params) {
67
65
  Logger.println('Your drain has been successfully saved');
68
66
  }
69
67
 
70
- async function rm (params) {
68
+ export async function rm (params) {
71
69
  const [drainId] = params.args;
72
70
  const { alias, app: appIdOrName, addon: addonId } = params.options;
73
71
 
@@ -77,7 +75,7 @@ async function rm (params) {
77
75
  Logger.println('Your drain has been successfully removed');
78
76
  }
79
77
 
80
- async function enable (params) {
78
+ export async function enable (params) {
81
79
  const [drainId] = params.args;
82
80
  const { alias, app: appIdOrName, addon: addonId } = params.options;
83
81
 
@@ -87,7 +85,7 @@ async function enable (params) {
87
85
  Logger.println('Your drain has been enabled');
88
86
  }
89
87
 
90
- async function disable (params) {
88
+ export async function disable (params) {
91
89
  const [drainId] = params.args;
92
90
  const { alias, app: appIdOrName, addon: addonId } = params.options;
93
91
 
@@ -96,11 +94,3 @@ async function disable (params) {
96
94
 
97
95
  Logger.println('Your drain has been disabled');
98
96
  }
99
-
100
- module.exports = {
101
- list,
102
- create,
103
- rm,
104
- enable,
105
- disable,
106
- };
@@ -1,14 +1,12 @@
1
- 'use strict';
2
-
3
- const Application = require('../models/application.js');
4
- const Logger = require('../logger.js');
5
- const colors = require('colors/safe');
6
- const variables = require('../models/variables.js');
7
- const { sendToApi } = require('../models/send-to-api.js');
8
- const { toNameEqualsValueString, validateName } = require('@clevercloud/client/cjs/utils/env-vars.js');
9
- const application = require('@clevercloud/client/cjs/api/v2/application.js');
10
-
11
- async function list (params) {
1
+ import * as Application from '../models/application.js';
2
+ import { Logger } from '../logger.js';
3
+ import colors from 'colors/safe.js';
4
+ import * as variables from '../models/variables.js';
5
+ import { sendToApi } from '../models/send-to-api.js';
6
+ import { toNameEqualsValueString, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
7
+ import * as application from '@clevercloud/client/esm/api/v2/application.js';
8
+
9
+ export async function list (params) {
12
10
  const { alias, app: appIdOrName, 'add-export': addExportsOption, format } = params.options;
13
11
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
14
12
 
@@ -60,7 +58,7 @@ async function list (params) {
60
58
  }
61
59
  }
62
60
 
63
- async function set (params) {
61
+ export async function set (params) {
64
62
  const [envName, value] = params.args;
65
63
  const { alias, app: appIdOrName } = params.options;
66
64
 
@@ -76,7 +74,7 @@ async function set (params) {
76
74
  Logger.println('Your environment variable has been successfully saved');
77
75
  };
78
76
 
79
- async function rm (params) {
77
+ export async function rm (params) {
80
78
  const [envName] = params.args;
81
79
  const { alias, app: appIdOrName } = params.options;
82
80
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
@@ -86,7 +84,7 @@ async function rm (params) {
86
84
  Logger.println('Your environment variable has been successfully removed');
87
85
  };
88
86
 
89
- async function importEnv (params) {
87
+ export async function importEnv (params) {
90
88
  const { alias, app: appIdOrName, json } = params.options;
91
89
  const format = json ? 'json' : 'name-equals-value';
92
90
  const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
@@ -97,7 +95,7 @@ async function importEnv (params) {
97
95
  Logger.println('Environment variables have been set');
98
96
  };
99
97
 
100
- async function importVarsFromLocalEnv (params) {
98
+ export async function importVarsFromLocalEnv (params) {
101
99
  const [envNames] = params.args;
102
100
  const { alias, app: appIdOrName } = params.options;
103
101
 
@@ -117,5 +115,3 @@ async function importVarsFromLocalEnv (params) {
117
115
 
118
116
  Logger.println('Your environment variables have been successfully saved');
119
117
  };
120
-
121
- module.exports = { list, set, rm, importEnv, importVarsFromLocalEnv };
@@ -1,9 +1,7 @@
1
- 'use strict';
1
+ import * as Application from '../models/application.js';
2
+ import { Logger } from '../logger.js';
2
3
 
3
- const Application = require('../models/application.js');
4
- const Logger = require('../logger.js');
5
-
6
- async function link (params) {
4
+ export async function link (params) {
7
5
  const [app] = params.args;
8
6
  const { org: orgaIdOrName, alias } = params.options;
9
7
 
@@ -15,5 +13,3 @@ async function link (params) {
15
13
 
16
14
  Logger.println('Your application has been successfully linked!');
17
15
  }
18
-
19
- module.exports = { link };