clever-tools 3.11.0 → 3.12.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.
- package/bin/clever.js +140 -1
- package/package.json +4 -3
- package/src/clever-client/auth-bridge.js +61 -0
- package/src/clever-client/ng.js +18 -0
- package/src/commands/addon.js +22 -21
- package/src/commands/config.js +3 -3
- package/src/commands/curl.js +17 -17
- package/src/commands/database.js +9 -9
- package/src/commands/domain.js +13 -16
- package/src/commands/env.js +15 -8
- package/src/commands/kv.js +1 -1
- package/src/commands/link.js +4 -2
- package/src/commands/ng.js +202 -0
- package/src/commands/profile.js +9 -6
- package/src/commands/published-config.js +7 -7
- package/src/commands/stop.js +2 -2
- package/src/commands/tcp-redirs.js +5 -5
- package/src/commands/tokens.js +137 -0
- package/src/experimental-features.js +46 -2
- package/src/lib/ng-print.js +195 -0
- package/src/logger.js +3 -0
- package/src/models/activity.js +2 -3
- package/src/models/addon.js +4 -4
- package/src/models/application.js +26 -16
- package/src/models/configuration.js +1 -0
- package/src/models/namespaces.js +3 -2
- package/src/models/ng-resources.js +270 -0
- package/src/models/ng.js +276 -0
- package/src/models/send-to-api.js +18 -0
- package/src/parsers.js +57 -3
- package/src/prompt-password.js +10 -0
package/bin/clever.js
CHANGED
|
@@ -16,7 +16,7 @@ import * as Application from '../src/models/application.js';
|
|
|
16
16
|
import { AVAILABLE_ZONES } from '../src/models/application.js';
|
|
17
17
|
import { EXPERIMENTAL_FEATURES } from '../src/experimental-features.js';
|
|
18
18
|
import { getExitOnOption, getOutputFormatOption, getSameCommitPolicyOption } from '../src/command-options.js';
|
|
19
|
-
import { getFeatures } from '../src/models/configuration.js';
|
|
19
|
+
import { getFeatures, conf } from '../src/models/configuration.js';
|
|
20
20
|
|
|
21
21
|
import * as Addon from '../src/models/addon.js';
|
|
22
22
|
import * as ApplicationConfiguration from '../src/models/application_configuration.js';
|
|
@@ -44,6 +44,7 @@ import * as login from '../src/commands/login.js';
|
|
|
44
44
|
import * as logout from '../src/commands/logout.js';
|
|
45
45
|
import * as logs from '../src/commands/logs.js';
|
|
46
46
|
import * as makeDefault from '../src/commands/makeDefault.js';
|
|
47
|
+
import * as ng from '../src/commands/ng.js';
|
|
47
48
|
import * as notifyEmail from '../src/commands/notify-email.js';
|
|
48
49
|
import * as open from '../src/commands/open.js';
|
|
49
50
|
import * as consoleModule from '../src/commands/console.js';
|
|
@@ -56,6 +57,7 @@ import * as ssh from '../src/commands/ssh.js';
|
|
|
56
57
|
import * as status from '../src/commands/status.js';
|
|
57
58
|
import * as stop from '../src/commands/stop.js';
|
|
58
59
|
import * as tcpRedirs from '../src/commands/tcp-redirs.js';
|
|
60
|
+
import * as tokens from '../src/commands/tokens.js';
|
|
59
61
|
import * as unlink from '../src/commands/unlink.js';
|
|
60
62
|
import * as version from '../src/commands/version.js';
|
|
61
63
|
import * as webhooks from '../src/commands/webhooks.js';
|
|
@@ -94,6 +96,39 @@ async function run () {
|
|
|
94
96
|
kvIdOrName: cliparse.argument('kv-id', {
|
|
95
97
|
description: 'Add-on/Real ID (or name, if unambiguous) of a Materia KV or Redis® add-on',
|
|
96
98
|
}),
|
|
99
|
+
apiTokenId: cliparse.argument('api-token-id', { description: 'API token ID' }),
|
|
100
|
+
apiTokenName: cliparse.argument('api-token-name', { description: 'API token name' }),
|
|
101
|
+
ngId: cliparse.argument('id', {
|
|
102
|
+
description: 'Network Group ID',
|
|
103
|
+
parser: Parsers.ngResourceType,
|
|
104
|
+
}),
|
|
105
|
+
ngLabel: cliparse.argument('ng-label', {
|
|
106
|
+
description: 'Network Group label',
|
|
107
|
+
parser: Parsers.ngResourceType,
|
|
108
|
+
}),
|
|
109
|
+
ngIdOrLabel: cliparse.argument('ng-id-or-label', {
|
|
110
|
+
description: 'Network Group ID or label',
|
|
111
|
+
parser: Parsers.ngResourceType,
|
|
112
|
+
}),
|
|
113
|
+
ngDescription: cliparse.argument('ng-description', {
|
|
114
|
+
description: 'Network Group description',
|
|
115
|
+
}),
|
|
116
|
+
ngExternalPeerLabel: cliparse.argument('external-peer-label', {
|
|
117
|
+
description: 'External peer label',
|
|
118
|
+
parser: Parsers.ngResourceType,
|
|
119
|
+
}),
|
|
120
|
+
ngExternalIdOrLabel: cliparse.argument('external-peer-id-or-label', {
|
|
121
|
+
description: 'External peer ID or label',
|
|
122
|
+
parser: Parsers.ngResourceType,
|
|
123
|
+
}),
|
|
124
|
+
ngAnyIdOrLabel: cliparse.argument('id-or-label', {
|
|
125
|
+
description: 'ID or Label of a Network group, a member or an (external) peer',
|
|
126
|
+
parser: Parsers.ngResourceType,
|
|
127
|
+
}),
|
|
128
|
+
wgPublicKey: cliparse.argument('public-key', {
|
|
129
|
+
metavar: 'public_key',
|
|
130
|
+
description: 'Wireguard public key of the external peer to link to a Network Group',
|
|
131
|
+
}),
|
|
97
132
|
addonIdOrName: cliparse.argument('addon-id', {
|
|
98
133
|
description: 'Add-on ID (or name, if unambiguous)',
|
|
99
134
|
parser: Parsers.addonIdOrName,
|
|
@@ -147,6 +182,36 @@ async function run () {
|
|
|
147
182
|
|
|
148
183
|
// OPTIONS
|
|
149
184
|
const opts = {
|
|
185
|
+
apiTokenExpiration: cliparse.option('expiration', {
|
|
186
|
+
aliases: ['e'],
|
|
187
|
+
metavar: 'expiration',
|
|
188
|
+
parser: Parsers.futureDateOrDuration,
|
|
189
|
+
description: 'Duration until API token expiration (e.g.: 1h, 4d, 2w, 6M), default 1y',
|
|
190
|
+
}),
|
|
191
|
+
// Network Groups options
|
|
192
|
+
ngDescription: cliparse.option('description', {
|
|
193
|
+
metavar: 'description',
|
|
194
|
+
description: 'Network Group description',
|
|
195
|
+
}),
|
|
196
|
+
ngMembersIdsToLink: cliparse.option('link', {
|
|
197
|
+
metavar: 'members_ids',
|
|
198
|
+
description: "Comma separated list of members IDs to link to a Network Group ('app_xxx', 'addon_xxx', 'external_xxx')",
|
|
199
|
+
parser: Parsers.commaSeparated,
|
|
200
|
+
}),
|
|
201
|
+
ngMemberLabel: cliparse.option('label', {
|
|
202
|
+
required: false,
|
|
203
|
+
metavar: 'member_label',
|
|
204
|
+
description: 'The member label',
|
|
205
|
+
parser: Parsers.ngResourceType,
|
|
206
|
+
}),
|
|
207
|
+
ngPeerGetConfig: cliparse.flag('config', {
|
|
208
|
+
description: 'Get the Wireguard configuration of an external peer',
|
|
209
|
+
}),
|
|
210
|
+
ngResourceType: cliparse.option('type', {
|
|
211
|
+
metavar: 'type',
|
|
212
|
+
description: 'Type of resource to look for (NetworkGroup, Member, CleverPeer, ExternalPeer)',
|
|
213
|
+
parser: Parsers.ngValidType,
|
|
214
|
+
}),
|
|
150
215
|
sourceableEnvVarsList: cliparse.flag('add-export', { description: 'Display sourceable env variables setting' }),
|
|
151
216
|
logsFormat: getOutputFormatOption(['json-stream']),
|
|
152
217
|
activityFormat: getOutputFormatOption(['json-stream']),
|
|
@@ -746,6 +811,56 @@ async function run () {
|
|
|
746
811
|
args: [args.alias],
|
|
747
812
|
}, makeDefault.makeDefault);
|
|
748
813
|
|
|
814
|
+
// NETWORK GROUP COMMANDS
|
|
815
|
+
const ngCreateExternalPeerCommand = cliparse.command('external', {
|
|
816
|
+
description: 'Create an external peer in a Network Group',
|
|
817
|
+
args: [args.ngExternalPeerLabel, args.ngIdOrLabel, args.wgPublicKey],
|
|
818
|
+
}, ng.createExternalPeer);
|
|
819
|
+
const ngDeleteExternalPeerCommand = cliparse.command('external', {
|
|
820
|
+
description: 'Delete an external peer from a Network Group',
|
|
821
|
+
args: [args.ngExternalIdOrLabel, args.ngIdOrLabel],
|
|
822
|
+
}, ng.deleteExternalPeer);
|
|
823
|
+
const ngCreateCommand = cliparse.command('create', {
|
|
824
|
+
description: 'Create a Network Group',
|
|
825
|
+
args: [args.ngLabel],
|
|
826
|
+
privateOptions: [opts.ngMembersIdsToLink, opts.ngDescription, opts.optTags],
|
|
827
|
+
commands: [ngCreateExternalPeerCommand],
|
|
828
|
+
}, ng.createNg);
|
|
829
|
+
const ngDeleteCommand = cliparse.command('delete', {
|
|
830
|
+
description: 'Delete a Network Group',
|
|
831
|
+
args: [args.ngIdOrLabel],
|
|
832
|
+
commands: [ngDeleteExternalPeerCommand],
|
|
833
|
+
}, ng.deleteNg);
|
|
834
|
+
const ngLinkCommand = cliparse.command('link', {
|
|
835
|
+
description: 'Link an application or a database add-on by its ID to a Network Group',
|
|
836
|
+
args: [args.ngAnyIdOrLabel, args.ngIdOrLabel],
|
|
837
|
+
}, ng.linkToNg);
|
|
838
|
+
const ngUnlinkCommand = cliparse.command('unlink', {
|
|
839
|
+
description: 'Unlink an application or a database add-on by its ID from a Network Group',
|
|
840
|
+
args: [args.ngAnyIdOrLabel, args.ngIdOrLabel],
|
|
841
|
+
}, ng.unlinkFromNg);
|
|
842
|
+
const ngGetCommand = cliparse.command('get', {
|
|
843
|
+
description: 'Get details about a Network Group, a member or a peer',
|
|
844
|
+
args: [args.ngAnyIdOrLabel],
|
|
845
|
+
options: [opts.ngResourceType, opts.humanJsonOutputFormat],
|
|
846
|
+
}, ng.get);
|
|
847
|
+
const ngGetConfigCommand = cliparse.command('get-config', {
|
|
848
|
+
description: 'Get the Wireguard configuration of a peer in a Network Group',
|
|
849
|
+
args: [args.ngExternalIdOrLabel, args.ngIdOrLabel],
|
|
850
|
+
options: [opts.humanJsonOutputFormat],
|
|
851
|
+
}, ng.getPeerConfig);
|
|
852
|
+
const ngSearchCommand = cliparse.command('search', {
|
|
853
|
+
description: 'Search Network Groups, members or peers and get their details',
|
|
854
|
+
args: [args.ngAnyIdOrLabel],
|
|
855
|
+
options: [opts.ngResourceType, opts.humanJsonOutputFormat],
|
|
856
|
+
}, ng.search);
|
|
857
|
+
const networkGroupsCommand = cliparse.command('ng', {
|
|
858
|
+
description: 'List Network Groups',
|
|
859
|
+
options: [opts.orgaIdOrName],
|
|
860
|
+
privateOptions: [opts.humanJsonOutputFormat],
|
|
861
|
+
commands: [ngCreateCommand, ngDeleteCommand, ngLinkCommand, ngUnlinkCommand, ngGetCommand, ngGetConfigCommand, ngSearchCommand],
|
|
862
|
+
}, ng.listNg);
|
|
863
|
+
|
|
749
864
|
// NOTIFY-EMAIL COMMAND
|
|
750
865
|
const addEmailNotificationCommand = cliparse.command('add', {
|
|
751
866
|
description: 'Add a new email notification',
|
|
@@ -880,6 +995,22 @@ async function run () {
|
|
|
880
995
|
commands: [tcpRedirsListNamespacesCommand, tcpRedirsAddCommand, tcpRedirsRemoveCommand],
|
|
881
996
|
}, tcpRedirs.list);
|
|
882
997
|
|
|
998
|
+
// TOKENS COMMANDS
|
|
999
|
+
const apiTokenCreateCommand = cliparse.command('create', {
|
|
1000
|
+
description: 'Create an API token',
|
|
1001
|
+
args: [args.apiTokenName],
|
|
1002
|
+
options: [opts.apiTokenExpiration, opts.humanJsonOutputFormat],
|
|
1003
|
+
}, tokens.create);
|
|
1004
|
+
const apiTokenRevokeCommand = cliparse.command('revoke', {
|
|
1005
|
+
description: 'Revoke an API token',
|
|
1006
|
+
args: [args.apiTokenId],
|
|
1007
|
+
}, tokens.revoke);
|
|
1008
|
+
const tokensCommands = cliparse.command('tokens', {
|
|
1009
|
+
description: `Manage API tokens to query Clever Cloud API from ${conf.AUTH_BRIDGE_HOST}`,
|
|
1010
|
+
commands: [apiTokenCreateCommand, apiTokenRevokeCommand],
|
|
1011
|
+
privateOptions: [opts.humanJsonOutputFormat],
|
|
1012
|
+
}, tokens.list);
|
|
1013
|
+
|
|
883
1014
|
// UNLINK COMMAND
|
|
884
1015
|
const appUnlinkCommand = cliparse.command('unlink', {
|
|
885
1016
|
description: 'Unlink this repo from an existing application',
|
|
@@ -982,6 +1113,14 @@ async function run () {
|
|
|
982
1113
|
commands.push(colorizeExperimentalCommand(kvRawCommand, 'kv'));
|
|
983
1114
|
}
|
|
984
1115
|
|
|
1116
|
+
if (featuresFromConf.tokens) {
|
|
1117
|
+
commands.push(colorizeExperimentalCommand(tokensCommands, 'tokens'));
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
if (featuresFromConf.ng) {
|
|
1121
|
+
commands.push(colorizeExperimentalCommand(networkGroupsCommand, 'ng'));
|
|
1122
|
+
}
|
|
1123
|
+
|
|
985
1124
|
// CLI PARSER
|
|
986
1125
|
const cliParser = cliparse.cli({
|
|
987
1126
|
name: 'clever',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clever-tools",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"description": "Command Line Interface for Clever Cloud.",
|
|
5
5
|
"main": "bin/clever.js",
|
|
6
6
|
"type": "module",
|
|
@@ -26,12 +26,13 @@
|
|
|
26
26
|
"scripts/*.sh"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@clevercloud/client": "9.
|
|
29
|
+
"@clevercloud/client": "9.2.0",
|
|
30
|
+
"@inquirer/prompts": "7.3.2",
|
|
30
31
|
"cliparse": "0.5.0",
|
|
31
32
|
"colors": "1.4.0",
|
|
32
33
|
"common-env": "6.4.0",
|
|
33
34
|
"curlconverter": "3.21.0",
|
|
34
|
-
"
|
|
35
|
+
"dedent": "1.5.3",
|
|
35
36
|
"eventsource": "1.1.0",
|
|
36
37
|
"ioredis": "5.4.1",
|
|
37
38
|
"iso8601-duration": "2.1.2",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function createApiToken ({ email, password, mfaCode, name, description, expirationDate }) {
|
|
2
|
+
return Promise.resolve({
|
|
3
|
+
method: 'post',
|
|
4
|
+
url: '/api-tokens',
|
|
5
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
6
|
+
body: { email, password, mfaCode, name, description, expirationDate },
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function listApiTokens () {
|
|
11
|
+
return Promise.resolve({
|
|
12
|
+
method: 'get',
|
|
13
|
+
url: '/api-tokens',
|
|
14
|
+
headers: { Accept: 'application/json' },
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function updateApiToken (apiTokenId, { name, description }) {
|
|
19
|
+
return Promise.resolve({
|
|
20
|
+
method: 'put',
|
|
21
|
+
url: `/api-tokens/${apiTokenId}`,
|
|
22
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
23
|
+
body: { name, description },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function deleteApiToken (apiTokenId) {
|
|
28
|
+
return Promise.resolve({
|
|
29
|
+
method: 'delete',
|
|
30
|
+
url: `/api-tokens/${apiTokenId}`,
|
|
31
|
+
headers: { Accept: 'application/json' },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function addOauthHeaderPlaintext (tokens) {
|
|
36
|
+
|
|
37
|
+
return async function (requestParams) {
|
|
38
|
+
|
|
39
|
+
const authorizationHeader = (
|
|
40
|
+
'OAuth '
|
|
41
|
+
+ [
|
|
42
|
+
`oauth_consumer_key="${tokens.OAUTH_CONSUMER_KEY}"`,
|
|
43
|
+
`oauth_token="${tokens.API_OAUTH_TOKEN}"`,
|
|
44
|
+
// %26 is URL escaped character "&"
|
|
45
|
+
`oauth_signature="${tokens.OAUTH_CONSUMER_SECRET}%26${tokens.API_OAUTH_TOKEN_SECRET}"`,
|
|
46
|
+
// oauth_nonce is not mandatory
|
|
47
|
+
// oauth_signature_method is not mandatory, it defaults to PLAINTEXT
|
|
48
|
+
// oauth_timestamp is not mandatory
|
|
49
|
+
// oauth_version is not mandatory, it defaults to 1.0
|
|
50
|
+
].join(', ')
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
...requestParams,
|
|
55
|
+
headers: {
|
|
56
|
+
authorization: authorizationHeader,
|
|
57
|
+
...requestParams.headers,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// TODO: Move this to the Clever Cloud JS Client
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GET /networkgroups/organisations/{ownerId}/networkgroups/search?query
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {String} params.ownerId
|
|
7
|
+
* @param {String} params.query
|
|
8
|
+
*/
|
|
9
|
+
export function searchNetworkGroupOrResource (params) {
|
|
10
|
+
// no multipath for /self or /organisations/{id}
|
|
11
|
+
return Promise.resolve({
|
|
12
|
+
method: 'get',
|
|
13
|
+
url: `/v4/networkgroups/organisations/${params.ownerId}/networkgroups/search`,
|
|
14
|
+
headers: { Accept: 'application/json' },
|
|
15
|
+
queryParams: { query: params.query },
|
|
16
|
+
// no body
|
|
17
|
+
});
|
|
18
|
+
}
|
package/src/commands/addon.js
CHANGED
|
@@ -12,6 +12,7 @@ import { sendToApi } from '../models/send-to-api.js';
|
|
|
12
12
|
import { toNameEqualsValueString } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
13
13
|
import { resolveAddonId } from '../models/ids-resolver.js';
|
|
14
14
|
import { conf } from '../models/configuration.js';
|
|
15
|
+
import dedent from 'dedent';
|
|
15
16
|
|
|
16
17
|
const formatTable = initFormatTable();
|
|
17
18
|
|
|
@@ -124,42 +125,42 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
124
125
|
const WIP_PROVIDERS = {
|
|
125
126
|
keycloak: {
|
|
126
127
|
status: 'beta',
|
|
127
|
-
postCreateInstructions:
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
postCreateInstructions: dedent`
|
|
129
|
+
Learn more about Keycloak on Clever Cloud: ${conf.DOC_URL}/addons/keycloak/,
|
|
130
|
+
`,
|
|
130
131
|
},
|
|
131
132
|
kv: {
|
|
132
133
|
status: 'alpha',
|
|
133
|
-
postCreateInstructions:
|
|
134
|
-
colors.yellow('You can easily use Materia KV with \'redis-cli\', with such commands:')
|
|
135
|
-
colors.blue(`source <(clever addon env ${addon.id} -F shell)`)
|
|
136
|
-
colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls')
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
postCreateInstructions: dedent`
|
|
135
|
+
${colors.yellow('You can easily use Materia KV with \'redis-cli\', with such commands:')}
|
|
136
|
+
${colors.blue(`source <(clever addon env ${addon.id} -F shell)`)}
|
|
137
|
+
${colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls')}
|
|
138
|
+
Learn more about Materia KV on Clever Cloud: ${conf.DOC_URL}/addons/materia-kv/
|
|
139
|
+
`,
|
|
139
140
|
},
|
|
140
141
|
'addon-matomo': {
|
|
141
142
|
status: 'beta',
|
|
142
|
-
postCreateInstructions:
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
postCreateInstructions: dedent`
|
|
144
|
+
Learn more about Matomo on Clever Cloud: ${conf.DOC_URL}/addons/matomo/
|
|
145
|
+
`,
|
|
145
146
|
},
|
|
146
147
|
metabase: {
|
|
147
148
|
status: 'beta',
|
|
148
|
-
postCreateInstructions:
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
postCreateInstructions: dedent`
|
|
150
|
+
Learn more about Metabase on Clever Cloud: ${conf.DOC_URL}/addons/metabase/
|
|
151
|
+
`,
|
|
151
152
|
},
|
|
152
153
|
otoroshi: {
|
|
153
154
|
status: 'beta',
|
|
154
|
-
postCreateInstructions:
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
postCreateInstructions: dedent`
|
|
156
|
+
Learn more about Otoroshi with LLM on Clever Cloud: ${conf.DOC_URL}/addons/otoroshi/
|
|
157
|
+
`,
|
|
157
158
|
},
|
|
158
159
|
'addon-pulsar': {
|
|
159
160
|
status: 'beta',
|
|
160
|
-
postCreateInstructions:
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
postCreateInstructions: dedent`
|
|
162
|
+
Learn more about Pulsar on Clever Cloud: ${conf.DOC_URL}/addons/pulsar/
|
|
163
|
+
`,
|
|
163
164
|
},
|
|
164
165
|
};
|
|
165
166
|
|
package/src/commands/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { update as updateApplication } from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
2
|
|
|
3
3
|
import * as Application from '../models/application.js';
|
|
4
4
|
import * as ApplicationConfiguration from '../models/application_configuration.js';
|
|
@@ -26,7 +26,7 @@ export async function set (params) {
|
|
|
26
26
|
const config = ApplicationConfiguration.getById(configurationName);
|
|
27
27
|
|
|
28
28
|
if (config != null) {
|
|
29
|
-
const app = await
|
|
29
|
+
const app = await updateApplication({ id: ownerId, appId }, { [config.name]: ApplicationConfiguration.parse(config, configurationValue) }).then(sendToApi);
|
|
30
30
|
|
|
31
31
|
ApplicationConfiguration.printById(app, configurationName);
|
|
32
32
|
}
|
|
@@ -41,7 +41,7 @@ export async function update (params) {
|
|
|
41
41
|
throw new Error('No configuration to update');
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const app = await
|
|
44
|
+
const app = await updateApplication({ id: ownerId, appId }, options).then(sendToApi);
|
|
45
45
|
|
|
46
46
|
for (const configName of Object.keys(options)) {
|
|
47
47
|
ApplicationConfiguration.printByName(app, configName);
|
package/src/commands/curl.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import {
|
|
2
|
+
import { conf, loadOAuthConf } from '../models/configuration.js';
|
|
3
3
|
import { addOauthHeader } from '@clevercloud/client/esm/oauth.js';
|
|
4
4
|
import { Logger } from '../logger.js';
|
|
5
5
|
import colors from 'colors/safe.js';
|
|
6
6
|
import curlconverter from 'curlconverter';
|
|
7
|
+
import dedent from 'dedent';
|
|
7
8
|
|
|
8
9
|
async function loadTokens () {
|
|
9
10
|
const tokens = await loadOAuthConf();
|
|
@@ -16,22 +17,21 @@ async function loadTokens () {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
function printCleverCurlHelp () {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
${apiDocUrlv4}`);
|
|
20
|
+
Logger.println(dedent`
|
|
21
|
+
Usage: clever curl
|
|
22
|
+
Query Clever Cloud's API using Clever Tools credentials. For example:
|
|
23
|
+
|
|
24
|
+
clever curl ${conf.API_HOST}/v2/self
|
|
25
|
+
clever curl ${conf.API_HOST}/v2/summary
|
|
26
|
+
clever curl ${conf.API_HOST}/v4/products/zones
|
|
27
|
+
clever curl ${conf.API_HOST}/v2/organisations/<ORGANISATION_ID>/applications | jq '.[].id'
|
|
28
|
+
clever curl ${conf.API_HOST}/v4/billing/organisations/<ORGANISATION_ID>/<INVOICE_NUMBER>.pdf > invoice.pdf
|
|
29
|
+
|
|
30
|
+
Our API documentation is available here :
|
|
31
|
+
|
|
32
|
+
${conf.API_DOC_URL}/v2/
|
|
33
|
+
${conf.API_DOC_URL}/v4/
|
|
34
|
+
`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export async function curl () {
|
package/src/commands/database.js
CHANGED
|
@@ -2,8 +2,8 @@ import { sendToApi } from '../models/send-to-api.js';
|
|
|
2
2
|
import { getBackups } from '@clevercloud/client/esm/api/v2/backups.js';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
4
|
import { formatTable as initFormatTable } from '../format-table.js';
|
|
5
|
-
import superagent from 'superagent';
|
|
6
5
|
import fs from 'node:fs';
|
|
6
|
+
import { Writable } from 'node:stream';
|
|
7
7
|
import { findOwnerId } from '../models/addon.js';
|
|
8
8
|
import { resolveRealId, resolveAddonId } from '../models/ids-resolver.js';
|
|
9
9
|
|
|
@@ -80,14 +80,14 @@ export async function downloadBackups (params) {
|
|
|
80
80
|
throw new Error('no backup with this ID');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (output) {
|
|
88
|
-
fs.writeFileSync(output, res.body);
|
|
89
|
-
return;
|
|
83
|
+
const response = await globalThis.fetch(backup.download_url);
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw new Error('Failed to download backup');
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
|
|
88
|
+
await response
|
|
89
|
+
.body
|
|
90
|
+
.pipeTo(
|
|
91
|
+
Writable.toWeb(output ? fs.createWriteStream(output) : process.stdout),
|
|
92
|
+
);
|
|
93
93
|
}
|
package/src/commands/domain.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
removeDomain,
|
|
10
10
|
unmarkFavouriteDomain,
|
|
11
11
|
} from '@clevercloud/client/esm/api/v2/application.js';
|
|
12
|
-
import { getSummary } from '@clevercloud/client/
|
|
12
|
+
import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
|
|
13
13
|
import { sendToApi } from '../models/send-to-api.js';
|
|
14
14
|
import colors from 'colors/safe.js';
|
|
15
15
|
import { parse as parseDomain } from 'tldts';
|
|
@@ -17,6 +17,7 @@ import { diagDomainConfig } from '@clevercloud/client/esm/utils/diag-domain-conf
|
|
|
17
17
|
import { sortDomains } from '@clevercloud/client/esm/utils/domains.js';
|
|
18
18
|
import { DnsResolver } from '../models/node-dns-resolver.js';
|
|
19
19
|
import _ from 'lodash';
|
|
20
|
+
import { getDefaultLoadBalancersDnsInfo } from '@clevercloud/client/esm/api/v4/load-balancers.js';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @typedef {import('@clevercloud/client/esm/utils/diag-domain-config.types.js').DomainInfo} DomainInfo
|
|
@@ -110,7 +111,7 @@ export async function diagApplication (params) {
|
|
|
110
111
|
let hasError = false;
|
|
111
112
|
|
|
112
113
|
const app = await getApp({ id: ownerId, appId }).then(sendToApi);
|
|
113
|
-
const expectedDnsForPublicLoadBalancer = await getDefaultLoadBalancersDnsInfo({
|
|
114
|
+
const expectedDnsForPublicLoadBalancer = await getDefaultLoadBalancersDnsInfo({ ownerId, appId }).then(sendToApi);
|
|
114
115
|
|
|
115
116
|
const loadBalancerDnsConfig = {
|
|
116
117
|
aRecords: expectedDnsForPublicLoadBalancer[0].dns.a,
|
|
@@ -181,7 +182,16 @@ export async function overview (params) {
|
|
|
181
182
|
|
|
182
183
|
const applicationsWithDomains = await Promise.all(
|
|
183
184
|
applications.map(async (app) => {
|
|
184
|
-
const domains = await getAllDomains({ id: app.ownerId, appId: app.id })
|
|
185
|
+
const domains = await getAllDomains({ id: app.ownerId, appId: app.id })
|
|
186
|
+
.then(sendToApi)
|
|
187
|
+
.catch((error) => {
|
|
188
|
+
// if the user cannot access application domains, we simply act as if there were no domains
|
|
189
|
+
if (error?.response?.status === 403) {
|
|
190
|
+
Logger.printErrorLine(`You cannot list domains for application ${app.name} (${app.id})`);
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
});
|
|
185
195
|
return { app, domains };
|
|
186
196
|
}),
|
|
187
197
|
);
|
|
@@ -351,19 +361,6 @@ function reportDomainDiagnostics ({ hostname, pathPrefix, resolvedDnsConfig, dia
|
|
|
351
361
|
}
|
|
352
362
|
}
|
|
353
363
|
|
|
354
|
-
// TODO: put in clever client
|
|
355
|
-
function getDefaultLoadBalancersDnsInfo (params) {
|
|
356
|
-
return Promise.resolve({
|
|
357
|
-
method: 'get',
|
|
358
|
-
url: `/v4/load-balancers/organisations/${params.id}/applications/${params.appId}/load-balancers/default`,
|
|
359
|
-
headers: {
|
|
360
|
-
Accept: 'application/json',
|
|
361
|
-
},
|
|
362
|
-
// no query params
|
|
363
|
-
// no body
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
|
|
367
364
|
/**
|
|
368
365
|
* Parses domain information from vhosts array
|
|
369
366
|
*
|
package/src/commands/env.js
CHANGED
|
@@ -4,16 +4,23 @@ import colors from 'colors/safe.js';
|
|
|
4
4
|
import * as variables from '../models/variables.js';
|
|
5
5
|
import { sendToApi } from '../models/send-to-api.js';
|
|
6
6
|
import { toNameEqualsValueString, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
7
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
getAllEnvVars,
|
|
9
|
+
getAllEnvVarsForAddons,
|
|
10
|
+
getAllEnvVarsForDependencies,
|
|
11
|
+
updateEnvVar,
|
|
12
|
+
updateAllEnvVars,
|
|
13
|
+
removeEnvVar,
|
|
14
|
+
} from '@clevercloud/client/esm/api/v2/application.js';
|
|
8
15
|
|
|
9
16
|
export async function list (params) {
|
|
10
17
|
const { alias, app: appIdOrName, 'add-export': addExportsOption, format } = params.options;
|
|
11
18
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
12
19
|
|
|
13
20
|
const [envFromApp, envFromAddons, envFromDeps] = await Promise.all([
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
getAllEnvVars({ id: ownerId, appId }).then(sendToApi),
|
|
22
|
+
getAllEnvVarsForAddons({ id: ownerId, appId }).then(sendToApi),
|
|
23
|
+
getAllEnvVarsForDependencies({ id: ownerId, appId }).then(sendToApi),
|
|
17
24
|
]);
|
|
18
25
|
|
|
19
26
|
switch (format) {
|
|
@@ -69,7 +76,7 @@ export async function set (params) {
|
|
|
69
76
|
|
|
70
77
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
71
78
|
|
|
72
|
-
await
|
|
79
|
+
await updateEnvVar({ id: ownerId, appId, envName }, { value }).then(sendToApi);
|
|
73
80
|
|
|
74
81
|
Logger.println('Your environment variable has been successfully saved');
|
|
75
82
|
};
|
|
@@ -79,7 +86,7 @@ export async function rm (params) {
|
|
|
79
86
|
const { alias, app: appIdOrName } = params.options;
|
|
80
87
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
81
88
|
|
|
82
|
-
await
|
|
89
|
+
await removeEnvVar({ id: ownerId, appId, envName }).then(sendToApi);
|
|
83
90
|
|
|
84
91
|
Logger.println('Your environment variable has been successfully removed');
|
|
85
92
|
};
|
|
@@ -90,7 +97,7 @@ export async function importEnv (params) {
|
|
|
90
97
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
91
98
|
|
|
92
99
|
const envVars = await variables.readVariablesFromStdin(format);
|
|
93
|
-
await
|
|
100
|
+
await updateAllEnvVars({ id: ownerId, appId }, envVars).then(sendToApi);
|
|
94
101
|
|
|
95
102
|
Logger.println('Environment variables have been set');
|
|
96
103
|
};
|
|
@@ -110,7 +117,7 @@ export async function importVarsFromLocalEnv (params) {
|
|
|
110
117
|
|
|
111
118
|
for (const envName of envNames) {
|
|
112
119
|
const value = process.env[envName] || '';
|
|
113
|
-
await
|
|
120
|
+
await updateEnvVar({ id: ownerId, appId, envName }, { value }).then(sendToApi);
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
Logger.println('Your environment variables have been successfully saved');
|
package/src/commands/kv.js
CHANGED
|
@@ -2,7 +2,7 @@ import Redis from 'ioredis';
|
|
|
2
2
|
import colors from 'colors/safe.js';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
4
|
import { sendToApi } from '../models/send-to-api.js';
|
|
5
|
-
import { getAllEnvVars } from '@clevercloud/client/
|
|
5
|
+
import { getAllEnvVars } from '@clevercloud/client/esm/api/v2/addon.js';
|
|
6
6
|
import { findAddonsByNameOrId } from '../models/ids-resolver.js';
|
|
7
7
|
|
|
8
8
|
const URL_ENV_KEY = 'REDIS_URL';
|
package/src/commands/link.js
CHANGED
|
@@ -7,9 +7,11 @@ export async function link (params) {
|
|
|
7
7
|
|
|
8
8
|
if (app.app_id != null && orgaIdOrName != null) {
|
|
9
9
|
Logger.warn('You\'ve specified a unique application ID, organisation option will be ignored');
|
|
10
|
+
await Application.linkRepo(app, null, alias);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
await Application.linkRepo(app, orgaIdOrName, alias);
|
|
10
14
|
}
|
|
11
|
-
|
|
12
|
-
await Application.linkRepo(app, orgaIdOrName, alias);
|
|
13
15
|
|
|
14
16
|
Logger.println('Your application has been successfully linked!');
|
|
15
17
|
}
|