@devmn/cloud-cli
Version:
CLI tool for Intelligo Cloud.
334 lines (314 loc) • 12.7 kB
text/typescript
import Constants from '../utils/Constants'
import StdOutUtil from '../utils/StdOutUtil'
import Utils from '../utils/Utils'
import CliHelper from '../utils/CliHelper'
import StorageHelper from '../utils/StorageHelper'
import ErrorFactory from '../utils/ErrorFactory'
import SpinnerHelper from '../utils/SpinnerHelper'
import {
getErrorForIP,
getErrorForPassword,
getErrorForEmail,
getErrorForMachineName
} from '../utils/ValidationsHandler'
import { IMachine } from '../models/storage/StoredObjects'
import CliApiManager from '../api/CliApiManager'
import Command, {
IParams,
IOption,
ICommandLineOptions,
IParam,
ParamType
} from './Command'
const K = Utils.extendCommonKeys({
ip: 'caproverIP',
root: 'caproverRootDomain',
newPwd: 'newPassword',
newPwdCheck: 'newPasswordCheck',
email: 'certificateEmail'
})
export default class ServerSetup extends Command {
protected command = 'serversetup'
protected aliases = ['setup']
protected description =
'Performs necessary actions to prepare CapRover on your server.'
private machine: IMachine = { authToken: '', baseUrl: '', name: '' }
private ip: string
private password: string = Constants.DEFAULT_PASSWORD
protected options = (params?: IParams): IOption[] => [
this.getDefaultConfigFileOption(() => this.preQuestions(params!)),
{
name: 'assumeYes',
char: 'y',
type: 'confirm',
message: () =>
(params ? 'have you' : 'assume you have') +
' already started CapRover container on your server' +
(params ? '?' : ''), // Use function to not append ':' on question message generation
default: params && true,
when: !this.configFileProvided,
preProcessParam: (param?: IParam) => {
if (param && !param.value) {
StdOutUtil.printError(
'\nCannot setup CapRover if container is not started!\n'
)
StdOutUtil.printWarning(
'Start it by running the following line:'
)
StdOutUtil.printMessage(
'docker run -p 80:80 -p 443:443 -p 3000:3000 -v /var/run/docker.sock:/var/run/docker.sock -v /captain:/captain caprover/caprover'
)
StdOutUtil.printMessage(
'\nPlease read tutorial on CapRover.com to learn how to install CapRover on a server.\n',
true
)
}
}
},
{
name: K.ip,
char: 'i',
env: 'CAPROVER_IP',
aliases: [{ name: 'ipAddress', hide: true }],
type: 'input',
message: 'IP address of your server',
default: params && Constants.SAMPLE_IP,
filter: (ip: string) => ip.trim(),
validate: (ip: string) => getErrorForIP(ip),
preProcessParam: async (param: IParam) => {
this.ip = param.value
if (!this.findParamValue(params, K.pwd)) {
// No password provided: try default password
this.machine.authToken = await this.getAuthTokenFromIp(true)
}
}
},
{
name: K.pwd,
char: 'p',
env: 'CAPROVER_PASSWORD',
aliases: [{ name: 'currentPassword', hide: true }],
type: 'password',
message: 'current CapRover password',
when: () => !this.machine.authToken, // The default password didn't work
validate: (password: string) => getErrorForPassword(password),
preProcessParam: async (param?: IParam) => {
if (param) {
// Password provided
this.password = param.value
this.machine.authToken = await this.getAuthTokenFromIp()
}
}
},
{
name: K.root,
char: 'r',
env: 'CAPROVER_ROOT_DOMAIN',
aliases: [{ name: 'rootDomain', hide: true }],
type: 'input',
message: 'CapRover server root domain',
when: () => this.checkFreshInstallation(), // Server not already setupped
filter: (domain: string) => Utils.cleanDomain(domain),
validate: (domain: string) =>
domain
? true
: // tslint:disable-next-line: max-line-length
'Please enter a valid root domain, for example use "test.yourdomain.com" if you setup your DNS to point "*.test.yourdomain.com" to the ip address of your server.',
preProcessParam: async (param: IParam) =>
await this.updateRootDomain(param.value)
},
{
name: K.newPwd,
char: 'w',
env: 'CAPROVER_NEW_PASSWORD',
type: 'password',
message: `new CapRover password (min ${Constants.MIN_CHARS_FOR_PASSWORD} characters)`,
when: () => this.password === Constants.DEFAULT_PASSWORD,
validate: (password: string) =>
getErrorForPassword(password, Constants.MIN_CHARS_FOR_PASSWORD)
},
{
name: K.newPwdCheck,
type: 'password',
message: 'enter new CapRover password again',
hide: true,
when: () => this.paramFrom(params, K.newPwd) === ParamType.Question,
validate: (password: string) =>
getErrorForPassword(
password,
this.paramValue<string>(params, K.newPwd)
)
},
{
name: K.email,
char: 'e',
env: 'CAPROVER_CERTIFICATE_EMAIL',
aliases: [{ name: 'emailForHttps', hide: true }],
type: 'input',
message:
'"valid" email address to get certificate and enable HTTPS',
filter: (email: string) => email.trim(),
validate: (email: string) => getErrorForEmail(email),
preProcessParam: (param: IParam) =>
this.enableSslAndChangePassword(
param.value,
this.paramValue(params, K.newPwd)
)
},
{
name: K.name,
char: 'n',
env: 'CAPROVER_NAME',
aliases: [{ name: 'machineName', hide: true }],
type: 'input',
message:
'CapRover machine name, with whom the login credentials are stored locally',
default: params && CliHelper.get().findDefaultCaptainName(),
filter: (name: string) => name.trim(),
validate: (name: string) => getErrorForMachineName(name),
preProcessParam: (param?: IParam) =>
param && (this.machine.name = param.value)
}
]
protected async preAction(
cmdLineoptions: ICommandLineOptions
): Promise<ICommandLineOptions> {
StdOutUtil.printMessage('Setup CapRover machine on your server...\n')
return Promise.resolve(cmdLineoptions)
}
protected preQuestions(params: IParams) {
if (this.findParamValue(params, K.name)) {
const err = getErrorForMachineName(
this.findParamValue(params, K.name)!.value
)
if (err !== true) {
StdOutUtil.printError(`${err || 'Error!'}\n`, true)
}
}
}
private async getAuthTokenFromIp(firstTry?: boolean): Promise<string> {
try {
return await CliApiManager.get({
authToken: '',
baseUrl: `http://${this.ip}:${Constants.SETUP_PORT}`,
name: ''
}).getAuthToken(this.password)
} catch (e) {
if (
firstTry &&
e.captainStatus === ErrorFactory.STATUS_WRONG_PASSWORD
) {
return ''
}
if ((e + '').indexOf('Found. Redirecting to https://') >= 0) {
StdOutUtil.printWarning(
'\nYou may have already setup the server! Use caprover login to log into an existing server.'
)
} else {
StdOutUtil.printWarning(
'\nYou may have specified a wrong IP address or not already started CapRover container on your server!'
)
}
StdOutUtil.errorHandler(e)
return ''
}
}
private async checkFreshInstallation(): Promise<boolean> {
try {
const rootDomain: string = (
await CliApiManager.get({
authToken: this.machine.authToken,
baseUrl: `http://${this.ip}:${Constants.SETUP_PORT}`,
name: ''
}).getCaptainInfo()
).rootDomain
if (rootDomain) {
StdOutUtil.printWarning(
`\nYou may have already setup the server with root domain: ${rootDomain}! Use caprover login to log into an existing server.`,
true
)
}
} catch (e) {
StdOutUtil.errorHandler(e)
}
return true
}
private async updateRootDomain(rootDomain: string) {
try {
await CliApiManager.get({
authToken: this.machine.authToken,
baseUrl: `http://${this.ip}:${Constants.SETUP_PORT}`,
name: ''
}).updateRootDomain(rootDomain)
this.machine.baseUrl = `http://${Constants.ADMIN_DOMAIN}.${rootDomain}`
} catch (e) {
if (e.captainStatus === ErrorFactory.VERIFICATION_FAILED) {
StdOutUtil.printError(
`\nCannot verify that ${StdOutUtil.getColoredMachineUrl(
rootDomain
)} points to your server IP.`
)
StdOutUtil.printError(
`Are you sure that you setup your DNS to point "*.${rootDomain}" to ${this.ip}?`
)
StdOutUtil.printError(
`Double check your DNS, if everything looks correct note that DNS changes take up to 24 hours to work properly. Check with your Domain Provider.`
)
}
StdOutUtil.errorHandler(e)
}
}
private async enableSslAndChangePassword(
email: string,
newPassword?: string
) {
let forcedSsl = false
try {
SpinnerHelper.start('Enabling SSL... Takes a few seconds...')
await CliApiManager.get(this.machine).enableRootSsl(email)
this.machine.baseUrl = Utils.cleanAdminDomainUrl(
this.machine.baseUrl,
true
)!
await CliApiManager.get(this.machine).forceSsl(true)
forcedSsl = true
if (newPassword !== undefined) {
await CliApiManager.get(this.machine).changePass(
this.password,
newPassword
)
this.password = newPassword
await CliApiManager.get(this.machine).getAuthToken(
this.password
)
}
SpinnerHelper.stop()
} catch (e) {
if (forcedSsl) {
StdOutUtil.printError(
'\nServer is setup, but password was not changed due to an error. You cannot use serversetup again.'
)
StdOutUtil.printError(
`Instead, go to ${StdOutUtil.getColoredMachineUrl(
this.machine.baseUrl
)} and change your password on settings page.`
)
StdOutUtil.printError(
`Then use <caprover login> command to connect to your server.`
)
}
SpinnerHelper.fail()
StdOutUtil.errorHandler(e)
}
}
protected async action(params: IParams): Promise<void> {
StorageHelper.get().saveMachine(this.machine)
StdOutUtil.printGreenMessage(
`CapRover server setup completed: it is available as ${StdOutUtil.getColoredMachine(
this.machine
)}\n`
)
StdOutUtil.printMessage('For more details and docs see CapRover.com\n')
}
}