@zappinginc/zm2 6.0.14
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/.claude/settings.local.json +8 -0
- package/.gitattributes +4 -0
- package/.mocharc.js +14 -0
- package/CHANGELOG.md +2416 -0
- package/CLAUDE.md +84 -0
- package/CONTRIBUTING.md +124 -0
- package/GNU-AGPL-3.0.txt +665 -0
- package/LICENSE +1 -0
- package/README.md +248 -0
- package/bin/zm2 +3 -0
- package/bin/zm2-dev +3 -0
- package/bin/zm2-docker +3 -0
- package/bin/zm2-runtime +3 -0
- package/bin/zm2-windows +3 -0
- package/bin/zm2.ps1 +3 -0
- package/bun.lock +421 -0
- package/constants.js +114 -0
- package/index.js +13 -0
- package/lib/API/Configuration.js +212 -0
- package/lib/API/Containerizer.js +335 -0
- package/lib/API/Dashboard.js +459 -0
- package/lib/API/Deploy.js +117 -0
- package/lib/API/Extra.js +775 -0
- package/lib/API/ExtraMgmt/Docker.js +30 -0
- package/lib/API/Log.js +315 -0
- package/lib/API/LogManagement.js +371 -0
- package/lib/API/Modules/LOCAL.js +122 -0
- package/lib/API/Modules/Modularizer.js +148 -0
- package/lib/API/Modules/NPM.js +445 -0
- package/lib/API/Modules/TAR.js +362 -0
- package/lib/API/Modules/flagExt.js +46 -0
- package/lib/API/Modules/index.js +120 -0
- package/lib/API/Monit.js +247 -0
- package/lib/API/Serve.js +343 -0
- package/lib/API/Startup.js +629 -0
- package/lib/API/UX/helpers.js +213 -0
- package/lib/API/UX/index.js +9 -0
- package/lib/API/UX/pm2-describe.js +193 -0
- package/lib/API/UX/pm2-ls-minimal.js +31 -0
- package/lib/API/UX/pm2-ls.js +483 -0
- package/lib/API/Version.js +382 -0
- package/lib/API/interpreter.json +12 -0
- package/lib/API/pm2-plus/PM2IO.js +372 -0
- package/lib/API/pm2-plus/auth-strategies/CliAuth.js +288 -0
- package/lib/API/pm2-plus/auth-strategies/WebAuth.js +187 -0
- package/lib/API/pm2-plus/helpers.js +97 -0
- package/lib/API/pm2-plus/link.js +126 -0
- package/lib/API/pm2-plus/pres/motd +16 -0
- package/lib/API/pm2-plus/pres/motd.update +26 -0
- package/lib/API/pm2-plus/pres/welcome +28 -0
- package/lib/API/pm2-plus/process-selector.js +52 -0
- package/lib/API/schema.json +379 -0
- package/lib/API.js +1931 -0
- package/lib/Client.js +776 -0
- package/lib/Common.js +911 -0
- package/lib/Configuration.js +304 -0
- package/lib/Daemon.js +456 -0
- package/lib/Event.js +37 -0
- package/lib/God/ActionMethods.js +909 -0
- package/lib/God/ClusterMode.js +97 -0
- package/lib/God/ForkMode.js +297 -0
- package/lib/God/Methods.js +265 -0
- package/lib/God/Reload.js +240 -0
- package/lib/God.js +632 -0
- package/lib/HttpInterface.js +76 -0
- package/lib/ProcessContainer.js +305 -0
- package/lib/ProcessContainerBun.js +360 -0
- package/lib/ProcessContainerFork.js +42 -0
- package/lib/ProcessContainerForkBun.js +33 -0
- package/lib/ProcessUtils.js +55 -0
- package/lib/TreeKill.js +118 -0
- package/lib/Utility.js +430 -0
- package/lib/VersionCheck.js +46 -0
- package/lib/Watcher.js +117 -0
- package/lib/Worker.js +169 -0
- package/lib/binaries/CLI.js +1041 -0
- package/lib/binaries/DevCLI.js +183 -0
- package/lib/binaries/Runtime.js +101 -0
- package/lib/binaries/Runtime4Docker.js +192 -0
- package/lib/completion.js +229 -0
- package/lib/completion.sh +40 -0
- package/lib/motd +36 -0
- package/lib/templates/Dockerfiles/Dockerfile-java.tpl +7 -0
- package/lib/templates/Dockerfiles/Dockerfile-nodejs.tpl +8 -0
- package/lib/templates/Dockerfiles/Dockerfile-ruby.tpl +7 -0
- package/lib/templates/ecosystem-es.tpl +24 -0
- package/lib/templates/ecosystem-simple-es.tpl +8 -0
- package/lib/templates/ecosystem-simple.tpl +6 -0
- package/lib/templates/ecosystem.tpl +22 -0
- package/lib/templates/init-scripts/launchd.tpl +35 -0
- package/lib/templates/init-scripts/openrc.tpl +52 -0
- package/lib/templates/init-scripts/pm2-init-amazon.sh +86 -0
- package/lib/templates/init-scripts/rcd-openbsd.tpl +41 -0
- package/lib/templates/init-scripts/rcd.tpl +44 -0
- package/lib/templates/init-scripts/smf.tpl +43 -0
- package/lib/templates/init-scripts/systemd-online.tpl +22 -0
- package/lib/templates/init-scripts/systemd.tpl +22 -0
- package/lib/templates/init-scripts/upstart.tpl +103 -0
- package/lib/templates/logrotate.d/pm2 +10 -0
- package/lib/templates/sample-apps/http-server/README.md +14 -0
- package/lib/templates/sample-apps/http-server/api.js +9 -0
- package/lib/templates/sample-apps/http-server/ecosystem.config.js +14 -0
- package/lib/templates/sample-apps/http-server/package.json +11 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/README.md +45 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/custom-metrics.js +66 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/ecosystem.config.js +12 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/package.json +11 -0
- package/lib/templates/sample-apps/python-app/README.md +4 -0
- package/lib/templates/sample-apps/python-app/echo.py +7 -0
- package/lib/templates/sample-apps/python-app/ecosystem.config.js +12 -0
- package/lib/templates/sample-apps/python-app/package.json +11 -0
- package/lib/tools/Config.js +248 -0
- package/lib/tools/IsAbsolute.js +20 -0
- package/lib/tools/copydirSync.js +101 -0
- package/lib/tools/deleteFolderRecursive.js +19 -0
- package/lib/tools/find-package-json.js +74 -0
- package/lib/tools/fmt.js +72 -0
- package/lib/tools/isbinaryfile.js +94 -0
- package/lib/tools/json5.js +752 -0
- package/lib/tools/open.js +63 -0
- package/lib/tools/passwd.js +58 -0
- package/lib/tools/promise.min.js +1 -0
- package/lib/tools/sexec.js +55 -0
- package/lib/tools/treeify.js +113 -0
- package/lib/tools/which.js +120 -0
- package/lib/tools/xdg-open +861 -0
- package/package.json +219 -0
- package/paths.js +93 -0
- package/pm2 +11 -0
- package/preinstall.js +24 -0
- package/run.sh +9 -0
- package/types/index.d.ts +722 -0
- package/types/tsconfig.json +14 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var cst = require('../../../constants.js');
|
|
4
|
+
const chalk = require('ansis');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const Table = require('cli-tableau');
|
|
8
|
+
const pkg = require('../../../package.json')
|
|
9
|
+
const IOAPI = require('@pm2/js-api')
|
|
10
|
+
const promptly = require('promptly')
|
|
11
|
+
var CLIStrategy = require('./auth-strategies/CliAuth')
|
|
12
|
+
var WebStrategy = require('./auth-strategies/WebAuth')
|
|
13
|
+
const exec = require('child_process').exec
|
|
14
|
+
|
|
15
|
+
const OAUTH_CLIENT_ID_WEB = '138558311'
|
|
16
|
+
const OAUTH_CLIENT_ID_CLI = '0943857435'
|
|
17
|
+
|
|
18
|
+
module.exports = class PM2ioHandler {
|
|
19
|
+
|
|
20
|
+
static usePM2Client (instance) {
|
|
21
|
+
this.pm2 = instance
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static strategy () {
|
|
25
|
+
switch (process.platform) {
|
|
26
|
+
case 'darwin': {
|
|
27
|
+
return new WebStrategy({
|
|
28
|
+
client_id: OAUTH_CLIENT_ID_WEB
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
case 'win32': {
|
|
32
|
+
return new WebStrategy({
|
|
33
|
+
client_id: OAUTH_CLIENT_ID_WEB
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
case 'linux': {
|
|
37
|
+
const isDesktop = process.env.XDG_CURRENT_DESKTOP || process.env.XDG_SESSION_DESKTOP || process.env.DISPLAY
|
|
38
|
+
const isSSH = process.env.SSH_TTY || process.env.SSH_CONNECTION
|
|
39
|
+
if (isDesktop && !isSSH) {
|
|
40
|
+
return new WebStrategy({
|
|
41
|
+
client_id: OAUTH_CLIENT_ID_WEB
|
|
42
|
+
})
|
|
43
|
+
} else {
|
|
44
|
+
return new CLIStrategy({
|
|
45
|
+
client_id: OAUTH_CLIENT_ID_CLI
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
default: {
|
|
50
|
+
return new CLIStrategy({
|
|
51
|
+
client_id: OAUTH_CLIENT_ID_CLI
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static init () {
|
|
58
|
+
this._strategy = this.strategy()
|
|
59
|
+
/**
|
|
60
|
+
* If you are using a local backend you should give those options :
|
|
61
|
+
* {
|
|
62
|
+
* services: {
|
|
63
|
+
* API: 'http://localhost:3000',
|
|
64
|
+
* OAUTH: 'http://localhost:3100'
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
*/
|
|
68
|
+
this.io = new IOAPI().use(this._strategy)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static launch (command, opts) {
|
|
72
|
+
// first init the strategy and the io client
|
|
73
|
+
this.init()
|
|
74
|
+
|
|
75
|
+
switch (command) {
|
|
76
|
+
case 'connect' :
|
|
77
|
+
case 'login' :
|
|
78
|
+
case 'register' :
|
|
79
|
+
case undefined :
|
|
80
|
+
case 'authenticate' : {
|
|
81
|
+
this.authenticate()
|
|
82
|
+
break
|
|
83
|
+
}
|
|
84
|
+
case 'validate' : {
|
|
85
|
+
this.validateAccount(opts)
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
case 'help' :
|
|
89
|
+
case 'welcome': {
|
|
90
|
+
var dt = fs.readFileSync(path.join(__dirname, './pres/welcome'));
|
|
91
|
+
console.log(dt.toString());
|
|
92
|
+
return process.exit(0)
|
|
93
|
+
}
|
|
94
|
+
case 'logout': {
|
|
95
|
+
this._strategy.isAuthenticated().then(isConnected => {
|
|
96
|
+
// try to kill the agent anyway
|
|
97
|
+
this.pm2.killAgent(err => {})
|
|
98
|
+
|
|
99
|
+
if (isConnected === false) {
|
|
100
|
+
console.log(`${cst.PM2_IO_MSG} Already disconnected`)
|
|
101
|
+
return process.exit(0)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this._strategy._retrieveTokens((err, tokens) => {
|
|
105
|
+
if (err) {
|
|
106
|
+
console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)
|
|
107
|
+
return process.exit(0)
|
|
108
|
+
}
|
|
109
|
+
this._strategy.deleteTokens(this.io).then(_ => {
|
|
110
|
+
console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)
|
|
111
|
+
return process.exit(0)
|
|
112
|
+
}).catch(err => {
|
|
113
|
+
console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error: ${err.message}`)
|
|
114
|
+
return process.exit(1)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
}).catch(err => {
|
|
118
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Failed to logout: ${err.message}`)
|
|
119
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
120
|
+
})
|
|
121
|
+
break
|
|
122
|
+
}
|
|
123
|
+
case 'create': {
|
|
124
|
+
this._strategy.isAuthenticated().then(res => {
|
|
125
|
+
// if the user isn't authenticated, we make them do the whole flow
|
|
126
|
+
if (res !== true) {
|
|
127
|
+
this.authenticate()
|
|
128
|
+
} else {
|
|
129
|
+
this.createBucket(this.createBucketHandler.bind(this))
|
|
130
|
+
}
|
|
131
|
+
}).catch(err => {
|
|
132
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Failed to create to the bucket: ${err.message}`)
|
|
133
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
134
|
+
})
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
case 'web': {
|
|
138
|
+
this._strategy.isAuthenticated().then(res => {
|
|
139
|
+
// if the user isn't authenticated, we make them do the whole flow
|
|
140
|
+
if (res === false) {
|
|
141
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You need to be authenticated to do that, please use: pm2 plus login`)
|
|
142
|
+
return process.exit(1)
|
|
143
|
+
}
|
|
144
|
+
this._strategy._retrieveTokens(() => {
|
|
145
|
+
return this.openUI()
|
|
146
|
+
})
|
|
147
|
+
}).catch(err => {
|
|
148
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Failed to open the UI: ${err.message}`)
|
|
149
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
150
|
+
})
|
|
151
|
+
break
|
|
152
|
+
}
|
|
153
|
+
default : {
|
|
154
|
+
console.log(`${cst.PM2_IO_MSG_ERR} Invalid command ${command}, available : login,register,validate,connect or web`)
|
|
155
|
+
process.exit(1)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static openUI () {
|
|
161
|
+
this.io.bucket.retrieveAll().then(res => {
|
|
162
|
+
const buckets = res.data
|
|
163
|
+
|
|
164
|
+
if (buckets.length === 0) {
|
|
165
|
+
return this.createBucket((err, bucket) => {
|
|
166
|
+
if (err) {
|
|
167
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)
|
|
168
|
+
if (bucket) {
|
|
169
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)
|
|
170
|
+
}
|
|
171
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
172
|
+
return process.exit(0)
|
|
173
|
+
}
|
|
174
|
+
const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
|
|
175
|
+
console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', targetURL)
|
|
176
|
+
this.open(targetURL)
|
|
177
|
+
return process.exit(0)
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
var table = new Table({
|
|
182
|
+
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},
|
|
183
|
+
head : ['Bucket name', 'Plan type']
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
buckets.forEach(function(bucket) {
|
|
187
|
+
table.push([bucket.name, bucket.credits.offer_type])
|
|
188
|
+
})
|
|
189
|
+
console.log(table.toString())
|
|
190
|
+
console.log(`${cst.PM2_IO_MSG} If you don't want to open the UI to a bucket, type 'none'`)
|
|
191
|
+
|
|
192
|
+
const choices = buckets.map(bucket => bucket.name)
|
|
193
|
+
choices.push('none')
|
|
194
|
+
|
|
195
|
+
promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {
|
|
196
|
+
if (value === 'none') process.exit(0)
|
|
197
|
+
|
|
198
|
+
const bucket = buckets.find(bucket => bucket.name === value)
|
|
199
|
+
if (bucket === undefined) return process.exit(0)
|
|
200
|
+
|
|
201
|
+
const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
|
|
202
|
+
console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', targetURL)
|
|
203
|
+
this.open(targetURL)
|
|
204
|
+
return process.exit(0)
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
static validateAccount (token) {
|
|
210
|
+
this.io.auth.validEmail(token)
|
|
211
|
+
.then(res => {
|
|
212
|
+
console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)
|
|
213
|
+
console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)
|
|
214
|
+
return process.exit(0)
|
|
215
|
+
}).catch(err => {
|
|
216
|
+
if (err.status === 401) {
|
|
217
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Invalid token`)
|
|
218
|
+
return process.exit(1)
|
|
219
|
+
} else if (err.status === 301) {
|
|
220
|
+
console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)
|
|
221
|
+
console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)
|
|
222
|
+
return process.exit(0)
|
|
223
|
+
}
|
|
224
|
+
const msg = err.data ? err.data.error_description || err.data.msg : err.message
|
|
225
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Failed to validate your email: ${msg}`)
|
|
226
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
227
|
+
return process.exit(1)
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static createBucketHandler (err, bucket) {
|
|
232
|
+
if (err) {
|
|
233
|
+
console.trace(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)
|
|
234
|
+
if (bucket) {
|
|
235
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)
|
|
236
|
+
}
|
|
237
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
238
|
+
return process.exit(0)
|
|
239
|
+
}
|
|
240
|
+
if (bucket === undefined) {
|
|
241
|
+
return process.exit(0)
|
|
242
|
+
}
|
|
243
|
+
console.log(`${cst.PM2_IO_MSG} Successfully connected to bucket ${bucket.name}`)
|
|
244
|
+
var targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
|
|
245
|
+
console.log(`${cst.PM2_IO_MSG} You can use the web interface over there: ${targetURL}`)
|
|
246
|
+
this.open(targetURL)
|
|
247
|
+
return process.exit(0)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static createBucket (cb) {
|
|
251
|
+
console.log(`${cst.PM2_IO_MSG} By default we allow you to trial PM2 Plus for 14 days without any credit card.`)
|
|
252
|
+
|
|
253
|
+
this.io.bucket.create({
|
|
254
|
+
name: 'PM2 Plus Monitoring'
|
|
255
|
+
}).then(res => {
|
|
256
|
+
const bucket = res.data.bucket
|
|
257
|
+
|
|
258
|
+
console.log(`${cst.PM2_IO_MSG} Successfully created the bucket`)
|
|
259
|
+
this.pm2.link({
|
|
260
|
+
public_key: bucket.public_id,
|
|
261
|
+
secret_key: bucket.secret_id,
|
|
262
|
+
pm2_version: pkg.version
|
|
263
|
+
}, (err) => {
|
|
264
|
+
if (err) {
|
|
265
|
+
return cb(new Error('Failed to connect your local PM2 to your bucket'), bucket)
|
|
266
|
+
} else {
|
|
267
|
+
return cb(null, bucket)
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
}).catch(err => {
|
|
271
|
+
return cb(new Error(`Failed to create a bucket: ${err.message}`))
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Connect the local agent to a specific bucket
|
|
277
|
+
* @param {Function} cb
|
|
278
|
+
*/
|
|
279
|
+
static connectToBucket (cb) {
|
|
280
|
+
this.io.bucket.retrieveAll().then(res => {
|
|
281
|
+
const buckets = res.data
|
|
282
|
+
|
|
283
|
+
if (buckets.length === 0) {
|
|
284
|
+
return this.createBucket(cb)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
var table = new Table({
|
|
288
|
+
style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},
|
|
289
|
+
head : ['Bucket name', 'Plan type']
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
buckets.forEach(function(bucket) {
|
|
293
|
+
table.push([bucket.name, bucket.payment.offer_type])
|
|
294
|
+
})
|
|
295
|
+
console.log(table.toString())
|
|
296
|
+
console.log(`${cst.PM2_IO_MSG} If you don't want to connect to a bucket, type 'none'`)
|
|
297
|
+
|
|
298
|
+
const choices = buckets.map(bucket => bucket.name)
|
|
299
|
+
choices.push('none')
|
|
300
|
+
|
|
301
|
+
promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {
|
|
302
|
+
if (value === 'none') return cb()
|
|
303
|
+
|
|
304
|
+
const bucket = buckets.find(bucket => bucket.name === value)
|
|
305
|
+
if (bucket === undefined) return cb()
|
|
306
|
+
this.pm2.link({
|
|
307
|
+
public_key: bucket.public_id,
|
|
308
|
+
secret_key: bucket.secret_id,
|
|
309
|
+
pm2_version: pkg.version
|
|
310
|
+
}, (err) => {
|
|
311
|
+
return err ? cb(err) : cb(null, bucket)
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Authenticate the user with either of the strategy
|
|
319
|
+
* @param {Function} cb
|
|
320
|
+
*/
|
|
321
|
+
static authenticate () {
|
|
322
|
+
this._strategy._retrieveTokens((err, tokens) => {
|
|
323
|
+
if (err) {
|
|
324
|
+
const msg = err.data ? err.data.error_description || err.data.msg : err.message
|
|
325
|
+
console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error : ${msg}`)
|
|
326
|
+
return process.exit(1)
|
|
327
|
+
}
|
|
328
|
+
console.log(`${cst.PM2_IO_MSG} Successfully authenticated`)
|
|
329
|
+
this.io.user.retrieve().then(res => {
|
|
330
|
+
const user = res.data
|
|
331
|
+
|
|
332
|
+
this.io.user.retrieve().then(res => {
|
|
333
|
+
const tmpUser = res.data
|
|
334
|
+
console.log(`${cst.PM2_IO_MSG} Successfully validated`)
|
|
335
|
+
this.connectToBucket(this.createBucketHandler.bind(this))
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
static open (target, appName, callback) {
|
|
342
|
+
let opener
|
|
343
|
+
const escape = function (s) {
|
|
344
|
+
return s.replace(/"/g, '\\"')
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (typeof (appName) === 'function') {
|
|
348
|
+
callback = appName
|
|
349
|
+
appName = null
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
switch (process.platform) {
|
|
353
|
+
case 'darwin': {
|
|
354
|
+
opener = appName ? `open -a "${escape(appName)}"` : `open`
|
|
355
|
+
break
|
|
356
|
+
}
|
|
357
|
+
case 'win32': {
|
|
358
|
+
opener = appName ? `start "" ${escape(appName)}"` : `start ""`
|
|
359
|
+
break
|
|
360
|
+
}
|
|
361
|
+
default: {
|
|
362
|
+
opener = appName ? escape(appName) : `xdg-open`
|
|
363
|
+
break
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (process.env.SUDO_USER) {
|
|
368
|
+
opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener
|
|
369
|
+
}
|
|
370
|
+
return exec(`${opener} "${escape(target)}"`, callback)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy')
|
|
4
|
+
const querystring = require('querystring');
|
|
5
|
+
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const fs = require('fs')
|
|
8
|
+
const url = require('url')
|
|
9
|
+
const exec = require('child_process').exec
|
|
10
|
+
const tryEach = require('async/tryEach')
|
|
11
|
+
const path = require('path')
|
|
12
|
+
const os = require('os')
|
|
13
|
+
const needle = require('needle')
|
|
14
|
+
const chalk = require('ansis')
|
|
15
|
+
const cst = require('../../../../constants.js')
|
|
16
|
+
const promptly = require('promptly')
|
|
17
|
+
|
|
18
|
+
module.exports = class CliStrategy extends AuthStrategy {
|
|
19
|
+
// the client will try to call this but we handle this part ourselves
|
|
20
|
+
retrieveTokens (km, cb) {
|
|
21
|
+
this.authenticated = false
|
|
22
|
+
this.callback = cb
|
|
23
|
+
this.km = km
|
|
24
|
+
this.BASE_URI = 'https://id.keymetrics.io';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// so the cli know if we need to tell user to login/register
|
|
28
|
+
isAuthenticated () {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
if (this.authenticated) return resolve(true)
|
|
31
|
+
|
|
32
|
+
let tokensPath = cst.PM2_IO_ACCESS_TOKEN
|
|
33
|
+
fs.readFile(tokensPath, (err, tokens) => {
|
|
34
|
+
if (err && err.code === 'ENOENT') return resolve(false)
|
|
35
|
+
if (err) return reject(err)
|
|
36
|
+
|
|
37
|
+
// verify that the token is valid
|
|
38
|
+
try {
|
|
39
|
+
tokens = JSON.parse(tokens || '{}')
|
|
40
|
+
} catch (err) {
|
|
41
|
+
fs.unlinkSync(tokensPath)
|
|
42
|
+
return resolve(false)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// if the refresh tokens is here, the user could be automatically authenticated
|
|
46
|
+
return resolve(typeof tokens.refresh_token === 'string')
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
verifyToken (refresh) {
|
|
52
|
+
return this.km.auth.retrieveToken({
|
|
53
|
+
client_id: this.client_id,
|
|
54
|
+
refresh_token: refresh
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// called when we are sure the user asked to be logged in
|
|
59
|
+
_retrieveTokens (optionalCallback) {
|
|
60
|
+
const km = this.km
|
|
61
|
+
const cb = this.callback
|
|
62
|
+
|
|
63
|
+
tryEach([
|
|
64
|
+
// try to find the token via the environment
|
|
65
|
+
(next) => {
|
|
66
|
+
if (!process.env.PM2_IO_TOKEN) {
|
|
67
|
+
return next(new Error('No token in env'))
|
|
68
|
+
}
|
|
69
|
+
this.verifyToken(process.env.PM2_IO_TOKEN)
|
|
70
|
+
.then((res) => {
|
|
71
|
+
return next(null, res.data)
|
|
72
|
+
}).catch(next)
|
|
73
|
+
},
|
|
74
|
+
// try to find it in the file system
|
|
75
|
+
(next) => {
|
|
76
|
+
fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => {
|
|
77
|
+
if (err) return next(err)
|
|
78
|
+
// verify that the token is valid
|
|
79
|
+
tokens = JSON.parse(tokens || '{}')
|
|
80
|
+
if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) {
|
|
81
|
+
return next(null, tokens)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.verifyToken(tokens.refresh_token)
|
|
85
|
+
.then((res) => {
|
|
86
|
+
return next(null, res.data)
|
|
87
|
+
}).catch(next)
|
|
88
|
+
})
|
|
89
|
+
},
|
|
90
|
+
// otherwise make the whole flow
|
|
91
|
+
(next) => {
|
|
92
|
+
return this.authenticate((err, data) => {
|
|
93
|
+
if (err instanceof Error) return next(err)
|
|
94
|
+
// verify that the token is valid
|
|
95
|
+
this.verifyToken(data.refresh_token)
|
|
96
|
+
.then((res) => {
|
|
97
|
+
return next(null, res.data)
|
|
98
|
+
}).catch(next)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
], (err, result) => {
|
|
102
|
+
// if present run the optional callback
|
|
103
|
+
if (typeof optionalCallback === 'function') {
|
|
104
|
+
optionalCallback(err, result)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (result.refresh_token) {
|
|
108
|
+
this.authenticated = true
|
|
109
|
+
let file = cst.PM2_IO_ACCESS_TOKEN
|
|
110
|
+
fs.writeFile(file, JSON.stringify(result), () => {
|
|
111
|
+
return cb(err, result)
|
|
112
|
+
})
|
|
113
|
+
} else {
|
|
114
|
+
return cb(err, result)
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
authenticate (cb) {
|
|
120
|
+
console.log(`${cst.PM2_IO_MSG} Using non-browser authentication.`)
|
|
121
|
+
promptly.confirm(`${cst.PM2_IO_MSG} Do you have a pm2.io account? (y/n)`, (err, answer) => {
|
|
122
|
+
// Either login or register
|
|
123
|
+
return answer === true ? this.login(cb) : this.register(cb)
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
login (cb) {
|
|
128
|
+
let retry = () => {
|
|
129
|
+
promptly.prompt(`${cst.PM2_IO_MSG} Your username or email: `, (err, username) => {
|
|
130
|
+
if (err) return retry();
|
|
131
|
+
|
|
132
|
+
promptly.password(`${cst.PM2_IO_MSG} Your password: `, { replace : '*' }, (err, password) => {
|
|
133
|
+
if (err) return retry();
|
|
134
|
+
|
|
135
|
+
console.log(`${cst.PM2_IO_MSG} Authenticating ...`)
|
|
136
|
+
this._loginUser({
|
|
137
|
+
username: username,
|
|
138
|
+
password: password
|
|
139
|
+
}, (err, data) => {
|
|
140
|
+
if (err) {
|
|
141
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Failed to authenticate: ${err.message}`)
|
|
142
|
+
return retry()
|
|
143
|
+
}
|
|
144
|
+
return cb(null, data)
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
retry()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
register (cb) {
|
|
154
|
+
console.log(`${cst.PM2_IO_MSG} No problem ! We just need few informations to create your account`)
|
|
155
|
+
|
|
156
|
+
var retry = () => {
|
|
157
|
+
promptly.prompt(`${cst.PM2_IO_MSG} Please choose an username :`, {
|
|
158
|
+
validator : this._validateUsername,
|
|
159
|
+
retry : true
|
|
160
|
+
}, (err, username) => {
|
|
161
|
+
promptly.prompt(`${cst.PM2_IO_MSG} Please choose an email :`, {
|
|
162
|
+
validator : this._validateEmail,
|
|
163
|
+
retry : true
|
|
164
|
+
},(err, email) => {
|
|
165
|
+
promptly.password(`${cst.PM2_IO_MSG} Please choose a password :`, { replace : '*' }, (err, password) => {
|
|
166
|
+
promptly.confirm(`${cst.PM2_IO_MSG} Do you accept the terms and privacy policy (https://pm2.io/legals/terms_conditions.pdf) ? (y/n)`, (err, answer) => {
|
|
167
|
+
if (err) {
|
|
168
|
+
console.error(chalk.bold.red(err));
|
|
169
|
+
return retry()
|
|
170
|
+
} else if (answer === false) {
|
|
171
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You must accept the terms and privacy policy to contiue.`)
|
|
172
|
+
return retry()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this._registerUser({
|
|
176
|
+
email : email,
|
|
177
|
+
password : password,
|
|
178
|
+
username : username
|
|
179
|
+
}, (err, data) => {
|
|
180
|
+
console.log('\n')
|
|
181
|
+
if (err) {
|
|
182
|
+
console.error(`${cst.PM2_IO_MSG_ERR} Unexpect error: ${err.message}`)
|
|
183
|
+
console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
|
|
184
|
+
return process.exit(1)
|
|
185
|
+
}
|
|
186
|
+
return cb(undefined, data)
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
retry()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Register function
|
|
198
|
+
* @param opts.username
|
|
199
|
+
* @param opts.password
|
|
200
|
+
* @param opts.email
|
|
201
|
+
*/
|
|
202
|
+
_registerUser (opts, cb) {
|
|
203
|
+
const data = Object.assign(opts, {
|
|
204
|
+
password_confirmation: opts.password,
|
|
205
|
+
accept_terms: true
|
|
206
|
+
})
|
|
207
|
+
needle.post(this.BASE_URI + '/api/oauth/register', data, {
|
|
208
|
+
json: true,
|
|
209
|
+
headers: {
|
|
210
|
+
'X-Register-Provider': 'pm2-register',
|
|
211
|
+
'x-client-id': this.client_id
|
|
212
|
+
}
|
|
213
|
+
}, function (err, res, body) {
|
|
214
|
+
if (err) return cb(err)
|
|
215
|
+
if (body.email && body.email.message) return cb(new Error(body.email.message))
|
|
216
|
+
if (body.username && body.username.message) return cb(new Error(body.username.message))
|
|
217
|
+
if (!body.access_token) return cb(new Error(body.msg))
|
|
218
|
+
|
|
219
|
+
return cb(null, {
|
|
220
|
+
refresh_token : body.refresh_token.token,
|
|
221
|
+
access_token : body.access_token.token
|
|
222
|
+
})
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
_loginUser (user_info, cb) {
|
|
227
|
+
const URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' +
|
|
228
|
+
this.client_id + '&redirect_uri=http://localhost:43532';
|
|
229
|
+
|
|
230
|
+
needle.get(this.BASE_URI + URL_AUTH, (err, res) => {
|
|
231
|
+
if (err) return cb(err);
|
|
232
|
+
|
|
233
|
+
var cookie = res.cookies;
|
|
234
|
+
|
|
235
|
+
needle.post(this.BASE_URI + '/api/oauth/login', user_info, {
|
|
236
|
+
cookies : cookie
|
|
237
|
+
}, (err, resp, body) => {
|
|
238
|
+
if (err) return cb(err)
|
|
239
|
+
if (resp.statusCode != 200) return cb('Wrong credentials')
|
|
240
|
+
|
|
241
|
+
var location = resp.headers['x-redirect']
|
|
242
|
+
|
|
243
|
+
needle.get(this.BASE_URI + location, {
|
|
244
|
+
cookies : cookie
|
|
245
|
+
}, (err, res) => {
|
|
246
|
+
if (err) return cb(err);
|
|
247
|
+
var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token;
|
|
248
|
+
needle.post(this.BASE_URI + '/api/oauth/token', {
|
|
249
|
+
client_id : this.client_id,
|
|
250
|
+
grant_type : 'refresh_token',
|
|
251
|
+
refresh_token : refresh_token,
|
|
252
|
+
scope : 'all'
|
|
253
|
+
}, (err, res, body) => {
|
|
254
|
+
if (err) return cb(err)
|
|
255
|
+
return cb(null, body)
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_validateEmail (email) {
|
|
263
|
+
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
264
|
+
if (re.test(email) == false)
|
|
265
|
+
throw new Error('Not an email');
|
|
266
|
+
return email;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_validateUsername (value) {
|
|
270
|
+
if (value.length < 6) {
|
|
271
|
+
throw new Error('Min length of 6');
|
|
272
|
+
}
|
|
273
|
+
return value;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
deleteTokens (km) {
|
|
277
|
+
return new Promise((resolve, reject) => {
|
|
278
|
+
// revoke the refreshToken
|
|
279
|
+
km.auth.revoke()
|
|
280
|
+
.then(res => {
|
|
281
|
+
// remove the token from the filesystem
|
|
282
|
+
let file = cst.PM2_IO_ACCESS_TOKEN
|
|
283
|
+
fs.unlinkSync(file)
|
|
284
|
+
return resolve(res)
|
|
285
|
+
}).catch(reject)
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
}
|