123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- 'use strict'
- var cst = require('../../../constants.js');
- const chalk = require('chalk');
- const path = require('path');
- const fs = require('fs');
- const Table = require('cli-tableau');
- const pkg = require('../../../package.json')
- const IOAPI = require('@pm2/js-api')
- const promptly = require('promptly')
- var CLIStrategy = require('./auth-strategies/CliAuth')
- var WebStrategy = require('./auth-strategies/WebAuth')
- const exec = require('child_process').exec
- const OAUTH_CLIENT_ID_WEB = '138558311'
- const OAUTH_CLIENT_ID_CLI = '0943857435'
- module.exports = class PM2ioHandler {
- static usePM2Client (instance) {
- this.pm2 = instance
- }
- static strategy () {
- switch (process.platform) {
- case 'darwin': {
- return new WebStrategy({
- client_id: OAUTH_CLIENT_ID_WEB
- })
- }
- case 'win32': {
- return new WebStrategy({
- client_id: OAUTH_CLIENT_ID_WEB
- })
- }
- case 'linux': {
- const isDesktop = process.env.XDG_CURRENT_DESKTOP || process.env.XDG_SESSION_DESKTOP || process.env.DISPLAY
- const isSSH = process.env.SSH_TTY || process.env.SSH_CONNECTION
- if (isDesktop && !isSSH) {
- return new WebStrategy({
- client_id: OAUTH_CLIENT_ID_WEB
- })
- } else {
- return new CLIStrategy({
- client_id: OAUTH_CLIENT_ID_CLI
- })
- }
- }
- default: {
- return new CLIStrategy({
- client_id: OAUTH_CLIENT_ID_CLI
- })
- }
- }
- }
- static init () {
- this._strategy = this.strategy()
- /**
- * If you are using a local backend you should give those options :
- * {
- * services: {
- * API: 'http://localhost:3000',
- * OAUTH: 'http://localhost:3100'
- * }
- * }
- */
- this.io = new IOAPI().use(this._strategy)
- }
- static launch (command, opts) {
- // first init the strategy and the io client
- this.init()
- switch (command) {
- case 'connect' :
- case 'login' :
- case 'register' :
- case undefined :
- case 'authenticate' : {
- this.authenticate()
- break
- }
- case 'validate' : {
- this.validateAccount(opts)
- break
- }
- case 'help' :
- case 'welcome': {
- var dt = fs.readFileSync(path.join(__dirname, './pres/welcome'));
- console.log(dt.toString());
- return process.exit(0)
- }
- case 'logout': {
- this._strategy.isAuthenticated().then(isConnected => {
- // try to kill the agent anyway
- this.pm2.killAgent(err => {})
- if (isConnected === false) {
- console.log(`${cst.PM2_IO_MSG} Already disconnected`)
- return process.exit(0)
- }
- this._strategy._retrieveTokens((err, tokens) => {
- if (err) {
- console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)
- return process.exit(0)
- }
- this._strategy.deleteTokens(this.io).then(_ => {
- console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)
- return process.exit(0)
- }).catch(err => {
- console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error: ${err.message}`)
- return process.exit(1)
- })
- })
- }).catch(err => {
- console.error(`${cst.PM2_IO_MSG_ERR} Failed to logout: ${err.message}`)
- console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
- })
- break
- }
- case 'create': {
- this._strategy.isAuthenticated().then(res => {
- // if the user isn't authenticated, we make them do the whole flow
- if (res !== true) {
- this.authenticate()
- } else {
- this.createBucket(this.createBucketHandler.bind(this))
- }
- }).catch(err => {
- console.error(`${cst.PM2_IO_MSG_ERR} Failed to create to the bucket: ${err.message}`)
- console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
- })
- break
- }
- case 'web': {
- this._strategy.isAuthenticated().then(res => {
- // if the user isn't authenticated, we make them do the whole flow
- if (res === false) {
- console.error(`${cst.PM2_IO_MSG_ERR} You need to be authenticated to do that, please use: pm2 plus login`)
- return process.exit(1)
- }
- this._strategy._retrieveTokens(() => {
- return this.openUI()
- })
- }).catch(err => {
- console.error(`${cst.PM2_IO_MSG_ERR} Failed to open the UI: ${err.message}`)
- console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
- })
- break
- }
- default : {
- console.log(`${cst.PM2_IO_MSG_ERR} Invalid command ${command}, available : login,register,validate,connect or web`)
- process.exit(1)
- }
- }
- }
- static openUI () {
- this.io.bucket.retrieveAll().then(res => {
- const buckets = res.data
- if (buckets.length === 0) {
- return this.createBucket((err, bucket) => {
- if (err) {
- console.error(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)
- if (bucket) {
- console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)
- }
- console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
- return process.exit(0)
- }
- const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
- console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', targetURL)
- this.open(targetURL)
- return process.exit(0)
- })
- }
- var table = new Table({
- style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},
- head : ['Bucket name', 'Plan type']
- })
- buckets.forEach(function(bucket) {
- table.push([bucket.name, bucket.credits.offer_type])
- })
- console.log(table.toString())
- console.log(`${cst.PM2_IO_MSG} If you don't want to open the UI to a bucket, type 'none'`)
- const choices = buckets.map(bucket => bucket.name)
- choices.push('none')
- promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {
- if (value === 'none') process.exit(0)
- const bucket = buckets.find(bucket => bucket.name === value)
- if (bucket === undefined) return process.exit(0)
- const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
- console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\n', ' ', targetURL)
- this.open(targetURL)
- return process.exit(0)
- })
- })
- }
- static validateAccount (token) {
- this.io.auth.validEmail(token)
- .then(res => {
- console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)
- console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)
- return process.exit(0)
- }).catch(err => {
- if (err.status === 401) {
- console.error(`${cst.PM2_IO_MSG_ERR} Invalid token`)
- return process.exit(1)
- } else if (err.status === 301) {
- console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)
- console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)
- return process.exit(0)
- }
- const msg = err.data ? err.data.error_description || err.data.msg : err.message
- console.error(`${cst.PM2_IO_MSG_ERR} Failed to validate your email: ${msg}`)
- console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
- return process.exit(1)
- })
- }
- static createBucketHandler (err, bucket) {
- if (err) {
- console.trace(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)
- if (bucket) {
- console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)
- }
- console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)
- return process.exit(0)
- }
- if (bucket === undefined) {
- return process.exit(0)
- }
- console.log(`${cst.PM2_IO_MSG} Successfully connected to bucket ${bucket.name}`)
- var targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`
- console.log(`${cst.PM2_IO_MSG} You can use the web interface over there: ${targetURL}`)
- this.open(targetURL)
- return process.exit(0)
- }
- static createBucket (cb) {
- console.log(`${cst.PM2_IO_MSG} By default we allow you to trial PM2 Plus for 14 days without any credit card.`)
- this.io.bucket.create({
- name: 'PM2 Plus Monitoring'
- }).then(res => {
- const bucket = res.data.bucket
- console.log(`${cst.PM2_IO_MSG} Successfully created the bucket`)
- this.pm2.link({
- public_key: bucket.public_id,
- secret_key: bucket.secret_id,
- pm2_version: pkg.version
- }, (err) => {
- if (err) {
- return cb(new Error('Failed to connect your local PM2 to your bucket'), bucket)
- } else {
- return cb(null, bucket)
- }
- })
- }).catch(err => {
- return cb(new Error(`Failed to create a bucket: ${err.message}`))
- })
- }
- /**
- * Connect the local agent to a specific bucket
- * @param {Function} cb
- */
- static connectToBucket (cb) {
- this.io.bucket.retrieveAll().then(res => {
- const buckets = res.data
- if (buckets.length === 0) {
- return this.createBucket(cb)
- }
- var table = new Table({
- style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},
- head : ['Bucket name', 'Plan type']
- })
- buckets.forEach(function(bucket) {
- table.push([bucket.name, bucket.payment.offer_type])
- })
- console.log(table.toString())
- console.log(`${cst.PM2_IO_MSG} If you don't want to connect to a bucket, type 'none'`)
- const choices = buckets.map(bucket => bucket.name)
- choices.push('none')
- promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {
- if (value === 'none') return cb()
- const bucket = buckets.find(bucket => bucket.name === value)
- if (bucket === undefined) return cb()
- this.pm2.link({
- public_key: bucket.public_id,
- secret_key: bucket.secret_id,
- pm2_version: pkg.version
- }, (err) => {
- return err ? cb(err) : cb(null, bucket)
- })
- })
- })
- }
- /**
- * Authenticate the user with either of the strategy
- * @param {Function} cb
- */
- static authenticate () {
- this._strategy._retrieveTokens((err, tokens) => {
- if (err) {
- const msg = err.data ? err.data.error_description || err.data.msg : err.message
- console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error : ${msg}`)
- return process.exit(1)
- }
- console.log(`${cst.PM2_IO_MSG} Successfully authenticated`)
- this.io.user.retrieve().then(res => {
- const user = res.data
- this.io.user.retrieve().then(res => {
- const tmpUser = res.data
- console.log(`${cst.PM2_IO_MSG} Successfully validated`)
- this.connectToBucket(this.createBucketHandler.bind(this))
- })
- })
- })
- }
- static open (target, appName, callback) {
- let opener
- const escape = function (s) {
- return s.replace(/"/g, '\\"')
- }
- if (typeof (appName) === 'function') {
- callback = appName
- appName = null
- }
- switch (process.platform) {
- case 'darwin': {
- opener = appName ? `open -a "${escape(appName)}"` : `open`
- break
- }
- case 'win32': {
- opener = appName ? `start "" ${escape(appName)}"` : `start ""`
- break
- }
- default: {
- opener = appName ? escape(appName) : `xdg-open`
- break
- }
- }
- if (process.env.SUDO_USER) {
- opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener
- }
- return exec(`${opener} "${escape(target)}"`, callback)
- }
- }
|