neonctl 0.5.4 → 0.7.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/package.json +3 -2
- package/src/api/operations.js +32 -0
- package/src/api/roles.js +20 -0
- package/src/auth.js +8 -7
- package/src/commands/auth.js +5 -1
- package/src/commands/projects.js +3 -1
- package/src/commands/roles.js +18 -0
- package/src/index.js +29 -12
- package/src/writer.js +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neonctl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "CLI tool for NeonDB Cloud management",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "NeonDB",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"cli-table": "^0.3.11",
|
|
27
27
|
"open": "^8.4.0",
|
|
28
28
|
"openid-client": "^5.1.9",
|
|
29
|
+
"yaml": "^2.1.1",
|
|
29
30
|
"yargs": "^17.5.1"
|
|
30
31
|
},
|
|
31
32
|
"publishConfig": {
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"build": "npm run clean && tsc && cp src/*.html dist/src/",
|
|
40
41
|
"clean": "rm -rf dist",
|
|
41
42
|
"start": "node index.js",
|
|
42
|
-
"publizh": "npm run build && npm publish ./dist
|
|
43
|
+
"publizh": "npm run build && npm publish --ignore-scripts ./dist"
|
|
43
44
|
},
|
|
44
45
|
"lint-staged": {
|
|
45
46
|
"*.ts": [
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getOperation = exports.waitOperationFinalState = void 0;
|
|
13
|
+
const log_1 = require("../log");
|
|
14
|
+
const gateway_1 = require("./gateway");
|
|
15
|
+
function sleep(ms) {
|
|
16
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
|
+
}
|
|
18
|
+
// wait 20 sec max.
|
|
19
|
+
const waitOperationFinalState = (props) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
for (let i = 0; i < 20; i++) {
|
|
21
|
+
const operation = yield (0, exports.getOperation)(Object.assign({}, props));
|
|
22
|
+
if (operation.status == 'finished') {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
log_1.log.info(`Waiting for operation "${props.operation_id}" to finish`);
|
|
26
|
+
yield sleep(1000);
|
|
27
|
+
}
|
|
28
|
+
throw Error(`timeout while waiting for operation ${props.operation_id}`);
|
|
29
|
+
});
|
|
30
|
+
exports.waitOperationFinalState = waitOperationFinalState;
|
|
31
|
+
const getOperation = (props) => (0, gateway_1.apiCall)(Object.assign(Object.assign({}, props), { path: `projects/${props.project_id}/operations/${props.operation_id}`, method: 'GET' }));
|
|
32
|
+
exports.getOperation = getOperation;
|
package/src/api/roles.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.resetPassword = void 0;
|
|
13
|
+
const gateway_1 = require("./gateway");
|
|
14
|
+
const operations_1 = require("./operations");
|
|
15
|
+
const resetPassword = (props) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const response = yield (0, gateway_1.apiCall)(Object.assign(Object.assign({}, props), { path: `projects/${props.project_id}/roles/${props.role_name}/reset_password`, method: 'POST' }));
|
|
17
|
+
yield (0, operations_1.waitOperationFinalState)(Object.assign(Object.assign({}, props), response));
|
|
18
|
+
return response;
|
|
19
|
+
});
|
|
20
|
+
exports.resetPassword = resetPassword;
|
package/src/auth.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.auth = exports.refreshToken = void 0;
|
|
15
|
+
exports.auth = exports.refreshToken = exports.defaultClientID = void 0;
|
|
16
16
|
const openid_client_1 = require("openid-client");
|
|
17
17
|
const node_http_1 = require("node:http");
|
|
18
18
|
const node_fs_1 = require("node:fs");
|
|
@@ -24,15 +24,15 @@ const SERVER_TIMEOUT = 10000;
|
|
|
24
24
|
// where to wait for incoming redirect request from oauth server to arrive
|
|
25
25
|
const REDIRECT_URI = (port) => `http://127.0.0.1:${port}/callback`;
|
|
26
26
|
// These scopes cannot be cancelled, they are always needed.
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'offline_access',
|
|
27
|
+
const ALWAYS_PRESENT_SCOPES = ['openid', 'offline', 'offline_access'];
|
|
28
|
+
const NEONCTL_SCOPES = [
|
|
29
|
+
...ALWAYS_PRESENT_SCOPES,
|
|
31
30
|
'urn:neoncloud:projects:create',
|
|
32
31
|
'urn:neoncloud:projects:read',
|
|
33
|
-
'urn:neoncloud:projects:
|
|
32
|
+
'urn:neoncloud:projects:update',
|
|
34
33
|
'urn:neoncloud:projects:delete',
|
|
35
34
|
];
|
|
35
|
+
exports.defaultClientID = 'neonctl';
|
|
36
36
|
openid_client_1.custom.setHttpOptionsDefaults({
|
|
37
37
|
timeout: SERVER_TIMEOUT,
|
|
38
38
|
});
|
|
@@ -94,8 +94,9 @@ const auth = ({ oauthHost, clientId }) => __awaiter(void 0, void 0, void 0, func
|
|
|
94
94
|
//
|
|
95
95
|
// Open browser to let user authenticate
|
|
96
96
|
//
|
|
97
|
+
const scopes = clientId == exports.defaultClientID ? NEONCTL_SCOPES : ALWAYS_PRESENT_SCOPES;
|
|
97
98
|
const authUrl = neonOAuthClient.authorizationUrl({
|
|
98
|
-
scope:
|
|
99
|
+
scope: scopes.join(' '),
|
|
99
100
|
state,
|
|
100
101
|
code_challenge: codeChallenge,
|
|
101
102
|
code_challenge_method: 'S256',
|
package/src/commands/auth.js
CHANGED
|
@@ -94,7 +94,11 @@ const ensureAuth = (props) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
const token = tokenSet.access_token || 'UNKNOWN';
|
|
97
|
-
yield validateToken({
|
|
97
|
+
yield validateToken({
|
|
98
|
+
apiHost: props['api-host'],
|
|
99
|
+
token,
|
|
100
|
+
output: 'json',
|
|
101
|
+
});
|
|
98
102
|
props.token = token;
|
|
99
103
|
return;
|
|
100
104
|
}
|
package/src/commands/projects.js
CHANGED
|
@@ -13,7 +13,9 @@ exports.create = exports.list = void 0;
|
|
|
13
13
|
const projects_1 = require("../api/projects");
|
|
14
14
|
const writer_1 = require("../writer");
|
|
15
15
|
const list = (props) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
-
(0, writer_1.writeOut)(props)(yield (0, projects_1.listProjects)(props), {
|
|
16
|
+
(0, writer_1.writeOut)(props)(yield (0, projects_1.listProjects)(props), {
|
|
17
|
+
fields: ['id', 'name', 'region_name', 'created_at'],
|
|
18
|
+
});
|
|
17
19
|
});
|
|
18
20
|
exports.list = list;
|
|
19
21
|
const create = (props) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.resetPwd = void 0;
|
|
13
|
+
const writer_1 = require("../writer");
|
|
14
|
+
const roles_1 = require("../api/roles");
|
|
15
|
+
const resetPwd = (props) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
(0, writer_1.writeOut)(props)(yield (0, roles_1.resetPassword)(props), { fields: ['dsn'] });
|
|
17
|
+
});
|
|
18
|
+
exports.resetPwd = resetPwd;
|
package/src/index.js
CHANGED
|
@@ -41,6 +41,7 @@ const gateway_1 = require("./api/gateway");
|
|
|
41
41
|
const auth_1 = require("./commands/auth");
|
|
42
42
|
const config_1 = require("./config");
|
|
43
43
|
const log_1 = require("./log");
|
|
44
|
+
const auth_2 = require("./auth");
|
|
44
45
|
const showHelpMiddleware = (argv) => {
|
|
45
46
|
if (argv._.length === 1) {
|
|
46
47
|
yargs.showHelp();
|
|
@@ -51,9 +52,11 @@ const builder = yargs
|
|
|
51
52
|
.scriptName(package_json_1.default.name)
|
|
52
53
|
.usage('usage: $0 <cmd> [args]')
|
|
53
54
|
.help()
|
|
54
|
-
.option('
|
|
55
|
-
describe: 'Set output format
|
|
56
|
-
type: '
|
|
55
|
+
.option('output', {
|
|
56
|
+
describe: 'Set output format',
|
|
57
|
+
type: 'string',
|
|
58
|
+
choices: ['json', 'yaml', 'table'],
|
|
59
|
+
default: 'table',
|
|
57
60
|
})
|
|
58
61
|
.option('api-host', {
|
|
59
62
|
describe: 'The API host',
|
|
@@ -74,7 +77,7 @@ const builder = yargs
|
|
|
74
77
|
.option('client-id', {
|
|
75
78
|
description: 'OAuth client id',
|
|
76
79
|
type: 'string',
|
|
77
|
-
default:
|
|
80
|
+
default: auth_2.defaultClientID,
|
|
78
81
|
})
|
|
79
82
|
.command('auth', 'Authenticate user', (yargs) => yargs, (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
80
83
|
(yield Promise.resolve().then(() => __importStar(require('./commands/auth')))).authFlow(args);
|
|
@@ -87,18 +90,32 @@ const builder = yargs
|
|
|
87
90
|
})
|
|
88
91
|
.command('me', 'Get user info', (yargs) => yargs.middleware(auth_1.ensureAuth), (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
89
92
|
yield (yield Promise.resolve().then(() => __importStar(require('./commands/users')))).me(args);
|
|
93
|
+
}))
|
|
94
|
+
.command('roles', 'Manage roles', (yargs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
|
+
yargs
|
|
96
|
+
.usage('usage: $0 roles <cmd> [args]')
|
|
97
|
+
.command('resetpassword', 'Reset password for a role', (yargs) => yargs
|
|
98
|
+
.option('project-id', {
|
|
99
|
+
describe: 'Project ID',
|
|
100
|
+
type: 'string',
|
|
101
|
+
demandOption: true,
|
|
102
|
+
})
|
|
103
|
+
.option('role-name', {
|
|
104
|
+
describe: 'Role name',
|
|
105
|
+
type: 'string',
|
|
106
|
+
demandOption: true,
|
|
107
|
+
}), (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
+
yield (yield Promise.resolve().then(() => __importStar(require('./commands/roles')))).resetPwd(Object.assign(Object.assign({}, args), { role_name: args['role-name'], project_id: args['project-id'] }));
|
|
109
|
+
}))
|
|
110
|
+
.middleware(showHelpMiddleware)
|
|
111
|
+
.middleware(auth_1.ensureAuth);
|
|
90
112
|
}))
|
|
91
113
|
.command('projects', 'Manage projects', (yargs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
114
|
yargs
|
|
93
115
|
.usage('usage: $0 projects <cmd> [args]')
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// (yargs) => yargs,
|
|
98
|
-
// async (args) => {
|
|
99
|
-
// await (await import('./commands/projects')).list(args);
|
|
100
|
-
// }
|
|
101
|
-
// )
|
|
116
|
+
.command('list', 'List projects', (yargs) => yargs, (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
|
+
yield (yield Promise.resolve().then(() => __importStar(require('./commands/projects')))).list(args);
|
|
118
|
+
}))
|
|
102
119
|
.command('create', 'Create a project', (yargs) => yargs.option('name', {
|
|
103
120
|
describe: 'Project name',
|
|
104
121
|
type: 'string',
|
package/src/writer.js
CHANGED
|
@@ -4,9 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.writeOut = void 0;
|
|
7
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
7
8
|
const cli_table_1 = __importDefault(require("cli-table"));
|
|
9
|
+
// Allow PIPE to finish reading before the end of the output.
|
|
10
|
+
process.stdout.on('error', function (err) {
|
|
11
|
+
if (err.code == 'EPIPE') {
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
8
15
|
const writeOut = (props) => (data, config) => {
|
|
9
|
-
if (props.
|
|
16
|
+
if (props.output == 'yaml') {
|
|
17
|
+
process.stdout.write(yaml_1.default.stringify(data, null, 2));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (props.output == 'json') {
|
|
10
21
|
process.stdout.write(JSON.stringify(data, null, 2));
|
|
11
22
|
return;
|
|
12
23
|
}
|