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
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import colors from 'colors/safe.js';
|
|
2
|
+
import * as networkGroup from '../models/ng.js';
|
|
3
|
+
import * as networkGroupResources from '../models/ng-resources.js';
|
|
4
|
+
|
|
5
|
+
import { Logger } from '../logger.js';
|
|
6
|
+
import { printResults } from '../lib/ng-print.js';
|
|
7
|
+
|
|
8
|
+
/** Create a Network Group
|
|
9
|
+
* @param {Object} params
|
|
10
|
+
* @param {Array<Object>} params.args
|
|
11
|
+
* @param {Object} params.args[0] Network Group label
|
|
12
|
+
* @param {string} params.options.description Network Group description
|
|
13
|
+
* @param {Array<string>} params.options.link Array of member IDs or labels to link to the Network Group
|
|
14
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
15
|
+
* @param {string} params.options.tags Comma-separated list of tags
|
|
16
|
+
*/
|
|
17
|
+
export async function createNg (params) {
|
|
18
|
+
const [ngLabel] = params.args;
|
|
19
|
+
const label = ngLabel.ngResourceLabel;
|
|
20
|
+
const { description, link: membersIds, org, tags } = params.options;
|
|
21
|
+
|
|
22
|
+
await networkGroup.create(label, description, tags, membersIds, org);
|
|
23
|
+
|
|
24
|
+
const successMessage = `Network Group ${colors.green(label)} successfully created`;
|
|
25
|
+
if (membersIds == null) {
|
|
26
|
+
Logger.printSuccess(`${successMessage}!`);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
Logger.printSuccess(`${successMessage} with member(s):`);
|
|
30
|
+
Logger.println(membersIds.map((id) => colors.grey(` - ${id}`)).join('\n'));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Delete a Network Group
|
|
35
|
+
* @param {Object} params
|
|
36
|
+
* @param {Object} params.args[0] Network Group ID or label
|
|
37
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
38
|
+
*/
|
|
39
|
+
export async function deleteNg (params) {
|
|
40
|
+
const [ngIdOrLabel] = params.args;
|
|
41
|
+
const { org } = params.options;
|
|
42
|
+
|
|
43
|
+
await networkGroup.destroy(ngIdOrLabel, org);
|
|
44
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
45
|
+
Logger.printSuccess(`Network Group ${colors.green(ngText)} successfully deleted!`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Create an external peer in a Network Group
|
|
49
|
+
* @param {Object} params
|
|
50
|
+
* @param {Object} params.args[0] External peer ID or label
|
|
51
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
52
|
+
* @param {string} params.args[2] Wireguard public key
|
|
53
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
54
|
+
*/
|
|
55
|
+
export async function createExternalPeer (params) {
|
|
56
|
+
const [peerIdOrLabel, ngIdOrLabel, publicKey] = params.args;
|
|
57
|
+
const { org } = params.options;
|
|
58
|
+
|
|
59
|
+
await networkGroupResources.createExternalPeerWithParent(ngIdOrLabel, peerIdOrLabel.ngResourceLabel, publicKey, org);
|
|
60
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
61
|
+
Logger.printSuccess(`External peer ${colors.green(peerIdOrLabel.ngResourceLabel)} successfully created in Network Group ${colors.green(ngText)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Delete an external peer from a Network Group
|
|
65
|
+
* @param {Object} params
|
|
66
|
+
* @param {Object} params.args[0] External peer ID or label
|
|
67
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
68
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
69
|
+
*/
|
|
70
|
+
export async function deleteExternalPeer (params) {
|
|
71
|
+
const [peerIdOrLabel, ngIdOrLabel] = params.args;
|
|
72
|
+
const { org } = params.options;
|
|
73
|
+
|
|
74
|
+
const peerText = peerIdOrLabel.ngResourceLabel ?? peerIdOrLabel.memberId;
|
|
75
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
76
|
+
await networkGroupResources.deleteExternalPeerWithParent(ngIdOrLabel, peerText, org);
|
|
77
|
+
Logger.printSuccess(`External peer ${colors.green(peerText)} successfully deleted from Network Group ${colors.green(ngText)}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Link a member to a Network Group
|
|
81
|
+
* @param {Object} params
|
|
82
|
+
* @param {string} params.args[0] Member ID
|
|
83
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
84
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
85
|
+
*/
|
|
86
|
+
export async function linkToNg (params) {
|
|
87
|
+
const [resourceId, ngIdOrLabel] = params.args;
|
|
88
|
+
const { org } = params.options;
|
|
89
|
+
|
|
90
|
+
await networkGroupResources.linkMember(ngIdOrLabel, resourceId.memberId, org);
|
|
91
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
92
|
+
Logger.printSuccess(`Member ${colors.green(resourceId.memberId)} successfully linked to Network Group ${colors.green(ngText)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Unlink a member from a Network Group
|
|
96
|
+
* @param {Object} params
|
|
97
|
+
* @param {string} params.args[0] Member ID
|
|
98
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
99
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
100
|
+
*/
|
|
101
|
+
export async function unlinkFromNg (params) {
|
|
102
|
+
const [resourceId, ngIdOrLabel] = params.args;
|
|
103
|
+
const { org } = params.options;
|
|
104
|
+
|
|
105
|
+
await networkGroupResources.unlinkMember(ngIdOrLabel, resourceId.memberId, org);
|
|
106
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
107
|
+
Logger.printSuccess(`Member ${colors.green(resourceId.memberId)} successfully unlinked from Network Group ${colors.green(ngText)}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Print the configuration of a Network Group's peer
|
|
111
|
+
* @param {Object} params
|
|
112
|
+
* @param {Object} params.args[0] Peer ID or label
|
|
113
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
114
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
115
|
+
* @param {string} params.options.format Output format
|
|
116
|
+
*/
|
|
117
|
+
export async function getPeerConfig (params) {
|
|
118
|
+
const [peerIdOrLabel, ngIdOrLabel] = params.args;
|
|
119
|
+
const { org, format } = params.options;
|
|
120
|
+
|
|
121
|
+
const config = await networkGroup.getPeerConfig(peerIdOrLabel, ngIdOrLabel, org);
|
|
122
|
+
|
|
123
|
+
switch (format) {
|
|
124
|
+
case 'json': {
|
|
125
|
+
Logger.printJson(config);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'human':
|
|
129
|
+
default: {
|
|
130
|
+
const decodedConfiguration = Buffer.from(config.configuration, 'base64').toString('utf8');
|
|
131
|
+
Logger.println(decodedConfiguration);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** List Network Groups, their members and peers
|
|
137
|
+
* @param {Object} params
|
|
138
|
+
* @param {Object} params.options.orgaIdOrName Organisation ID or name
|
|
139
|
+
* @param {string} params.options.format Output format
|
|
140
|
+
*/
|
|
141
|
+
export async function listNg (params) {
|
|
142
|
+
const { org, format } = params.options;
|
|
143
|
+
|
|
144
|
+
const ngs = await networkGroup.getAllNGs(org);
|
|
145
|
+
|
|
146
|
+
switch (format) {
|
|
147
|
+
case 'json': {
|
|
148
|
+
Logger.printJson(ngs);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case 'human':
|
|
152
|
+
default: {
|
|
153
|
+
if (!ngs.length) {
|
|
154
|
+
Logger.println(`ℹ️ No Network Group found, create one with ${colors.blue('clever ng create')} command`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const ngList = ngs.map(({
|
|
158
|
+
id,
|
|
159
|
+
label,
|
|
160
|
+
networkIp,
|
|
161
|
+
members,
|
|
162
|
+
peers,
|
|
163
|
+
}) => ({
|
|
164
|
+
ID: id,
|
|
165
|
+
Label: label,
|
|
166
|
+
'Network CIDR': networkIp,
|
|
167
|
+
Members: Object.keys(members).length,
|
|
168
|
+
Peers: Object.keys(peers).length,
|
|
169
|
+
}));
|
|
170
|
+
|
|
171
|
+
console.table(ngList);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Show information about a Network Group, a member or a peer
|
|
177
|
+
* @param {Object} params
|
|
178
|
+
* @param {Object} params.args[0] ID or label of the Network Group, a member or a peer
|
|
179
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
180
|
+
* @param {string} params.options.format Output format
|
|
181
|
+
*/
|
|
182
|
+
export async function get (params) {
|
|
183
|
+
const [idOrLabel] = params.args;
|
|
184
|
+
const { org, format } = params.options;
|
|
185
|
+
const type = params.options.type ?? 'single';
|
|
186
|
+
|
|
187
|
+
await printResults(idOrLabel, org, format, 'get', type);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Show information about a Network Group, a member or a peer
|
|
191
|
+
* @param {Object} params
|
|
192
|
+
* @param {Object} params.args[0] ID or label of the Network Group, a member or a peer
|
|
193
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
194
|
+
* @param {string} params.options.format Output format
|
|
195
|
+
*/
|
|
196
|
+
export async function search (params) {
|
|
197
|
+
const [idOrLabel] = params.args;
|
|
198
|
+
const { org, format } = params.options;
|
|
199
|
+
const type = params.options.type;
|
|
200
|
+
|
|
201
|
+
await printResults(idOrLabel, org, format, 'search', type);
|
|
202
|
+
}
|
package/src/commands/profile.js
CHANGED
|
@@ -2,6 +2,7 @@ import colors from 'colors/safe.js';
|
|
|
2
2
|
import openPage from 'open';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
4
|
import * as User from '../models/user.js';
|
|
5
|
+
import dedent from 'dedent';
|
|
5
6
|
|
|
6
7
|
export async function profile (params) {
|
|
7
8
|
const { format } = params.options;
|
|
@@ -37,12 +38,14 @@ export async function profile (params) {
|
|
|
37
38
|
}
|
|
38
39
|
case 'human':
|
|
39
40
|
default: {
|
|
40
|
-
Logger.println(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
Logger.println(dedent`
|
|
42
|
+
You're currently logged in as:
|
|
43
|
+
User id ${formattedUser.id}
|
|
44
|
+
Name ${formattedUser.name ?? colors.red.bold('[not specified]')}
|
|
45
|
+
Email ${formattedUser.email}
|
|
46
|
+
Token expiration ${tokenExpiration}
|
|
47
|
+
Two factor auth ${formattedUser.has2FA ? 'yes' : 'no'}
|
|
48
|
+
`);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
};
|
|
@@ -3,13 +3,13 @@ import { Logger } from '../logger.js';
|
|
|
3
3
|
import * as variables from '../models/variables.js';
|
|
4
4
|
import { sendToApi } from '../models/send-to-api.js';
|
|
5
5
|
import { toNameEqualsValueString, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
6
|
-
import
|
|
6
|
+
import { getAllExposedEnvVars, updateAllExposedEnvVars } from '@clevercloud/client/esm/api/v2/application.js';
|
|
7
7
|
|
|
8
8
|
export async function list (params) {
|
|
9
9
|
const { alias, app: appIdOrName, format } = params.options;
|
|
10
10
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
11
11
|
|
|
12
|
-
const publishedConfigs = await
|
|
12
|
+
const publishedConfigs = await getAllExposedEnvVars({ id: ownerId, appId }).then(sendToApi);
|
|
13
13
|
const pairs = Object.entries(publishedConfigs).map(([name, value]) => ({ name, value }));
|
|
14
14
|
|
|
15
15
|
switch (format) {
|
|
@@ -39,9 +39,9 @@ export async function set (params) {
|
|
|
39
39
|
|
|
40
40
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
41
41
|
|
|
42
|
-
const publishedConfigs = await
|
|
42
|
+
const publishedConfigs = await getAllExposedEnvVars({ id: ownerId, appId }).then(sendToApi);
|
|
43
43
|
publishedConfigs[varName] = varValue;
|
|
44
|
-
await
|
|
44
|
+
await updateAllExposedEnvVars({ id: ownerId, appId }, publishedConfigs).then(sendToApi);
|
|
45
45
|
|
|
46
46
|
Logger.println('Your published config item has been successfully saved');
|
|
47
47
|
};
|
|
@@ -51,9 +51,9 @@ export async function rm (params) {
|
|
|
51
51
|
const { alias, app: appIdOrName } = params.options;
|
|
52
52
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
53
53
|
|
|
54
|
-
const publishedConfigs = await
|
|
54
|
+
const publishedConfigs = await getAllExposedEnvVars({ id: ownerId, appId }).then(sendToApi);
|
|
55
55
|
delete publishedConfigs[varName];
|
|
56
|
-
await
|
|
56
|
+
await updateAllExposedEnvVars({ id: ownerId, appId }, publishedConfigs).then(sendToApi);
|
|
57
57
|
|
|
58
58
|
Logger.println('Your published config item has been successfully removed');
|
|
59
59
|
};
|
|
@@ -64,7 +64,7 @@ export async function importEnv (params) {
|
|
|
64
64
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
65
65
|
|
|
66
66
|
const publishedConfigs = await variables.readVariablesFromStdin(format);
|
|
67
|
-
await
|
|
67
|
+
await updateAllExposedEnvVars({ id: ownerId, appId }, publishedConfigs).then(sendToApi);
|
|
68
68
|
|
|
69
69
|
Logger.println('Your published configs have been set');
|
|
70
70
|
};
|
package/src/commands/stop.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as Application from '../models/application.js';
|
|
2
|
-
import * as application from '@clevercloud/client/esm/api/v2/application.js';
|
|
3
2
|
import { Logger } from '../logger.js';
|
|
4
3
|
import { sendToApi } from '../models/send-to-api.js';
|
|
4
|
+
import { undeploy as stopApplication } from '@clevercloud/client/esm/api/v2/application.js';
|
|
5
5
|
|
|
6
6
|
export async function stop (params) {
|
|
7
7
|
const { alias, app: appIdOrName } = params.options;
|
|
8
8
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
9
9
|
|
|
10
|
-
await
|
|
10
|
+
await stopApplication({ id: ownerId, appId }).then(sendToApi);
|
|
11
11
|
Logger.println('App successfully stopped!');
|
|
12
12
|
}
|
|
@@ -4,8 +4,8 @@ import * as Namespaces from '../models/namespaces.js';
|
|
|
4
4
|
import { sendToApi } from '../models/send-to-api.js';
|
|
5
5
|
import * as Interact from '../models/interact.js';
|
|
6
6
|
import { Logger } from '../logger.js';
|
|
7
|
-
import * as application from '@clevercloud/client/esm/api/v2/application.js';
|
|
8
7
|
import * as Application from '../models/application.js';
|
|
8
|
+
import { addTcpRedir, getTcpRedirs, removeTcpRedir } from '@clevercloud/client/esm/api/v2/application.js';
|
|
9
9
|
|
|
10
10
|
export async function listNamespaces (params) {
|
|
11
11
|
const { alias, app: appIdOrName, format } = params.options;
|
|
@@ -44,7 +44,7 @@ export async function list (params) {
|
|
|
44
44
|
const { alias, app: appIdOrName, format } = params.options;
|
|
45
45
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
46
46
|
|
|
47
|
-
const redirs = await
|
|
47
|
+
const redirs = await getTcpRedirs({ id: ownerId, appId }).then(sendToApi);
|
|
48
48
|
|
|
49
49
|
switch (format) {
|
|
50
50
|
case 'json': {
|
|
@@ -83,10 +83,10 @@ export async function add (params) {
|
|
|
83
83
|
const { alias, app: appIdOrName, namespace, yes: skipConfirmation } = params.options;
|
|
84
84
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
85
85
|
|
|
86
|
-
const { port } = await
|
|
86
|
+
const { port } = await addTcpRedir({ id: ownerId, appId }, { namespace }).then(sendToApi).catch((error) => {
|
|
87
87
|
if (error.status === 402) {
|
|
88
88
|
return acceptPayment(error.response.body, skipConfirmation).then(() => {
|
|
89
|
-
return
|
|
89
|
+
return addTcpRedir({ id: ownerId, appId, payment: 'accepted' }, { namespace }).then(sendToApi);
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
else {
|
|
@@ -102,7 +102,7 @@ export async function remove (params) {
|
|
|
102
102
|
const { alias, app: appIdOrName, namespace } = params.options;
|
|
103
103
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
104
104
|
|
|
105
|
-
await
|
|
105
|
+
await removeTcpRedir({ id: ownerId, appId, sourcePort: port, namespace }).then(sendToApi);
|
|
106
106
|
|
|
107
107
|
Logger.println('Successfully removed tcp redirection.');
|
|
108
108
|
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import colors from 'colors/safe.js';
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
3
|
+
import { createApiToken, deleteApiToken, listApiTokens } from '../clever-client/auth-bridge.js';
|
|
4
|
+
import { sendToAuthBridge } from '../models/send-to-api.js';
|
|
5
|
+
import { getCurrent as getCurrentUser } from '../models/user.js';
|
|
6
|
+
import { conf } from '../models/configuration.js';
|
|
7
|
+
import dedent from 'dedent';
|
|
8
|
+
import { promptPassword } from '../prompt-password.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a new API token
|
|
12
|
+
* @param {Object} params - Function parameters
|
|
13
|
+
* @param {[string]} params.args - Command line args
|
|
14
|
+
* @param {Object} params.options - Command line options
|
|
15
|
+
* @param {'json'|'human'} params.options.format - Output format
|
|
16
|
+
*/
|
|
17
|
+
export async function create (params) {
|
|
18
|
+
const [apiTokenName] = params.args;
|
|
19
|
+
const { expiration, format } = params.options;
|
|
20
|
+
|
|
21
|
+
// Expire in 1 year
|
|
22
|
+
const dateObject = new Date();
|
|
23
|
+
dateObject.setFullYear(dateObject.getFullYear() + 1);
|
|
24
|
+
const maxExpirationDate = dateObject;
|
|
25
|
+
|
|
26
|
+
let expirationDate;
|
|
27
|
+
if (expiration != null) {
|
|
28
|
+
if (expiration > maxExpirationDate) {
|
|
29
|
+
throw new Error('You cannot set an expiration date greater than 1 year');
|
|
30
|
+
}
|
|
31
|
+
expirationDate = expiration;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
expirationDate = maxExpirationDate;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const user = await getCurrentUser();
|
|
38
|
+
|
|
39
|
+
const password = await promptPassword('Enter your password:');
|
|
40
|
+
|
|
41
|
+
let mfaCode;
|
|
42
|
+
if (user.preferredMFA === 'TOTP') {
|
|
43
|
+
mfaCode = await promptPassword('Enter your 2FA code:');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const tokenData = {
|
|
47
|
+
email: user.email,
|
|
48
|
+
password,
|
|
49
|
+
mfaCode,
|
|
50
|
+
name: apiTokenName,
|
|
51
|
+
expirationDate: expirationDate.toISOString(),
|
|
52
|
+
};
|
|
53
|
+
const createdToken = await createApiToken(tokenData).then(sendToAuthBridge).catch((error) => {
|
|
54
|
+
const errorCode = error?.cause?.responseBody?.code;
|
|
55
|
+
if (errorCode === 'invalid-credential') {
|
|
56
|
+
throw new Error('Invalid credentials, check your password');
|
|
57
|
+
}
|
|
58
|
+
if (errorCode === 'invalid-mfa-code') {
|
|
59
|
+
throw new Error('Invalid credentials, check your 2FA code');
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
switch (format) {
|
|
65
|
+
case 'json':
|
|
66
|
+
Logger.printJson(createdToken);
|
|
67
|
+
break;
|
|
68
|
+
case 'human':
|
|
69
|
+
default:
|
|
70
|
+
Logger.println(dedent`
|
|
71
|
+
${colors.green('✔')} API token successfully created! Store it securely, you won't able to print it again.
|
|
72
|
+
|
|
73
|
+
- API token ID : ${colors.grey(createdToken.apiTokenId)}
|
|
74
|
+
- API token : ${colors.grey(createdToken.apiToken)}
|
|
75
|
+
- Expiration : ${colors.grey(formatDate(createdToken.expirationDate))}
|
|
76
|
+
|
|
77
|
+
Export this token and use it to make authenticated requests to the Clever Cloud API through the Auth Bridge:
|
|
78
|
+
|
|
79
|
+
export CC_API_TOKEN=${createdToken.apiToken}
|
|
80
|
+
curl -H "Authorization: Bearer $CC_API_TOKEN" ${conf.AUTH_BRIDGE_HOST}/v2/self
|
|
81
|
+
|
|
82
|
+
Then, to revoke this token, run:
|
|
83
|
+
clever tokens revoke ${createdToken.apiTokenId}
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get information about an API token
|
|
90
|
+
* @param {Object} params - Function parameters
|
|
91
|
+
* @param {Object} params.options - Command line options
|
|
92
|
+
* @param {Object} params.options.format - Output format
|
|
93
|
+
* @returns {Promise<void>}
|
|
94
|
+
*/
|
|
95
|
+
export async function list (params) {
|
|
96
|
+
const { format } = params.options;
|
|
97
|
+
|
|
98
|
+
const tokens = await listApiTokens().then(sendToAuthBridge);
|
|
99
|
+
|
|
100
|
+
if (format === 'json') {
|
|
101
|
+
Logger.printJson(tokens);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
if (tokens.length === 0) {
|
|
105
|
+
Logger.println(`ℹ️ No API token found, create one with ${colors.blue('clever tokens create')} command`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.table(tokens.map((token) => {
|
|
109
|
+
return {
|
|
110
|
+
'API token ID': token.apiTokenId,
|
|
111
|
+
Name: token.name,
|
|
112
|
+
'Creation IP address': token.ip,
|
|
113
|
+
Creation: formatDate(token.creationDate),
|
|
114
|
+
Expiration: formatDate(token.expirationDate),
|
|
115
|
+
};
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Revoke an API token
|
|
123
|
+
* @param {Object} params - Function parameters
|
|
124
|
+
* @param {string[]} params.args - Command line arguments, token ID to revoke is expected as first argument
|
|
125
|
+
* @returns {Promise<void>}
|
|
126
|
+
*/
|
|
127
|
+
export async function revoke (params) {
|
|
128
|
+
const [apiTokenId] = params.args;
|
|
129
|
+
|
|
130
|
+
await deleteApiToken(apiTokenId).then(sendToAuthBridge);
|
|
131
|
+
|
|
132
|
+
Logger.println(colors.green('✔'), 'API token successfully revoked!');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function formatDate (dateInput) {
|
|
136
|
+
return new Date(dateInput).toISOString().substring(0, 16).replace('T', ' ');
|
|
137
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import dedent from 'dedent';
|
|
2
|
+
import { conf } from './models/configuration.js';
|
|
3
|
+
|
|
1
4
|
export const EXPERIMENTAL_FEATURES = {
|
|
2
5
|
kv: {
|
|
3
6
|
status: 'alpha',
|
|
4
7
|
description: 'Send commands to databases such as Materia KV or Redis® directly from Clever Tools, without other dependencies',
|
|
5
|
-
instructions: `
|
|
8
|
+
instructions: dedent`
|
|
6
9
|
Target any compatible add-on by its name or ID (with an org ID if needed) and send commands to it:
|
|
7
10
|
|
|
8
11
|
clever kv myMateriaKV SET myKey myValue
|
|
@@ -12,6 +15,47 @@ export const EXPERIMENTAL_FEATURES = {
|
|
|
12
15
|
clever kv redis_xxxxx --org org_xxxxx PING
|
|
13
16
|
|
|
14
17
|
Learn more about Materia KV: https://www.clever-cloud.com/developers/doc/addons/materia-kv/
|
|
15
|
-
|
|
18
|
+
`,
|
|
19
|
+
},
|
|
20
|
+
tokens: {
|
|
21
|
+
status: 'beta',
|
|
22
|
+
description: `Manage API tokens to query Clever Cloud API from ${conf.AUTH_BRIDGE_HOST}`,
|
|
23
|
+
instructions: dedent`
|
|
24
|
+
Create, list or revoke API tokens from a single command:
|
|
25
|
+
|
|
26
|
+
clever tokens create myTokenName
|
|
27
|
+
clever tokens --format json
|
|
28
|
+
clever tokens revoke myTokenId
|
|
29
|
+
|
|
30
|
+
Learn more about Clever Cloud API: https://www.clever-cloud.com/developers/api
|
|
31
|
+
`,
|
|
32
|
+
},
|
|
33
|
+
ng: {
|
|
34
|
+
status: 'beta',
|
|
35
|
+
description: 'Manage Network Groups to manage applications, add-ons, external peers through a Wireguard network',
|
|
36
|
+
instructions: dedent`
|
|
37
|
+
- Create a Network Group:
|
|
38
|
+
clever ng create myNG
|
|
39
|
+
- Create a Network Group with members (application, database add-on):
|
|
40
|
+
clever ng create myNG --link app_xxx,addon_xxx
|
|
41
|
+
- List Network Groups:
|
|
42
|
+
clever ng
|
|
43
|
+
- Delete a Network Group:
|
|
44
|
+
clever ng delete myNG
|
|
45
|
+
- (Un)Link an application or a database add-on to an existing Network Group:
|
|
46
|
+
clever ng link app_xxx myNG
|
|
47
|
+
clever ng unlink addon_xxx myNG
|
|
48
|
+
- Get the Wireguard configuration of a peer:
|
|
49
|
+
clever ng get-config peerIdOrLabel myNG
|
|
50
|
+
- Get details about a Network Group, a member or a peer:
|
|
51
|
+
clever ng get myNg
|
|
52
|
+
clever ng get app_xxx
|
|
53
|
+
clever ng get peerId
|
|
54
|
+
clever ng get memberLabel
|
|
55
|
+
- Search Network Groups, members or peers:
|
|
56
|
+
clever ng search myQuery
|
|
57
|
+
|
|
58
|
+
Learn more about Network Groups: https://github.com/CleverCloud/clever-tools/blob/master/docs/ng.md
|
|
59
|
+
`,
|
|
16
60
|
},
|
|
17
61
|
};
|