neutrinos-cli 1.0.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/.configs/auth.json +5 -0
- package/.configs/preferences.json +5 -0
- package/.env +6 -0
- package/README.md +173 -0
- package/bin/cli.js +239 -0
- package/cli-auth/auth.js +54 -0
- package/cli-auth/publish.js +7 -0
- package/cli-auth/server.js +44 -0
- package/cli-auth/services/auth-utils.js +68 -0
- package/commands/alpha-publish.js +219 -0
- package/commands/attribute.js +155 -0
- package/commands/build.js +83 -0
- package/commands/deprecate.js +88 -0
- package/commands/dev.js +21 -0
- package/commands/generate.js +19 -0
- package/commands/new-workspace.js +142 -0
- package/commands/publish.js +334 -0
- package/commands/select-packages.mjs +36 -0
- package/commands/serve.js +27 -0
- package/package.json +34 -0
- package/setup.js +55 -0
- package/templates/assets/default-icon.png +0 -0
- package/templates/component/.component.ts.hbs +126 -0
- package/templates/component/.spec.ts.hbs +15 -0
- package/templates/component/.styles.ts.hbs +2 -0
- package/templates/module/.module.js.hbs +11 -0
- package/templates/plugins-server/index.js +18 -0
- package/templates/project/.vscode/extensions.json +6 -0
- package/templates/project/ATTRIBUTE.md +127 -0
- package/templates/project/Dockerfile +15 -0
- package/templates/project/helmchart/.helmignore +23 -0
- package/templates/project/helmchart/Chart.yaml +24 -0
- package/templates/project/helmchart/templates/NOTES.txt +22 -0
- package/templates/project/helmchart/templates/_helpers.tpl +62 -0
- package/templates/project/helmchart/templates/deployment.yaml +69 -0
- package/templates/project/helmchart/templates/ingress.yaml +62 -0
- package/templates/project/helmchart/templates/service.yaml +14 -0
- package/templates/project/helmchart/values.yaml +74 -0
- package/templates/project/index.html +24 -0
- package/templates/project/index.ts +86 -0
- package/templates/project/public-api.ts +0 -0
- package/templates/project/tsconfig.json +27 -0
- package/utils/attribute-utils.js +149 -0
- package/utils/check-valid-ws.js +21 -0
- package/utils/copy-utils.js +68 -0
- package/utils/create-client.js +23 -0
- package/utils/file-utils.js +43 -0
- package/utils/generate-component.js +101 -0
- package/utils/generate-module.js +51 -0
- package/utils/get-package-info.js +53 -0
- package/utils/get-packages.js +15 -0
- package/utils/inquirer-utils.js +49 -0
- package/utils/logger.js +35 -0
- package/utils/marketplace-api-utils.js +34 -0
- package/utils/path-utils.js +40 -0
- package/utils/prettify.js +36 -0
- package/utils/user-seesion-utils.js +43 -0
package/.env
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
PORT=32231
|
|
2
|
+
ISSUER_URL=https://marketplace.neutrinos-apps.com
|
|
3
|
+
IDS_CLIENT_ID=FYqh5ZhdbIp2SrQPGZYMe
|
|
4
|
+
IDS_CLIENT_SECRET=7aJ3-A70E7rS-o5rBwspFeeefM6OfCfOODEy1xDn4iqLLVRreKgvkE9N61cz2I3pn8I0n_DjHAsxr3iDHnAQJw
|
|
5
|
+
MARKETPLACE_URL=https://marketplace.neutrinos-apps.com/marketplace-headless/api/packages/
|
|
6
|
+
MARKETPLACE_URL_PROD=https://marketplace.neutrinos-apps.com/marketplace-headless/api/packages/
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# neutrinos CLI
|
|
2
|
+
|
|
3
|
+
neutrinos CLI is a command-line interface for managing, developing and publishing the packages.
|
|
4
|
+
|
|
5
|
+
### Installation
|
|
6
|
+
|
|
7
|
+
To install neutrinos CLI, use the following command:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i -g neutrinos-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
neutrinos command [options]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Commands
|
|
20
|
+
|
|
21
|
+
- `new <name>` Create a new project
|
|
22
|
+
- `generate` | `g` Generate a new plugin. Run "help generate" for more information.
|
|
23
|
+
- `auth` Perform authentication tasks like login, logout, or status
|
|
24
|
+
- `publish [options] [packageName]` Bundle and publish the plugin
|
|
25
|
+
- `build <type> [pluginName]` Build the plugin
|
|
26
|
+
- `start [options]` Start the live server for the plugin
|
|
27
|
+
- `serve` Start the express server that serves the plugins
|
|
28
|
+
|
|
29
|
+
### Options
|
|
30
|
+
|
|
31
|
+
- `-v, --version` Output the version number
|
|
32
|
+
- `-h, --help` Display help for command
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### `new <name>`
|
|
41
|
+
|
|
42
|
+
##### Description
|
|
43
|
+
|
|
44
|
+
- Create a new project with the given name. The project will be created in the current directory.
|
|
45
|
+
|
|
46
|
+
##### Usage
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
neutrinos new <name>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
##### Arguments
|
|
53
|
+
|
|
54
|
+
- `<name>` project or workspace name
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### `generate` | `g`
|
|
59
|
+
|
|
60
|
+
##### Description
|
|
61
|
+
|
|
62
|
+
- Generate a new component or module
|
|
63
|
+
|
|
64
|
+
##### Usage
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
neutrinos generate [command] [argument] || neutrinos g [command] [argument]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
##### commands
|
|
71
|
+
|
|
72
|
+
- `component <name>` | `c <name>` Component is a package enabling drag and drop functionality within the canvas
|
|
73
|
+
- `module <name>` | `m <name>` Module is a globally accessible package, available throughout the entire project on `alpha.plugins` | `ap`.
|
|
74
|
+
- `attribute [command] [options]` | `a [command] [options]` Generate an Attribute for a component, Read the `ATTRIBUTE.md` file in projetc/workspcae directory for more help
|
|
75
|
+
|
|
76
|
+
##### Arguments
|
|
77
|
+
|
|
78
|
+
- `<name>` component or module name
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## `Build <type> [pluginName]`
|
|
83
|
+
|
|
84
|
+
#### Description
|
|
85
|
+
|
|
86
|
+
- Build the plugins
|
|
87
|
+
|
|
88
|
+
#### Usage
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
neutrinos build <type> [pluginName]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Arguments
|
|
95
|
+
|
|
96
|
+
- `<type>` The type of build, it can be one of `plugin` or `docker`
|
|
97
|
+
- `pluginName` package Name of the component/module to build, it is an optional argument and if not pluginName it will build every package in the current workspace
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## `start [options]`
|
|
102
|
+
|
|
103
|
+
#### Description
|
|
104
|
+
|
|
105
|
+
- Start the live server for the workspace
|
|
106
|
+
|
|
107
|
+
#### Usage
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
neutrinos start [options]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Options
|
|
114
|
+
|
|
115
|
+
- `-p, --port <port>` Port to start the server on, default is 6969
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## `serve`
|
|
120
|
+
|
|
121
|
+
#### Description
|
|
122
|
+
|
|
123
|
+
- Start the express server that serves the plugins
|
|
124
|
+
|
|
125
|
+
#### Usage
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
neutrinos serve
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `auth [command]`
|
|
134
|
+
|
|
135
|
+
#### Description
|
|
136
|
+
|
|
137
|
+
- Perform authentication tasks like login or status
|
|
138
|
+
|
|
139
|
+
#### Usage
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
neutrinos auth [command]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Commands:
|
|
146
|
+
|
|
147
|
+
- `login` Authenticate with the server
|
|
148
|
+
- `state` Get the current state of auth
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## `publish [PackageName] [options]`
|
|
153
|
+
|
|
154
|
+
#### Description
|
|
155
|
+
|
|
156
|
+
- Publish a package to the server
|
|
157
|
+
|
|
158
|
+
#### Usage
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
neutrinos publish [PackageName] [Options]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Arguments
|
|
165
|
+
|
|
166
|
+
- `PackageName` The name of the component/module to publish, if not cli will show the list of packages inside the current workspace/project
|
|
167
|
+
|
|
168
|
+
#### Options
|
|
169
|
+
|
|
170
|
+
- `-t, --version-type <versionType>` Version type for verion up, it can be one of `patch`, `minor`, `major`
|
|
171
|
+
- `-d, --display-name <displayName>` Display Name for the package
|
|
172
|
+
- `-i, --icon <iconPath>` Path to the icon
|
|
173
|
+
- `-m, --images <imagePaths...>` path to the image
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
//@ts-check
|
|
3
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
dotenv.config({
|
|
6
|
+
path: join(__dirname, '../.env'),
|
|
7
|
+
});
|
|
8
|
+
////////////////////////////////////
|
|
9
|
+
import { greenBright } from 'colorette';
|
|
10
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
11
|
+
import EventEmitter from 'node:events';
|
|
12
|
+
import { cwd, env, exit } from 'node:process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import open from 'open';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { init } from '../cli-auth/server.js';
|
|
17
|
+
import { publish as alphaPublish } from '../commands/alpha-publish.js';
|
|
18
|
+
import { addAttribute } from '../commands/attribute.js';
|
|
19
|
+
import { build } from '../commands/build.js';
|
|
20
|
+
import { deprecate } from '../commands/deprecate.js';
|
|
21
|
+
import { servePlugin } from '../commands/dev.js';
|
|
22
|
+
import { generate } from '../commands/generate.js';
|
|
23
|
+
import { createWorkspace } from '../commands/new-workspace.js';
|
|
24
|
+
import { publish } from '../commands/publish.js';
|
|
25
|
+
import { startPluginsServer } from '../commands/serve.js';
|
|
26
|
+
import { validateWorkspace } from '../utils/check-valid-ws.js';
|
|
27
|
+
import { done, failed, inprogress, log } from '../utils/logger.js';
|
|
28
|
+
import { authConfigJson } from '../utils/path-utils.js';
|
|
29
|
+
export const createProgram = () => {
|
|
30
|
+
const program = new Command();
|
|
31
|
+
|
|
32
|
+
program.version('1.0.0', '-v, --version', 'Output the version number');
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command('new <name>')
|
|
36
|
+
.description('Create a new project')
|
|
37
|
+
.action((name) => {
|
|
38
|
+
createWorkspace(join(cwd(), name), name);
|
|
39
|
+
});
|
|
40
|
+
const generateCmd = program
|
|
41
|
+
.command('generate')
|
|
42
|
+
.alias('g')
|
|
43
|
+
.description('Generate a new plugin. Run "help generate" for more information.');
|
|
44
|
+
|
|
45
|
+
generateCmd
|
|
46
|
+
.command('component')
|
|
47
|
+
.alias('c')
|
|
48
|
+
.argument('<name>', 'Component name')
|
|
49
|
+
.option('-d,--description <description>', 'Description of the package')
|
|
50
|
+
.description('Generate a new component plugin')
|
|
51
|
+
.action(async (name, options) => {
|
|
52
|
+
await generate.component({
|
|
53
|
+
name,
|
|
54
|
+
dir: cwd(),
|
|
55
|
+
description: options.description,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
generateCmd
|
|
59
|
+
.command('module')
|
|
60
|
+
.alias('m')
|
|
61
|
+
.argument('<name>', 'Module name')
|
|
62
|
+
.option('-d,--description <description>', 'Description of the package')
|
|
63
|
+
.description('Generate a new module plugin')
|
|
64
|
+
.action(async (name, options) => {
|
|
65
|
+
await generate.module({
|
|
66
|
+
name,
|
|
67
|
+
dir: cwd(),
|
|
68
|
+
description: options.description,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
generateCmd
|
|
72
|
+
.command('attribute')
|
|
73
|
+
.alias('a')
|
|
74
|
+
.argument('[componentName]', 'Component name')
|
|
75
|
+
.option('-t , --type <attributeType>', 'Type of attribute can be property, event, validation')
|
|
76
|
+
.option(
|
|
77
|
+
'-u , --ui-type <uiType>',
|
|
78
|
+
'Ui-type of attribute can be ===> input, toggle, dropdown, multi-select, typed-input, range, color-picker, data-source, data-set, table-actions, data-mapping',
|
|
79
|
+
)
|
|
80
|
+
.option('-l , --label <label>', 'Label for an attribute')
|
|
81
|
+
.option('-e , --event <event>', 'Event name for an attribute if -t/--type is event')
|
|
82
|
+
.option('-c , --category <category>', 'Category for an attribute')
|
|
83
|
+
.option('-p , --placeholder <placeholder>', 'Placeholder for an attribute')
|
|
84
|
+
.option('-d , --defaultValue <defaultValue>', 'DefaultValue for an attribute')
|
|
85
|
+
.option('-o , --options <options...>', 'Options for an attribute')
|
|
86
|
+
.option('-f , --fieldMappings <FieldMappings>', 'FieldMappings for package')
|
|
87
|
+
.option('-n , --validation <validation>', 'Validation for an attribute')
|
|
88
|
+
.description('Generate an Attribute for component')
|
|
89
|
+
.action(async (componentName, options) => {
|
|
90
|
+
return await addAttribute(componentName, cwd(), options);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const authCommand = program.command('auth').description('perform: login, logout, or status');
|
|
94
|
+
authCommand
|
|
95
|
+
.command('login')
|
|
96
|
+
.description('authenticate with the server')
|
|
97
|
+
.action((action) => {
|
|
98
|
+
return new Promise(async (resolve, reject) => {
|
|
99
|
+
inprogress(`logging in...`);
|
|
100
|
+
const cliServer = await init({
|
|
101
|
+
tokenSetPath: authConfigJson(),
|
|
102
|
+
});
|
|
103
|
+
if (!cliServer) {
|
|
104
|
+
failed('Issuer not found');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const eventEmitter = new EventEmitter();
|
|
108
|
+
eventEmitter.on('cli-auth:login', () => {
|
|
109
|
+
done('login successful');
|
|
110
|
+
resolve();
|
|
111
|
+
process.exit(0);
|
|
112
|
+
});
|
|
113
|
+
eventEmitter.on('cli-auth:login-failed', () => {
|
|
114
|
+
failed('login failed');
|
|
115
|
+
reject();
|
|
116
|
+
process.exit(1);
|
|
117
|
+
});
|
|
118
|
+
process.on('SIGINT', () => {
|
|
119
|
+
failed('exitting unexpectedly');
|
|
120
|
+
cliServer.stop();
|
|
121
|
+
exit(0);
|
|
122
|
+
});
|
|
123
|
+
process.on('SIGTERM', () => {
|
|
124
|
+
failed('exitting unexpectedly');
|
|
125
|
+
cliServer.stop();
|
|
126
|
+
exit(0);
|
|
127
|
+
});
|
|
128
|
+
await cliServer.start(eventEmitter);
|
|
129
|
+
const url = `http://localhost:${env.PORT}/login`;
|
|
130
|
+
inprogress(`open to login ${greenBright(url)}`);
|
|
131
|
+
await open(url);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
authCommand
|
|
135
|
+
.command('state')
|
|
136
|
+
.description('get the current state of auth')
|
|
137
|
+
.action(() => {
|
|
138
|
+
console.log('state');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
program
|
|
142
|
+
.command('publish')
|
|
143
|
+
.argument('[packageName]', 'Component name')
|
|
144
|
+
.option(
|
|
145
|
+
'-t, --version-type <versionType>',
|
|
146
|
+
'Version type for verion up can be one of "patch", "minor", "major"',
|
|
147
|
+
)
|
|
148
|
+
.option('-d, --display-name <displayName>', 'Display Name for the package')
|
|
149
|
+
.option('-i, --icon <iconPath>', 'Icon path')
|
|
150
|
+
.option('-m, --images <imagePaths...>', 'Image paths')
|
|
151
|
+
.description('Bundle and publish the plugin')
|
|
152
|
+
.action(async (packageName, options) => {
|
|
153
|
+
if (
|
|
154
|
+
options.versionType &&
|
|
155
|
+
!['patch', 'minor', 'major'].includes((options.versionType || '').toLowerCase())
|
|
156
|
+
) {
|
|
157
|
+
failed('Invalid version type should be one of "patch", "minor", "major"');
|
|
158
|
+
exit(0);
|
|
159
|
+
}
|
|
160
|
+
return await publish(packageName, cwd(), options);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
program
|
|
164
|
+
.command('alpha:publish')
|
|
165
|
+
.argument('[packageName]', 'Component name')
|
|
166
|
+
.option(
|
|
167
|
+
'-t, --version-type <versionType>',
|
|
168
|
+
'Version type for version up can be one of "patch", "minor", "major"',
|
|
169
|
+
)
|
|
170
|
+
.option('-d, --display-name <displayName>', 'Display Name for the package')
|
|
171
|
+
.option('-i, --icon <iconPath>', 'Icon path')
|
|
172
|
+
.option('-m, --images <imagePaths...>', 'Image paths')
|
|
173
|
+
.option('--accept-defaults', 'Accept all default values')
|
|
174
|
+
.option('--all', 'Select all the packages')
|
|
175
|
+
.description('Bundle and publish the plugin')
|
|
176
|
+
.action(async (packageName, options) => {
|
|
177
|
+
if (
|
|
178
|
+
options.versionType &&
|
|
179
|
+
!['patch', 'minor', 'major'].includes((options.versionType || '').toLowerCase())
|
|
180
|
+
) {
|
|
181
|
+
failed('Invalid version type should be one of "patch", "minor", "major"');
|
|
182
|
+
exit(0);
|
|
183
|
+
}
|
|
184
|
+
return await alphaPublish(packageName, cwd(), options);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
program
|
|
188
|
+
.command('deprecate')
|
|
189
|
+
.option('-y, --yes', 'Confirmation to deprecate a package')
|
|
190
|
+
.option('--all', 'Select all the packages')
|
|
191
|
+
.argument('[packageName]', 'Package name')
|
|
192
|
+
.action(async (packageName, options) => {
|
|
193
|
+
return await deprecate(packageName, cwd(), options);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
program
|
|
197
|
+
.command('build')
|
|
198
|
+
.argument('<type>', 'Type of build: "plugins" or "docker"', (value, prevValue) => {
|
|
199
|
+
if (value !== 'plugins' && value !== 'docker') {
|
|
200
|
+
throw new InvalidArgumentError('"build" can be "plugins" or "docker"');
|
|
201
|
+
}
|
|
202
|
+
return value;
|
|
203
|
+
})
|
|
204
|
+
.argument('[pluginName]', 'Name of the plugin')
|
|
205
|
+
.option('--module', 'Build the plugin as a module')
|
|
206
|
+
.option('--all', 'Build all the plugin of the workspace')
|
|
207
|
+
.description('Build the plugin')
|
|
208
|
+
.action((type, pluginName, options) => {
|
|
209
|
+
build[type](cwd(), pluginName, options || {});
|
|
210
|
+
});
|
|
211
|
+
program
|
|
212
|
+
.command('start')
|
|
213
|
+
.option('-p, --port <port>', 'Port number for serving the plugin')
|
|
214
|
+
.description('start the live server for the plugin')
|
|
215
|
+
.action((options) => {
|
|
216
|
+
servePlugin(cwd(), options.port || 6969);
|
|
217
|
+
});
|
|
218
|
+
program
|
|
219
|
+
.command('serve')
|
|
220
|
+
.description('Start the express server that serves the plugins')
|
|
221
|
+
.action(() => {
|
|
222
|
+
startPluginsServer(cwd());
|
|
223
|
+
});
|
|
224
|
+
program.hook('preAction', async (thisCmd, actionCmd) => {
|
|
225
|
+
const cmd = actionCmd.name();
|
|
226
|
+
if (cmd === 'new' || cmd === 'login') {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!validateWorkspace(cwd())) {
|
|
230
|
+
log('Please run this command in a valid workspace directory');
|
|
231
|
+
exit(1);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return program;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const program = createProgram();
|
|
239
|
+
program.parse(process.argv);
|
package/cli-auth/auth.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { sep } from 'node:path';
|
|
5
|
+
import { authUtils } from './services/auth-utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {import('openid-client').Client} client
|
|
9
|
+
* @param {string} tokenSetPath
|
|
10
|
+
*/
|
|
11
|
+
export const auth = (client, tokenSetPath) => {
|
|
12
|
+
const authStateNonce = {
|
|
13
|
+
state: '',
|
|
14
|
+
nonce: '',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {import('fastify').FastifyRequest} req
|
|
19
|
+
* @param {import('fastify').FastifyReply} reply
|
|
20
|
+
*/
|
|
21
|
+
return {
|
|
22
|
+
/**
|
|
23
|
+
* @param {import('fastify').FastifyInstance} server
|
|
24
|
+
* @param {EventEmitter} eventEmitter
|
|
25
|
+
*/
|
|
26
|
+
mount: (server, eventEmitter) => {
|
|
27
|
+
server.get('/login', async (request, reply) => {
|
|
28
|
+
const redirectUrl = await authUtils.login(client, authStateNonce);
|
|
29
|
+
reply.redirect(redirectUrl);
|
|
30
|
+
});
|
|
31
|
+
server.get('/login-callback', async (request, reply) => {
|
|
32
|
+
const { tokenSet } = await authUtils.loginCallback(
|
|
33
|
+
client.callbackParams(request),
|
|
34
|
+
client,
|
|
35
|
+
authStateNonce,
|
|
36
|
+
);
|
|
37
|
+
const dirPath = tokenSetPath.substring(0, tokenSetPath.lastIndexOf(sep));
|
|
38
|
+
if (!existsSync(dirPath)) {
|
|
39
|
+
await mkdir(dirPath, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
await writeFile(tokenSetPath, JSON.stringify(tokenSet, null, 2), {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
});
|
|
44
|
+
if (tokenSet) {
|
|
45
|
+
reply.send('Login successful');
|
|
46
|
+
eventEmitter.emit('cli-auth:login');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
reply.send('Login failed');
|
|
50
|
+
eventEmitter.emit('cli-auth:login-failed');
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fastify from 'fastify';
|
|
2
|
+
import { env } from 'node:process';
|
|
3
|
+
import { Issuer } from 'openid-client';
|
|
4
|
+
import { auth } from './auth.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {{tokenSetPath: string}} config
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export const init = async (config) => {
|
|
11
|
+
if (!env.ISSUER_URL) {
|
|
12
|
+
console.error('Issuer URL not found');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const issuer = await Issuer.discover(env.ISSUER_URL);
|
|
16
|
+
if (!issuer) {
|
|
17
|
+
console.error('Issuer not found');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!env.IDS_CLIENT_ID || !env.IDS_CLIENT_SECRET) {
|
|
21
|
+
console.error('Client ID or Client Secret not found');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const client = new issuer.Client({
|
|
25
|
+
client_id: env.IDS_CLIENT_ID,
|
|
26
|
+
client_secret: env.IDS_CLIENT_SECRET,
|
|
27
|
+
});
|
|
28
|
+
const server = fastify();
|
|
29
|
+
const auth_ = auth(client, config.tokenSetPath);
|
|
30
|
+
return {
|
|
31
|
+
start: async (eventEmitter) => {
|
|
32
|
+
const port = parseInt(env.PORT || '32231');
|
|
33
|
+
auth_.mount(server, eventEmitter);
|
|
34
|
+
await server.listen({
|
|
35
|
+
port,
|
|
36
|
+
});
|
|
37
|
+
return server;
|
|
38
|
+
},
|
|
39
|
+
stop: async () => {
|
|
40
|
+
await server.close();
|
|
41
|
+
return server;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { env } from 'node:process';
|
|
4
|
+
|
|
5
|
+
export const authUtils = {
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('openid-client').Client} client
|
|
8
|
+
* @param {{state: string, nonce: string}} authStateNonce
|
|
9
|
+
*/
|
|
10
|
+
login: async (client, authStateNonce) => {
|
|
11
|
+
const authorizationRequest = {
|
|
12
|
+
redirect_uri: `http://localhost:${env.PORT}/login-callback`,
|
|
13
|
+
scope: 'openid profile email address phone offline_access user',
|
|
14
|
+
state: randomBytes(16).toString('hex'),
|
|
15
|
+
nonce: randomBytes(16).toString('hex'),
|
|
16
|
+
response_type: client.response_types[0],
|
|
17
|
+
prompt: 'consent',
|
|
18
|
+
};
|
|
19
|
+
authStateNonce.state = authorizationRequest.state;
|
|
20
|
+
authStateNonce.nonce = authorizationRequest.nonce;
|
|
21
|
+
const redirect = client.authorizationUrl(authorizationRequest);
|
|
22
|
+
return redirect;
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* @param {*} callbackParams
|
|
26
|
+
* @param {import('openid-client').Client} client
|
|
27
|
+
* @param {{state: string, nonce: string}} authStateNonce
|
|
28
|
+
* @returns {Promise<{redirect: string, tokenSet: null | any}>}
|
|
29
|
+
*/
|
|
30
|
+
loginCallback: async (callbackParams, client, authStateNonce) => {
|
|
31
|
+
/** @type {{redirect: string, tokenSet: null | any}} */
|
|
32
|
+
const result = {
|
|
33
|
+
redirect: '',
|
|
34
|
+
tokenSet: null,
|
|
35
|
+
};
|
|
36
|
+
if (Object.keys(callbackParams).length === 0) {
|
|
37
|
+
result.redirect = '/login';
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const tokenset = await client.callback(`http://localhost:${env.PORT}/login-callback`, callbackParams, {
|
|
41
|
+
nonce: authStateNonce.nonce,
|
|
42
|
+
state: authStateNonce.state,
|
|
43
|
+
});
|
|
44
|
+
authStateNonce.state = '';
|
|
45
|
+
authStateNonce.nonce = '';
|
|
46
|
+
result.tokenSet = tokenset;
|
|
47
|
+
result.redirect = '/login-success';
|
|
48
|
+
return result;
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* @param {import('openid-client').Client} client
|
|
52
|
+
* @param {string} tokenSetPath
|
|
53
|
+
* @returns {Promise<import('openid-client').IntrospectionResponse>}
|
|
54
|
+
*/
|
|
55
|
+
verifyToken: async (client, tokenSetPath) => {
|
|
56
|
+
const tokenSet = JSON.parse(await readFile(tokenSetPath, 'utf-8'));
|
|
57
|
+
return await client.introspect(tokenSet.access_token);
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* @param {import('openid-client').Client} client
|
|
61
|
+
* @param {string} tokenSetPath
|
|
62
|
+
*/
|
|
63
|
+
refreshToken: async (client, tokenSetPath) => {
|
|
64
|
+
const tokenSet = JSON.parse(await readFile(tokenSetPath, 'utf-8'));
|
|
65
|
+
const updatedTokenSet = await client.refresh(tokenSet.refresh_token);
|
|
66
|
+
await writeFile(tokenSetPath, JSON.stringify(updatedTokenSet, null, 2));
|
|
67
|
+
},
|
|
68
|
+
};
|