mirror of
https://github.com/caprover/caprover
synced 2026-08-25 00:46:38 +00:00
Merge branch 'master' into feature/v1.0
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
package.json
|
||||
package-lock.json
|
||||
node_modules/
|
||||
coverage/
|
||||
dist/
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
|
||||
Executable
+277
@@ -0,0 +1,277 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const HttpClient_1 = require("./HttpClient");
|
||||
class ApiManager {
|
||||
constructor(baseUrl, authTokenSaver) {
|
||||
this.authTokenSaver = authTokenSaver;
|
||||
const self = this;
|
||||
this.http = new HttpClient_1.default(baseUrl, ApiManager.authToken, function () {
|
||||
return self.getAuthToken(ApiManager.lastKnownPassword);
|
||||
});
|
||||
}
|
||||
destroy() {
|
||||
this.http.destroy();
|
||||
}
|
||||
setAuthToken(authToken) {
|
||||
ApiManager.authToken = authToken;
|
||||
this.http.setAuthToken(authToken);
|
||||
}
|
||||
static isLoggedIn() {
|
||||
return !!ApiManager.authToken;
|
||||
}
|
||||
getAuthToken(password) {
|
||||
const http = this.http;
|
||||
ApiManager.lastKnownPassword = password;
|
||||
let authTokenFetched = '';
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/login', { password }))
|
||||
.then(function (data) {
|
||||
authTokenFetched = data.token;
|
||||
self.setAuthToken(authTokenFetched);
|
||||
return authTokenFetched;
|
||||
})
|
||||
.then(self.authTokenSaver)
|
||||
.then(function () {
|
||||
return authTokenFetched;
|
||||
});
|
||||
}
|
||||
getCaptainInfo() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/info', {}));
|
||||
}
|
||||
updateRootDomain(rootDomain) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/changerootdomain', { rootDomain }));
|
||||
}
|
||||
enableRootSsl(emailAddress) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/enablessl', { emailAddress }));
|
||||
}
|
||||
forceSsl(isEnabled) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/forcessl', { isEnabled }));
|
||||
}
|
||||
getAllApps() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/appDefinitions', {})); // TODO user/apps/appDefinitions
|
||||
}
|
||||
fetchBuildLogs(appName) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/appData/' + appName, {})); // TODO user/apps/appData
|
||||
}
|
||||
uploadAppData(appName, file) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST_DATA, '/user/appData/' + appName + '?detached=1', { sourceFile: file })); // TODO user/apps/appData
|
||||
}
|
||||
uploadCaptainDefinitionContent(appName, captainDefinition, gitHash, detached) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appData/' + appName + (detached ? '?detached=1' : ''), {
|
||||
captainDefinitionContent: JSON.stringify(captainDefinition),
|
||||
gitHash
|
||||
}));
|
||||
}
|
||||
updateConfigAndSave(appName, appDefinition) {
|
||||
var instanceCount = appDefinition.instanceCount;
|
||||
var envVars = appDefinition.envVars;
|
||||
var notExposeAsWebApp = appDefinition.notExposeAsWebApp;
|
||||
var forceSsl = appDefinition.forceSsl;
|
||||
var volumes = appDefinition.volumes;
|
||||
var ports = appDefinition.ports;
|
||||
var nodeId = appDefinition.nodeId;
|
||||
var appPushWebhook = appDefinition.appPushWebhook;
|
||||
var customNginxConfig = appDefinition.customNginxConfig;
|
||||
var preDeployFunction = appDefinition.preDeployFunction;
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/update', {
|
||||
appName: appName,
|
||||
instanceCount: instanceCount,
|
||||
notExposeAsWebApp: notExposeAsWebApp,
|
||||
forceSsl: forceSsl,
|
||||
volumes: volumes,
|
||||
ports: ports,
|
||||
customNginxConfig: customNginxConfig,
|
||||
appPushWebhook: appPushWebhook,
|
||||
nodeId: nodeId,
|
||||
preDeployFunction: preDeployFunction,
|
||||
envVars: envVars
|
||||
}));
|
||||
}
|
||||
registerNewApp(appName, hasPersistentData) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/register', {
|
||||
appName,
|
||||
hasPersistentData
|
||||
}));
|
||||
}
|
||||
deleteApp(appName) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/delete', {
|
||||
appName
|
||||
}));
|
||||
}
|
||||
enableSslForBaseDomain(appName) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/enablebasedomainssl', {
|
||||
appName
|
||||
}));
|
||||
}
|
||||
attachNewCustomDomainToApp(appName, customDomain) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/customdomain', {
|
||||
appName,
|
||||
customDomain
|
||||
}));
|
||||
}
|
||||
enableSslForCustomDomain(appName, customDomain) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/enablecustomdomainssl', {
|
||||
appName,
|
||||
customDomain
|
||||
}));
|
||||
}
|
||||
removeCustomDomain(appName, customDomain) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/removecustomdomain', {
|
||||
appName,
|
||||
customDomain
|
||||
}));
|
||||
}
|
||||
getLoadBalancerInfo() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/loadbalancerinfo', {}));
|
||||
}
|
||||
getNetDataInfo() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/netdata', {}));
|
||||
}
|
||||
updateNetDataInfo(netDataInfo) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/netdata', { netDataInfo }));
|
||||
}
|
||||
changePass(oldPassword, newPassword) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/changepassword', {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}));
|
||||
}
|
||||
getVersionInfo() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/versioninfo', {}));
|
||||
}
|
||||
performUpdate(latestVersion) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/versioninfo', { latestVersion }));
|
||||
}
|
||||
getNginxConfig() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/nginxconfig', {}));
|
||||
}
|
||||
setNginxConfig(customBase, customCaptain) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/nginxconfig', {
|
||||
baseConfig: { customValue: customBase },
|
||||
captainConfig: { customValue: customCaptain }
|
||||
}));
|
||||
}
|
||||
getUnusedImages(mostRecentLimit) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/apps/appDefinitions/unusedImages', {
|
||||
mostRecentLimit: mostRecentLimit + ''
|
||||
}));
|
||||
}
|
||||
deleteImages(imageIds) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/apps/appDefinitions/deleteImages', {
|
||||
imageIds
|
||||
}));
|
||||
}
|
||||
getDockerRegistries() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/registries', {}));
|
||||
}
|
||||
enableSelfHostedDockerRegistry() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/selfhostregistry/enableregistry', {}));
|
||||
}
|
||||
disableSelfHostedDockerRegistry() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/selfhostregistry/disableregistry', {}));
|
||||
}
|
||||
addDockerRegistry(dockerRegistry) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/registries/insert', Object.assign({}, dockerRegistry)));
|
||||
}
|
||||
updateDockerRegistry(dockerRegistry) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/registries/update', Object.assign({}, dockerRegistry)));
|
||||
}
|
||||
deleteDockerRegistry(registryId) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/registries/delete', {
|
||||
registryId
|
||||
}));
|
||||
}
|
||||
setDefaultPushDockerRegistry(registryId) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/registries/setpush', {
|
||||
registryId
|
||||
}));
|
||||
}
|
||||
getAllNodes() {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/nodes', {}));
|
||||
}
|
||||
addDockerNode(nodeType, privateKey, remoteNodeIpAddress, captainIpAddress) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/nodes', {
|
||||
nodeType,
|
||||
privateKey,
|
||||
remoteNodeIpAddress,
|
||||
captainIpAddress
|
||||
}));
|
||||
}
|
||||
}
|
||||
ApiManager.lastKnownPassword = process.env.REACT_APP_DEFAULT_PASSWORD
|
||||
? process.env.REACT_APP_DEFAULT_PASSWORD + ''
|
||||
: 'captain42';
|
||||
ApiManager.authToken = !!process.env.REACT_APP_IS_DEBUG
|
||||
? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Im5hbWVzcGFjZSI6ImNhcHRhaW4iLCJ0b2tlblZlcnNpb24iOiI5NmRjM2U1MC00ZDk3LTRkNmItYTIzMS04MmNiZjY0ZTA2NTYifSwiaWF0IjoxNTQ1OTg0MDQwLCJleHAiOjE1ODE5ODQwNDB9.uGJyhb2JYsdw9toyMKX28bLVuB0PhnS2POwEjKpchww'
|
||||
: '';
|
||||
exports.default = ApiManager;
|
||||
//# sourceMappingURL=ApiManager.js.map
|
||||
Executable
+1
File diff suppressed because one or more lines are too long
Executable
+32
@@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ApiManager_1 = require("./ApiManager");
|
||||
const StorageHelper_1 = require("../utils/StorageHelper");
|
||||
function hashCode(str) {
|
||||
var hash = 0, i, chr;
|
||||
if (str.length === 0)
|
||||
return hash;
|
||||
for (i = 0; i < str.length; i++) {
|
||||
chr = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
class CliApiManager {
|
||||
static get(capMachine) {
|
||||
const hashKey = 'v' + hashCode(capMachine.baseUrl);
|
||||
if (!CliApiManager.instances[hashKey])
|
||||
CliApiManager.instances[hashKey] = new ApiManager_1.default(capMachine.baseUrl + '/api/v1', function (token) {
|
||||
capMachine.authToken = token;
|
||||
if (capMachine.name)
|
||||
StorageHelper_1.default.get().saveMachine(capMachine);
|
||||
return Promise.resolve();
|
||||
});
|
||||
CliApiManager.instances[hashKey].setAuthToken(capMachine.authToken);
|
||||
return CliApiManager.instances[hashKey];
|
||||
}
|
||||
}
|
||||
CliApiManager.instances = {};
|
||||
exports.default = CliApiManager;
|
||||
//# sourceMappingURL=CliApiManager.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CliApiManager.js","sourceRoot":"","sources":["../../src/api/CliApiManager.ts"],"names":[],"mappings":";;AAAA,6CAAsC;AAEtC,0DAAmD;AAGnD,SAAS,QAAQ,CAAC,GAAW;IAC5B,IAAI,IAAI,GAAG,CAAC,EACX,CAAC,EACD,GAAG,CAAC;IACL,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QAChC,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC;QAChC,IAAI,IAAI,CAAC,CAAC,CAAC,2BAA2B;KACtC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAqB,aAAa;IAGjC,MAAM,CAAC,GAAG,CAAC,UAAoB;QAC9B,MAAM,OAAO,GAAG,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC;YACpC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,oBAAU,CAAC,UAAU,CAAC,OAAO,GAAG,SAAS,EAAE,UAAS,KAAK;gBAC/F,UAAU,CAAC,SAAS,GAAG,KAAK,CAAC;gBAC7B,IAAI,UAAU,CAAC,IAAI;oBAAE,uBAAa,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;gBACjE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;YAC1B,CAAC,CAAC,CAAC;QAEJ,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAEpE,OAAO,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;;AAdM,uBAAS,GAAgC,EAAE,CAAC;AADpD,gCAgBC"}
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ErrorFactory_1 = require("../utils/ErrorFactory");
|
||||
const Logger_1 = require("../utils/Logger");
|
||||
const Request = require("request-promise");
|
||||
var TOKEN_HEADER = 'x-captain-auth';
|
||||
var NAMESPACE = 'x-namespace';
|
||||
var CAPTAIN = 'captain';
|
||||
class HttpClient {
|
||||
constructor(baseUrl, authToken, onAuthFailure) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.authToken = authToken;
|
||||
this.onAuthFailure = onAuthFailure;
|
||||
this.GET = 'GET';
|
||||
this.POST = 'POST';
|
||||
this.POST_DATA = 'POST_DATA';
|
||||
this.isDestroyed = false;
|
||||
//
|
||||
}
|
||||
createHeaders() {
|
||||
let headers = {};
|
||||
if (this.authToken)
|
||||
headers[TOKEN_HEADER] = this.authToken;
|
||||
headers[NAMESPACE] = CAPTAIN;
|
||||
// check user/appData or apiManager.uploadAppData before changing this signature.
|
||||
return headers;
|
||||
}
|
||||
setAuthToken(authToken) {
|
||||
this.authToken = authToken;
|
||||
}
|
||||
destroy() {
|
||||
this.isDestroyed = true;
|
||||
}
|
||||
fetch(method, endpoint, variables) {
|
||||
const self = this;
|
||||
return function () {
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
if (!process.env.REACT_APP_IS_DEBUG)
|
||||
return Promise.resolve();
|
||||
return new Promise(function (res) {
|
||||
setTimeout(res, 500);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return self.fetchInternal(method, endpoint, variables); //
|
||||
})
|
||||
.then(function (requestResponse) {
|
||||
const data = JSON.parse(requestResponse);
|
||||
if (data.status === ErrorFactory_1.default.STATUS_AUTH_TOKEN_INVALID) {
|
||||
return self
|
||||
.onAuthFailure() //
|
||||
.then(function () {
|
||||
return self
|
||||
.fetchInternal(method, endpoint, variables)
|
||||
.then(function (newRequestResponse) {
|
||||
return newRequestResponse;
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
return data;
|
||||
}
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data.status !== ErrorFactory_1.default.OKAY && data.status !== ErrorFactory_1.default.OKAY_BUILD_STARTED) {
|
||||
throw ErrorFactory_1.default.createError(data.status || ErrorFactory_1.default.UNKNOWN_ERROR, data.description || '');
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.then(function (data) {
|
||||
// These two blocks are clearly memory leaks! But I don't have time to fix them now... I need to CANCEL the promise, but since I don't
|
||||
// have CANCEL method on the native Promise, I return a promise that will never RETURN if the HttpClient is destroyed.
|
||||
// Will fix them later... but it shouldn't be a big deal anyways as it's only a problem when user navigates away from a page before the
|
||||
// network request returns back.
|
||||
return new Promise(function (resolve, reject) {
|
||||
// data.data here is the "data" field inside the API response! {status: 100, description: "Login succeeded", data: {…}}
|
||||
if (!self.isDestroyed)
|
||||
return resolve(data.data || { token: data.token }); // TODO remove || for API V2
|
||||
Logger_1.default.dev('Destroyed then not called');
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
// Logger.log('');
|
||||
// Logger.error(error.message || error);
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!self.isDestroyed)
|
||||
return reject(error);
|
||||
Logger_1.default.dev('Destroyed catch not called');
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
fetchInternal(method, endpoint, variables) {
|
||||
if (method === this.GET)
|
||||
return this.getReq(endpoint, variables);
|
||||
if (method === this.POST || method === this.POST_DATA)
|
||||
return this.postReq(endpoint, variables, method);
|
||||
throw new Error('Unknown method: ' + method);
|
||||
}
|
||||
getReq(endpoint, variables) {
|
||||
const self = this;
|
||||
return Request.get(this.baseUrl + endpoint, {
|
||||
headers: self.createHeaders(),
|
||||
qs: variables
|
||||
}).then(function (data) {
|
||||
return data;
|
||||
});
|
||||
}
|
||||
postReq(endpoint, variables, method) {
|
||||
const self = this;
|
||||
if (method === this.POST_DATA)
|
||||
return Request.post(this.baseUrl + endpoint, {
|
||||
headers: self.createHeaders(),
|
||||
formData: variables
|
||||
}).then(function (data) {
|
||||
return data;
|
||||
});
|
||||
return Request.post(this.baseUrl + endpoint, {
|
||||
headers: self.createHeaders(),
|
||||
form: variables
|
||||
}).then(function (data) {
|
||||
return data;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.default = HttpClient;
|
||||
//# sourceMappingURL=HttpClient.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"HttpClient.js","sourceRoot":"","sources":["../../src/api/HttpClient.ts"],"names":[],"mappings":"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"}
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const packagejson = require('../../package.json');
|
||||
const updateNotifier = require("update-notifier");
|
||||
updateNotifier({ pkg: packagejson }).notify({ isGlobal: true });
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const program = require("commander");
|
||||
// Command actions
|
||||
const login_1 = require("./login");
|
||||
const list_1 = require("./list");
|
||||
const logout_1 = require("./logout");
|
||||
const deploy_1 = require("./deploy");
|
||||
const serversetup_1 = require("./serversetup");
|
||||
// Setup
|
||||
program.version(packagejson.version).description(packagejson.description);
|
||||
// Commands
|
||||
program
|
||||
.command('login')
|
||||
.description('Login to a CaptainDuckDuck machine. You can be logged in to multiple machines simultaneously.')
|
||||
.action(() => {
|
||||
login_1.default();
|
||||
});
|
||||
program.command('list').alias('ls').description('List all Captain machines currently logged in.').action(() => {
|
||||
list_1.default();
|
||||
});
|
||||
program.command('logout').description('Logout from a specific Captain machine.').action(() => {
|
||||
logout_1.default();
|
||||
});
|
||||
program
|
||||
.command('serversetup')
|
||||
.description('Performs necessary actions and prepares your Captain server.')
|
||||
.action(() => {
|
||||
serversetup_1.default();
|
||||
});
|
||||
program
|
||||
.command('deploy')
|
||||
.description("Deploy your app (current directory) to a specific Captain machine. You'll be prompted to choose your Captain machine.\n\n" +
|
||||
'For use in scripts, i.e. non-interactive mode, you can use --host --pass --appName and -- branch flags.')
|
||||
.option('-d, --default', 'Use previously entered values for the current directory, avoid asking.')
|
||||
.option('-t, --tarFile <value>', 'Specify the tar file to be uploaded (rather than using git archive)')
|
||||
.option('-h, --host <value>', 'Specify th URL of the captain machine in command line')
|
||||
.option('-a, --appName <value>', 'Specify Name of the app to be deployed in command line')
|
||||
.option('-p, --pass <value>', 'Specify password for Captain in command line')
|
||||
.option('-b, --branch <value>', 'Specify branch name (default master)')
|
||||
.action((options) => {
|
||||
deploy_1.default(options);
|
||||
});
|
||||
// Error on unknown commands
|
||||
program.on('command:*', () => {
|
||||
const wrongCommands = program.args.join(' ');
|
||||
StdOutUtil_1.default.printError(`\nInvalid command: ${wrongCommands}\nSee --help for a list of available commands.`, true);
|
||||
});
|
||||
program.parse(process.argv);
|
||||
//# sourceMappingURL=captainduckduck.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"captainduckduck.js","sourceRoot":"","sources":["../../src/commands/captainduckduck.ts"],"names":[],"mappings":";;;AAEA,MAAM,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAClD,kDAAkD;AAClD,cAAc,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AAEhE,oDAA6C;AAC7C,qCAAqC;AAErC,kBAAkB;AAClB,mCAA4B;AAC5B,iCAA0B;AAC1B,qCAA8B;AAC9B,qCAA8B;AAC9B,+CAAwC;AAExC,QAAQ;AACR,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;AAE1E,WAAW;AAEX,OAAO;KACL,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,+FAA+F,CAAC;KAC5G,MAAM,CAAC,GAAG,EAAE;IACZ,eAAK,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEJ,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,gDAAgD,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE;IAC7G,cAAI,EAAE,CAAC;AACR,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,yCAAyC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE;IAC5F,gBAAM,EAAE,CAAC;AACV,CAAC,CAAC,CAAC;AAEH,OAAO;KACL,OAAO,CAAC,aAAa,CAAC;KACtB,WAAW,CAAC,8DAA8D,CAAC;KAC3E,MAAM,CAAC,GAAG,EAAE;IACZ,qBAAW,EAAE,CAAC;AACf,CAAC,CAAC,CAAC;AAEJ,OAAO;KACL,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CACX,2HAA2H;IAC1H,yGAAyG,CAC1G;KACA,MAAM,CAAC,eAAe,EAAE,wEAAwE,CAAC;KACjG,MAAM,CAAC,uBAAuB,EAAE,qEAAqE,CAAC;KACtG,MAAM,CAAC,oBAAoB,EAAE,uDAAuD,CAAC;KACrF,MAAM,CAAC,uBAAuB,EAAE,wDAAwD,CAAC;KACzF,MAAM,CAAC,oBAAoB,EAAE,8CAA8C,CAAC;KAC5E,MAAM,CAAC,sBAAsB,EAAE,sCAAsC,CAAC;KACtE,MAAM,CAAC,CAAC,OAAY,EAAE,EAAE;IACxB,gBAAM,CAAC,OAAO,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC;AAEJ,4BAA4B;AAC5B,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;IAC5B,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE7C,oBAAU,CAAC,UAAU,CAAC,sBAAsB,aAAa,gDAAgD,EAAE,IAAI,CAAC,CAAC;AAClH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const inquirer = require("inquirer");
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const ValidationsHandler_1 = require("../utils/ValidationsHandler");
|
||||
const StorageHelper_1 = require("../utils/StorageHelper");
|
||||
const CliHelper_1 = require("../utils/CliHelper");
|
||||
const DeployHelper_1 = require("../utils/DeployHelper");
|
||||
const CliApiManager_1 = require("../api/CliApiManager");
|
||||
function deploy(options) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const possibleApp = StorageHelper_1.default.get()
|
||||
.getDeployedDirectories()
|
||||
.find((dir) => dir.cwd === process.cwd());
|
||||
StdOutUtil_1.default.printMessage('Preparing deployment to Captain...\n');
|
||||
let deployParams = { deploySource: {} };
|
||||
if (options.default) {
|
||||
deployParams = {
|
||||
captainMachine: possibleApp ? StorageHelper_1.default.get().findMachine(possibleApp.machineNameToDeploy) : undefined,
|
||||
deploySource: possibleApp ? possibleApp.deploySource : {},
|
||||
appName: possibleApp ? possibleApp.appName : undefined
|
||||
};
|
||||
}
|
||||
else if (possibleApp) {
|
||||
StdOutUtil_1.default.printMessage(`\n\n**********\n\nProtip: You seem to have deployed ${possibleApp.appName} from this directory in the past, use --default flag to avoid having to re-enter the information.\n\n**********\n\n`);
|
||||
}
|
||||
if (options.appName) {
|
||||
deployParams.appName = options.appName;
|
||||
}
|
||||
if (options.branch) {
|
||||
deployParams.deploySource.branchToPush = options.branch;
|
||||
}
|
||||
if (options.tarFile) {
|
||||
deployParams.deploySource.tarFilePath = options.tarFile;
|
||||
}
|
||||
if (!deployParams.deploySource.tarFilePath) {
|
||||
if (!ValidationsHandler_1.validateIsGitRepository() || !ValidationsHandler_1.validateDefinitionFile()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (options.pass || options.host) {
|
||||
if (options.pass && options.host) {
|
||||
deployParams.captainMachine = {
|
||||
authToken: '',
|
||||
baseUrl: options.host,
|
||||
name: ''
|
||||
};
|
||||
yield CliApiManager_1.default.get(deployParams.captainMachine).getAuthToken(options.pass);
|
||||
}
|
||||
else {
|
||||
StdOutUtil_1.default.printError('host and pass should be either both defined or both undefined', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Show questions for what is being missing in deploy params
|
||||
let allApps = undefined;
|
||||
if (deployParams.captainMachine) {
|
||||
allApps = yield ValidationsHandler_1.ensureAuthentication(deployParams.captainMachine);
|
||||
}
|
||||
const allParametersAreSupplied = !!deployParams.appName &&
|
||||
!!deployParams.captainMachine &&
|
||||
(!!deployParams.deploySource.branchToPush || !!deployParams.deploySource.tarFilePath);
|
||||
if (!allParametersAreSupplied) {
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'captainNameToDeploy',
|
||||
default: possibleApp ? possibleApp.machineNameToDeploy : '',
|
||||
message: 'Select the Captain Machine you want to deploy to:',
|
||||
choices: CliHelper_1.default.get().getMachinesAsOptions(),
|
||||
when: () => !deployParams.captainMachine,
|
||||
filter: (capName) => __awaiter(this, void 0, void 0, function* () {
|
||||
deployParams.captainMachine = StorageHelper_1.default.get().findMachine(capName);
|
||||
if (deployParams.captainMachine)
|
||||
allApps = yield ValidationsHandler_1.ensureAuthentication(deployParams.captainMachine);
|
||||
return capName;
|
||||
})
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default: possibleApp && possibleApp.deploySource.branchToPush
|
||||
? possibleApp.deploySource.branchToPush
|
||||
: 'master',
|
||||
name: 'branchToPush',
|
||||
message: "Enter the 'git' branch you would like to deploy:",
|
||||
filter: (branchToPushEntered) => __awaiter(this, void 0, void 0, function* () {
|
||||
deployParams.deploySource.branchToPush = branchToPushEntered;
|
||||
return branchToPushEntered;
|
||||
}),
|
||||
when: (answers) => !deployParams.deploySource.branchToPush &&
|
||||
!deployParams.deploySource.tarFilePath &&
|
||||
!!deployParams.captainMachine
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
default: possibleApp ? possibleApp.appName : '',
|
||||
name: 'appName',
|
||||
message: 'Enter the Captain app name this directory will be deployed to:',
|
||||
choices: (answers) => {
|
||||
return CliHelper_1.default.get().getAppsAsOptions(allApps);
|
||||
},
|
||||
filter: (appNameEntered) => __awaiter(this, void 0, void 0, function* () {
|
||||
deployParams.appName = appNameEntered;
|
||||
return appNameEntered;
|
||||
}),
|
||||
when: (answers) => (!!deployParams.deploySource.branchToPush || !!deployParams.deploySource.tarFilePath) &&
|
||||
!deployParams.appName
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmedToDeploy',
|
||||
message: 'Note that uncommitted files and files in gitignore (if any) will not be pushed to server. \n Please confirm so that deployment process can start.',
|
||||
default: true,
|
||||
when: (answers) => !!deployParams.appName &&
|
||||
!!deployParams.captainMachine &&
|
||||
(!!deployParams.deploySource.branchToPush || !!deployParams.deploySource.tarFilePath)
|
||||
}
|
||||
];
|
||||
const answersToIgnore = (yield inquirer.prompt(questions));
|
||||
if (!answersToIgnore.confirmedToDeploy) {
|
||||
StdOutUtil_1.default.printMessage('\nOperation cancelled by the user...\n');
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
yield new DeployHelper_1.default(deployParams) //
|
||||
.startDeploy();
|
||||
}
|
||||
catch (e) {
|
||||
StdOutUtil_1.default.printError(e.message, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.default = deploy;
|
||||
//# sourceMappingURL=deploy.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const chalk_1 = require("chalk");
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const StorageHelper_1 = require("../utils/StorageHelper");
|
||||
function _displayMachine(machine) {
|
||||
console.log('>> ' + chalk_1.default.greenBright(machine.name) + ' at ' + chalk_1.default.cyan(machine.baseUrl));
|
||||
}
|
||||
function list() {
|
||||
StdOutUtil_1.default.printMessage('\nLogged in Captain Machines:\n');
|
||||
StorageHelper_1.default.get().getMachines().map((machine) => {
|
||||
_displayMachine(machine);
|
||||
});
|
||||
StdOutUtil_1.default.printMessage('');
|
||||
}
|
||||
exports.default = list;
|
||||
//# sourceMappingURL=list.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":";;;AAEA,iCAA0B;AAC1B,oDAA6C;AAC7C,0DAAmD;AAGnD,SAAS,eAAe,CAAC,OAAiB;IACzC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,eAAK,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,SAAS,IAAI;IACZ,oBAAU,CAAC,YAAY,CAAC,iCAAiC,CAAC,CAAC;IAE3D,uBAAa,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACjD,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,oBAAU,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,kBAAe,IAAI,CAAC"}
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const inquirer = require("inquirer");
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const StorageHelper_1 = require("../utils/StorageHelper");
|
||||
const Constants_1 = require("../utils/Constants");
|
||||
const Utils_1 = require("../utils/Utils");
|
||||
const CliHelper_1 = require("../utils/CliHelper");
|
||||
const CliApiManager_1 = require("../api/CliApiManager");
|
||||
const SAMPLE_DOMAIN = Constants_1.default.SAMPLE_DOMAIN;
|
||||
const cleanUpUrl = Utils_1.default.cleanUpUrl;
|
||||
function login() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
StdOutUtil_1.default.printMessage('Login to a Captain Machine');
|
||||
const questions = [
|
||||
{
|
||||
type: 'input',
|
||||
default: SAMPLE_DOMAIN,
|
||||
name: 'captainAddress',
|
||||
message: '\nEnter address of the Captain machine. \nIt is captain.[your-captain-root-domain] :',
|
||||
validate: (value) => {
|
||||
if (value === SAMPLE_DOMAIN) {
|
||||
return 'Enter a valid URL';
|
||||
}
|
||||
if (!cleanUpUrl(value))
|
||||
return 'This is an invalid URL: ' + value;
|
||||
let found = undefined;
|
||||
StorageHelper_1.default.get().getMachines().map((machine) => {
|
||||
if (cleanUpUrl(machine.baseUrl) === cleanUpUrl(value)) {
|
||||
found = machine.name;
|
||||
}
|
||||
});
|
||||
if (found) {
|
||||
return `${value} already exist as ${found} in your currently logged in machines. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
|
||||
}
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
return 'Please enter a valid address.';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'captainHasRootSsl',
|
||||
message: 'Is HTTPS activated for this Captain machine?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainPassword',
|
||||
message: 'Enter your password:',
|
||||
validate: (value) => {
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
return 'Please enter your password.';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainName',
|
||||
message: 'Enter a name for this Captain machine:',
|
||||
default: CliHelper_1.default.get().findDefaultCaptainName(),
|
||||
validate: (value) => {
|
||||
value = value.trim();
|
||||
if (StorageHelper_1.default.get().findMachine(value)) {
|
||||
return `${value} already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
|
||||
}
|
||||
if (CliHelper_1.default.get().isNameValid(value)) {
|
||||
return true;
|
||||
}
|
||||
return 'Please enter a Captain Name.';
|
||||
}
|
||||
}
|
||||
];
|
||||
const answers = (yield inquirer.prompt(questions));
|
||||
const { captainHasRootSsl, captainPassword, captainAddress, captainName } = answers;
|
||||
const handleHttp = captainHasRootSsl ? 'https://' : 'http://';
|
||||
const baseUrl = `${handleHttp}${cleanUpUrl(captainAddress)}`;
|
||||
try {
|
||||
const tokenToIgnore = yield CliApiManager_1.default.get({
|
||||
authToken: '',
|
||||
baseUrl,
|
||||
name: captainName
|
||||
}).getAuthToken(captainPassword);
|
||||
StdOutUtil_1.default.printGreenMessage(`\nLogged in successfully to ${baseUrl}`);
|
||||
StdOutUtil_1.default.printGreenMessage(`Authorization token is now saved as ${captainName} \n`);
|
||||
}
|
||||
catch (error) {
|
||||
const errorMessage = error.message ? error.message : error;
|
||||
StdOutUtil_1.default.printError(`Something bad happened. Cannot save "${captainName}" \n${errorMessage}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.default = login;
|
||||
//# sourceMappingURL=login.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":";;;;;;;;;;;AAEA,qCAAqC;AACrC,oDAA6C;AAC7C,0DAAmD;AACnD,kDAA2C;AAC3C,0CAAmC;AACnC,kDAA2C;AAE3C,wDAAiD;AAEjD,MAAM,aAAa,GAAG,mBAAS,CAAC,aAAa,CAAC;AAC9C,MAAM,UAAU,GAAG,eAAK,CAAC,UAAU,CAAC;AAEpC,SAAe,KAAK;;QACnB,oBAAU,CAAC,YAAY,CAAC,4BAA4B,CAAC,CAAC;QAEtD,MAAM,SAAS,GAAG;YACjB;gBACC,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,aAAa;gBACtB,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,sFAAsF;gBAC/F,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC3B,IAAI,KAAK,KAAK,aAAa,EAAE;wBAC5B,OAAO,mBAAmB,CAAC;qBAC3B;oBAED,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;wBAAE,OAAO,0BAA0B,GAAG,KAAK,CAAC;oBAElE,IAAI,KAAK,GAAG,SAAS,CAAC;oBACtB,uBAAa,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;wBACjD,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,UAAU,CAAC,KAAK,CAAC,EAAE;4BACtD,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;yBACrB;oBACF,CAAC,CAAC,CAAC;oBAEH,IAAI,KAAK,EAAE;wBACV,OAAO,GAAG,KAAK,qBAAqB,KAAK,8IAA8I,CAAC;qBACxL;oBAED,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE;wBAC1B,OAAO,IAAI,CAAC;qBACZ;oBAED,OAAO,+BAA+B,CAAC;gBACxC,CAAC;aACD;YACD;gBACC,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE,8CAA8C;gBACvD,OAAO,EAAE,IAAI;aACb;YACD;gBACC,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,sBAAsB;gBAC/B,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC3B,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE;wBAC1B,OAAO,IAAI,CAAC;qBACZ;oBAED,OAAO,6BAA6B,CAAC;gBACtC,CAAC;aACD;YACD;gBACC,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,wCAAwC;gBACjD,OAAO,EAAE,mBAAS,CAAC,GAAG,EAAE,CAAC,sBAAsB,EAAE;gBACjD,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC3B,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;oBAErB,IAAI,uBAAa,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;wBAC3C,OAAO,GAAG,KAAK,uHAAuH,CAAC;qBACvI;oBAED,IAAI,mBAAS,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;wBACvC,OAAO,IAAI,CAAC;qBACZ;oBAED,OAAO,8BAA8B,CAAC;gBACvC,CAAC;aACD;SACD,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAA4B,CAAC;QAC9E,MAAM,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QACpF,MAAM,UAAU,GAAG,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9D,MAAM,OAAO,GAAG,GAAG,UAAU,GAAG,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAE7D,IAAI;YACH,MAAM,aAAa,GAAG,MAAM,uBAAa,CAAC,GAAG,CAAC;gBAC7C,SAAS,EAAE,EAAE;gBACb,OAAO;gBACP,IAAI,EAAE,WAAW;aACjB,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;YAEjC,oBAAU,CAAC,iBAAiB,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;YACvE,oBAAU,CAAC,iBAAiB,CAAC,uCAAuC,WAAW,KAAK,CAAC,CAAC;SACtF;QAAC,OAAO,KAAK,EAAE;YACf,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAE3D,oBAAU,CAAC,UAAU,CAAC,wCAAwC,WAAW,OAAO,YAAY,EAAE,CAAC,CAAC;SAChG;IACF,CAAC;CAAA;AAED,kBAAe,KAAK,CAAC"}
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const inquirer = require("inquirer");
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const CliHelper_1 = require("../utils/CliHelper");
|
||||
function generateQuestions() {
|
||||
const listOfMachines = CliHelper_1.default.get().getMachinesAsOptions();
|
||||
return [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'captainNameToLogout',
|
||||
message: 'Select the Captain Machine you want to logout from:',
|
||||
choices: listOfMachines
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmedToLogout',
|
||||
message: 'Are you sure you want to logout from this Captain machine?',
|
||||
default: false,
|
||||
when: (answers) => answers.captainNameToLogout
|
||||
}
|
||||
];
|
||||
}
|
||||
function logout() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const questions = generateQuestions();
|
||||
StdOutUtil_1.default.printMessage('Logout from a Captain Machine and clear auth info');
|
||||
const answers = yield inquirer.prompt(questions);
|
||||
const { captainNameToLogout, confirmedToLogout } = answers;
|
||||
if (!captainNameToLogout || !confirmedToLogout) {
|
||||
StdOutUtil_1.default.printMessage('\nOperation cancelled by the user...\n');
|
||||
return;
|
||||
}
|
||||
CliHelper_1.default.get().logoutMachine(captainNameToLogout);
|
||||
});
|
||||
}
|
||||
exports.default = logout;
|
||||
//# sourceMappingURL=logout.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":";;;;;;;;;;;AAEA,qCAAqC;AACrC,oDAA6C;AAC7C,kDAA2C;AAE3C,SAAS,iBAAiB;IACzB,MAAM,cAAc,GAAG,mBAAS,CAAC,GAAG,EAAE,CAAC,oBAAoB,EAAE,CAAC;IAE9D,OAAO;QACN;YACC,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,qDAAqD;YAC9D,OAAO,EAAE,cAAc;SACvB;QACD;YACC,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,mBAAmB;YACzB,OAAO,EAAE,4DAA4D;YACrE,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,CAAC,OAAY,EAAE,EAAE,CAAC,OAAO,CAAC,mBAAmB;SACnD;KACD,CAAC;AACH,CAAC;AAED,SAAe,MAAM;;QACpB,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;QAEtC,oBAAU,CAAC,YAAY,CAAC,mDAAmD,CAAC,CAAC;QAE7E,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC;QAE3D,IAAI,CAAC,mBAAmB,IAAI,CAAC,iBAAiB,EAAE;YAC/C,oBAAU,CAAC,YAAY,CAAC,wCAAwC,CAAC,CAAC;YAClE,OAAO;SACP;QAED,mBAAS,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC;IACpD,CAAC;CAAA;AAED,kBAAe,MAAM,CAAC"}
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const inquirer = require("inquirer");
|
||||
const CliApiManager_1 = require("../api/CliApiManager");
|
||||
// In case the token is expired
|
||||
function requestLogin(machine) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const { baseUrl } = machine;
|
||||
StdOutUtil_1.default.printMessage('Your auth token is not valid anymore. Try to login again.');
|
||||
const questions = [
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainPassword',
|
||||
message: 'Please enter your password for ' + baseUrl,
|
||||
validate: (value) => {
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
return 'Please enter your password for ' + baseUrl;
|
||||
}
|
||||
}
|
||||
];
|
||||
const loginPassword = (yield inquirer.prompt(questions));
|
||||
const password = loginPassword.captainPassword;
|
||||
const responseIgnore = yield CliApiManager_1.default.get(machine).getAuthToken(password);
|
||||
});
|
||||
}
|
||||
exports.default = requestLogin;
|
||||
//# sourceMappingURL=requestLogin.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requestLogin.js","sourceRoot":"","sources":["../../src/commands/requestLogin.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,oDAA6C;AAC7C,qCAAqC;AAErC,wDAAiD;AAEjD,+BAA+B;AAC/B,SAA8B,YAAY,CAAC,OAAiB;;QAC3D,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAE5B,oBAAU,CAAC,YAAY,CAAC,2DAA2D,CAAC,CAAC;QAErF,MAAM,SAAS,GAAG;YACjB;gBACC,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,iCAAiC,GAAG,OAAO;gBACpD,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC3B,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE;wBAC1B,OAAO,IAAI,CAAC;qBACZ;oBAED,OAAO,iCAAiC,GAAG,OAAO,CAAC;gBACpD,CAAC;aACD;SACD,CAAC;QACF,MAAM,aAAa,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAQ,CAAC;QAChE,MAAM,QAAQ,GAAG,aAAa,CAAC,eAAe,CAAC;QAC/C,MAAM,cAAc,GAAG,MAAM,uBAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IAChF,CAAC;CAAA;AAtBD,+BAsBC"}
|
||||
Executable
+203
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const inquirer = require("inquirer");
|
||||
const Constants_1 = require("../utils/Constants");
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const ValidationsHandler_1 = require("../utils/ValidationsHandler");
|
||||
const CliApiManager_1 = require("../api/CliApiManager");
|
||||
const Utils_1 = require("../utils/Utils");
|
||||
const CliHelper_1 = require("../utils/CliHelper");
|
||||
const StorageHelper_1 = require("../utils/StorageHelper");
|
||||
const ErrorFactory_1 = require("../utils/ErrorFactory");
|
||||
const SpinnerHelper_1 = require("../utils/SpinnerHelper");
|
||||
let newPasswordFirstTry = undefined;
|
||||
let lastWorkingPassword = Constants_1.default.DEFAULT_PASSWORD;
|
||||
let serverIpAddress = '';
|
||||
let captainMachine = {
|
||||
authToken: '',
|
||||
baseUrl: '',
|
||||
name: ''
|
||||
};
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'hasInstalledCaptain',
|
||||
message: 'Have you already installed Captain on your server by running the following line:' +
|
||||
'\nmkdir /captain && docker run -p 80:80 -p 443:443 -p 3000:3000 -v /var/run/docker.sock:/var/run/docker.sock dockersaturn/captainduckduck ?',
|
||||
default: 'Yes',
|
||||
choices: ['Yes', 'No'],
|
||||
filter: (value) => {
|
||||
const answerFromUser = value.trim();
|
||||
if (answerFromUser === 'Yes')
|
||||
return answerFromUser;
|
||||
StdOutUtil_1.default.printMessage('\n\nCannot start the setup process if Captain is not installed.');
|
||||
StdOutUtil_1.default.printMessageAndExit('Please read tutorial on CaptainDuckDuck.com to learn how to install CaptainDuckDuck on a server.');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default: Constants_1.default.SAMPLE_IP,
|
||||
name: 'captainAddress',
|
||||
message: 'Enter IP address of your captain server:',
|
||||
filter: (value) => __awaiter(this, void 0, void 0, function* () {
|
||||
const ipFromUser = value.trim();
|
||||
if (ipFromUser === Constants_1.default.SAMPLE_IP || !ValidationsHandler_1.isIpAddress(ipFromUser)) {
|
||||
StdOutUtil_1.default.printError(`\nThis is an invalid IP Address: ${ipFromUser}`, true);
|
||||
}
|
||||
try {
|
||||
// login using captain42. and set the ipAddressToServer
|
||||
captainMachine.baseUrl = `http://${ipFromUser}:3000`;
|
||||
yield CliApiManager_1.default.get(captainMachine).getAuthToken(lastWorkingPassword);
|
||||
serverIpAddress = ipFromUser;
|
||||
}
|
||||
catch (e) {
|
||||
// User may have used a different default password
|
||||
if (e.captainStatus === ErrorFactory_1.default.STATUS_WRONG_PASSWORD)
|
||||
return '';
|
||||
StdOutUtil_1.default.errorHandler(e);
|
||||
}
|
||||
return ipFromUser;
|
||||
})
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainOriginalPassword',
|
||||
message: 'Enter your current password:',
|
||||
when: () => !captainMachine.authToken,
|
||||
filter: (value) => __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
yield CliApiManager_1.default.get(captainMachine).getAuthToken(value);
|
||||
lastWorkingPassword = value;
|
||||
return '';
|
||||
}
|
||||
catch (e) {
|
||||
StdOutUtil_1.default.errorHandler(e);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainRootDomain',
|
||||
message: 'Enter a root domain for this Captain server. For example, enter test.yourdomain.com if you' +
|
||||
' setup your DNS to point *.test.yourdomain.com to ip address of your server.',
|
||||
filter: (value) => __awaiter(this, void 0, void 0, function* () {
|
||||
const captainRootDomainFromUser = value.trim();
|
||||
try {
|
||||
yield CliApiManager_1.default.get(captainMachine).updateRootDomain(captainRootDomainFromUser);
|
||||
captainMachine = Utils_1.default.copyObject(captainMachine);
|
||||
captainMachine.baseUrl = `http://captain.${captainRootDomainFromUser}`;
|
||||
}
|
||||
catch (e) {
|
||||
StdOutUtil_1.default.printError('\n\n');
|
||||
if (e.captainStatus === ErrorFactory_1.default.VERIFICATION_FAILED) {
|
||||
if (captainRootDomainFromUser.indexOf('/') >= 0) {
|
||||
StdOutUtil_1.default.printError('DO NOT include http in your base domain, it should be just plain domain, e.g., test.domain.com');
|
||||
}
|
||||
if (captainRootDomainFromUser.indexOf('*') >= 0) {
|
||||
StdOutUtil_1.default.printError('DO NOT include * in your base domain, it should be just plain domain, e.g., test.domain.com');
|
||||
}
|
||||
StdOutUtil_1.default.printError(`\n\nCannot verify that http://captain.${captainRootDomainFromUser} points to your server IP.\n` +
|
||||
`\nAre you sure that you set *.${captainRootDomainFromUser} points to ${serverIpAddress}\n\n` +
|
||||
`Double check your DNS. If everything looks correct, note that, DNS changes take up to 24 hrs to work properly. Check with your Domain Provider.`);
|
||||
}
|
||||
StdOutUtil_1.default.errorHandler(e);
|
||||
}
|
||||
return captainRootDomainFromUser;
|
||||
})
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'newPasswordFirstTry',
|
||||
message: 'Enter a new password:',
|
||||
filter: (value) => {
|
||||
newPasswordFirstTry = value;
|
||||
if (!newPasswordFirstTry) {
|
||||
StdOutUtil_1.default.printError('Password empty.', true);
|
||||
throw new Error('Password empty');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'newPassword',
|
||||
message: 'Enter your new password again:',
|
||||
filter: (value) => __awaiter(this, void 0, void 0, function* () {
|
||||
const confirmPasswordValueFromUser = value;
|
||||
if ((newPasswordFirstTry !== confirmPasswordValueFromUser)) {
|
||||
StdOutUtil_1.default.printError('Passwords do not match. Try serversetup again.', true);
|
||||
throw new Error('Password mismatch');
|
||||
}
|
||||
return '';
|
||||
})
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'emailAddress',
|
||||
message: "Enter your 'valid' email address to enable HTTPS: ",
|
||||
filter: (value) => __awaiter(this, void 0, void 0, function* () {
|
||||
const emailAddressFromUser = value.trim();
|
||||
let forcedSsl = false;
|
||||
try {
|
||||
SpinnerHelper_1.default.start('Enabling SSL... Takes a few seconds...');
|
||||
yield CliApiManager_1.default.get(captainMachine).enableRootSsl(emailAddressFromUser);
|
||||
captainMachine = Utils_1.default.copyObject(captainMachine);
|
||||
captainMachine.baseUrl = captainMachine.baseUrl.replace('http://', 'https://');
|
||||
yield CliApiManager_1.default.get(captainMachine).forceSsl(true);
|
||||
forcedSsl = true;
|
||||
yield CliApiManager_1.default.get(captainMachine).changePass(lastWorkingPassword, newPasswordFirstTry);
|
||||
lastWorkingPassword = newPasswordFirstTry;
|
||||
yield CliApiManager_1.default.get(captainMachine).getAuthToken(lastWorkingPassword);
|
||||
SpinnerHelper_1.default.stop();
|
||||
}
|
||||
catch (e) {
|
||||
if (forcedSsl) {
|
||||
StdOutUtil_1.default.printError('Server is setup, but password was not changed due to an error. You cannot use serversetup again.');
|
||||
StdOutUtil_1.default.printError(`Instead, go to ${captainMachine.baseUrl} and change your password on settings page.`);
|
||||
StdOutUtil_1.default.printError(`Then, Use captainduckduck login on your local machine to connect to your server.`);
|
||||
}
|
||||
SpinnerHelper_1.default.fail();
|
||||
StdOutUtil_1.default.errorHandler(e);
|
||||
}
|
||||
return emailAddressFromUser;
|
||||
})
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainName',
|
||||
message: 'Enter a name for this Captain machine:',
|
||||
default: CliHelper_1.default.get().findDefaultCaptainName(),
|
||||
validate: (value) => {
|
||||
const newMachineName = value.trim();
|
||||
let errorMessage = undefined;
|
||||
if (StorageHelper_1.default.get().findMachine(newMachineName)) {
|
||||
return `${newMachineName} already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
|
||||
}
|
||||
if (CliHelper_1.default.get().isNameValid(newMachineName)) {
|
||||
captainMachine.name = newMachineName;
|
||||
return true;
|
||||
}
|
||||
return 'Please enter a valid Captain Name. Small letters, numbers, single hyphen.';
|
||||
}
|
||||
}
|
||||
];
|
||||
function serversetup() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
StdOutUtil_1.default.printMessage('\nSetup your Captain server\n');
|
||||
const answersIgnore = yield inquirer.prompt(questions);
|
||||
StorageHelper_1.default.get().saveMachine(captainMachine);
|
||||
StdOutUtil_1.default.printMessage(`\n\nCaptain is available at ${captainMachine.baseUrl}`);
|
||||
StdOutUtil_1.default.printMessage('\nFor more details and docs see http://www.captainduckduck.com\n\n');
|
||||
});
|
||||
}
|
||||
exports.default = serversetup;
|
||||
//# sourceMappingURL=serversetup.js.map
|
||||
Executable
+1
File diff suppressed because one or more lines are too long
Executable
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=AppDef.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AppDef.js","sourceRoot":"","sources":["../../src/models/AppDef.ts"],"names":[],"mappings":""}
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
;
|
||||
//# sourceMappingURL=IBuildLogs.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"IBuildLogs.js","sourceRoot":"","sources":["../../src/models/IBuildLogs.ts"],"names":[],"mappings":";;AAOC,CAAC"}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=ICaptainDefinition.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ICaptainDefinition.js","sourceRoot":"","sources":["../../src/models/ICaptainDefinition.ts"],"names":[],"mappings":""}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=IHashMapGeneric.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"IHashMapGeneric.js","sourceRoot":"","sources":["../../src/models/IHashMapGeneric.ts"],"names":[],"mappings":""}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=IOneClickAppModels.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"IOneClickAppModels.js","sourceRoot":"","sources":["../../src/models/IOneClickAppModels.ts"],"names":[],"mappings":""}
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class IRegistryTypes {
|
||||
}
|
||||
IRegistryTypes.LOCAL_REG = "LOCAL_REG";
|
||||
IRegistryTypes.REMOTE_REG = "REMOTE_REG";
|
||||
exports.IRegistryTypes = IRegistryTypes;
|
||||
//# sourceMappingURL=IRegistryInfo.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"IRegistryInfo.js","sourceRoot":"","sources":["../../src/models/IRegistryInfo.ts"],"names":[],"mappings":";;AAKA,MAAa,cAAc;;AACT,wBAAS,GAAG,WAAW,CAAC;AACxB,yBAAU,GAAG,YAAY,CAAC;AAF5C,wCAGC"}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=IVersionInfo.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"IVersionInfo.js","sourceRoot":"","sources":["../../src/models/IVersionInfo.ts"],"names":[],"mappings":""}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=StoredObjects.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"StoredObjects.js","sourceRoot":"","sources":["../../../src/models/storage/StoredObjects.ts"],"names":[],"mappings":""}
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const StorageHelper_1 = require("./StorageHelper");
|
||||
const StdOutUtil_1 = require("./StdOutUtil");
|
||||
class CliHelper {
|
||||
static get() {
|
||||
if (!CliHelper.instance)
|
||||
CliHelper.instance = new CliHelper();
|
||||
return CliHelper.instance;
|
||||
}
|
||||
isNameValid(value) {
|
||||
value = value || '';
|
||||
if (!!value && value.match(/^[-\d\w]+$/i) && value.indexOf('--') < 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
getAppsAsOptions(apps) {
|
||||
const firstItemInOption = [
|
||||
{
|
||||
name: '-- CANCEL --',
|
||||
value: '',
|
||||
short: ''
|
||||
}
|
||||
];
|
||||
const listOfApps = apps.map((app) => {
|
||||
return {
|
||||
name: `${app.appName}`,
|
||||
value: `${app.appName}`,
|
||||
short: `${app.appName}`
|
||||
};
|
||||
});
|
||||
return [...firstItemInOption, ...listOfApps];
|
||||
}
|
||||
getMachinesAsOptions() {
|
||||
const machines = StorageHelper_1.default.get().getMachines();
|
||||
const firstItemInOption = [
|
||||
{
|
||||
name: '-- CANCEL --',
|
||||
value: '',
|
||||
short: ''
|
||||
}
|
||||
];
|
||||
const listOfMachines = machines.map((machine) => {
|
||||
return {
|
||||
name: `${machine.name} at ${machine.baseUrl}`,
|
||||
value: `${machine.name}`,
|
||||
short: `${machine.name} at ${machine.baseUrl}`
|
||||
};
|
||||
});
|
||||
return [...firstItemInOption, ...listOfMachines];
|
||||
}
|
||||
logoutMachine(machineName) {
|
||||
const removedMachine = StorageHelper_1.default.get().removeMachine(machineName);
|
||||
StdOutUtil_1.default.printMessage(`You are now logged out from ${removedMachine.name} at ${removedMachine.baseUrl}...\n`);
|
||||
}
|
||||
findDefaultCaptainName() {
|
||||
let currentSuffix = StorageHelper_1.default.get().getMachines().length + 1;
|
||||
const self = this;
|
||||
while (!self.isSuffixValid(currentSuffix)) {
|
||||
currentSuffix++;
|
||||
}
|
||||
return self.getCaptainFullName(currentSuffix);
|
||||
}
|
||||
getCaptainFullName(suffix) {
|
||||
const formatSuffix = suffix < 10 ? `0${suffix}` : suffix;
|
||||
return `captain-${formatSuffix}`;
|
||||
}
|
||||
isSuffixValid(suffixNumber) {
|
||||
const self = this;
|
||||
let valid = true;
|
||||
StorageHelper_1.default.get().getMachines().map((machine) => {
|
||||
if (machine.name === self.getCaptainFullName(suffixNumber)) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
exports.default = CliHelper;
|
||||
//# sourceMappingURL=CliHelper.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CliHelper.js","sourceRoot":"","sources":["../../src/utils/CliHelper.ts"],"names":[],"mappings":"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"}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const SAMPLE_DOMAIN = "captain.captainroot.yourdomain.com";
|
||||
const SAMPLE_IP = "123.123.123.123";
|
||||
const DEFAULT_PASSWORD = "captain42";
|
||||
const DEFAULT_BRANCH_TO_PUSH = "branchToPush";
|
||||
const DEFAULT_APP_NAME = "appName";
|
||||
const EMPTY_STRING = "";
|
||||
exports.default = {
|
||||
SAMPLE_DOMAIN,
|
||||
SAMPLE_IP,
|
||||
DEFAULT_PASSWORD,
|
||||
DEFAULT_BRANCH_TO_PUSH,
|
||||
DEFAULT_APP_NAME,
|
||||
EMPTY_STRING
|
||||
};
|
||||
//# sourceMappingURL=Constants.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Constants.js","sourceRoot":"","sources":["../../src/utils/Constants.ts"],"names":[],"mappings":";;AAAA,MAAM,aAAa,GAAG,oCAAoC,CAAA;AAC1D,MAAM,SAAS,GAAG,iBAAiB,CAAA;AACnC,MAAM,gBAAgB,GAAG,WAAW,CAAA;AACpC,MAAM,sBAAsB,GAAG,cAAc,CAAA;AAC7C,MAAM,gBAAgB,GAAG,SAAS,CAAA;AAClC,MAAM,YAAY,GAAG,EAAE,CAAA;AAEvB,kBAAe;IACb,aAAa;IACb,SAAS;IACT,gBAAgB;IAChB,sBAAsB;IACtB,gBAAgB;IAChB,YAAY;CACb,CAAA"}
|
||||
Executable
+183
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const child_process_1 = require("child_process");
|
||||
const StdOutUtil_1 = require("../utils/StdOutUtil");
|
||||
const ProgressBar = require('progress');
|
||||
const commandExistsSync = require('command-exists').sync;
|
||||
const CliApiManager_1 = require("../api/CliApiManager");
|
||||
const SpinnerHelper_1 = require("../utils/SpinnerHelper");
|
||||
const StorageHelper_1 = require("./StorageHelper");
|
||||
class DeployHelper {
|
||||
constructor(deployParams) {
|
||||
this.deployParams = deployParams;
|
||||
this.lastLineNumberPrinted = -10000; // we want to show all lines to begin with!
|
||||
//
|
||||
}
|
||||
gitArchiveFile(zipFileFullPath, branchToPush) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Removes the temporary file created
|
||||
if (fs.pathExistsSync(zipFileFullPath))
|
||||
fs.removeSync(zipFileFullPath);
|
||||
if (!commandExistsSync('git')) {
|
||||
StdOutUtil_1.default.printError("'git' command not found...\nCaptain needs 'git' to create tar file of your source files...", true);
|
||||
reject("Captain needs 'git' to create tar file of your source files...");
|
||||
return;
|
||||
}
|
||||
child_process_1.exec(`git archive --format tar --output "${zipFileFullPath}" ${branchToPush}`, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
StdOutUtil_1.default.printError(`TAR file failed\n${err}\n`);
|
||||
fs.removeSync(zipFileFullPath);
|
||||
reject(new Error('TAR file failed'));
|
||||
return;
|
||||
}
|
||||
child_process_1.exec(`git rev-parse ${branchToPush}`, (err, stdout, stderr) => {
|
||||
const gitHash = (stdout || '').trim();
|
||||
if (err || !/^[a-f0-9]{40}$/.test(gitHash)) {
|
||||
StdOutUtil_1.default.printError(`Cannot find hash of last commit on this branch: ${branchToPush}\n${gitHash}\n${err}\n`);
|
||||
reject(new Error('rev-parse failed'));
|
||||
return;
|
||||
}
|
||||
StdOutUtil_1.default.printMessage(`Pushing last commit on ${branchToPush}: ${gitHash}`);
|
||||
resolve(gitHash);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
getFileStream(zipFileFullPath) {
|
||||
const fileSize = fs.statSync(zipFileFullPath).size;
|
||||
const fileStream = fs.createReadStream(zipFileFullPath);
|
||||
const barOpts = {
|
||||
width: 20,
|
||||
total: fileSize,
|
||||
clear: false
|
||||
};
|
||||
const bar = new ProgressBar(' uploading [:bar] :percent (ETA :etas)', barOpts);
|
||||
fileStream.on('data', (chunk) => {
|
||||
bar.tick(chunk.length);
|
||||
});
|
||||
fileStream.on('end', () => {
|
||||
StdOutUtil_1.default.printMessage('This might take several minutes. PLEASE BE PATIENT...');
|
||||
SpinnerHelper_1.default.start('Building your source code...\n');
|
||||
SpinnerHelper_1.default.setColor('yellow');
|
||||
});
|
||||
return fileStream;
|
||||
}
|
||||
startDeploy() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const appName = this.deployParams.appName;
|
||||
const branchToPush = this.deployParams.deploySource.branchToPush;
|
||||
const tarFilePath = this.deployParams.deploySource.tarFilePath;
|
||||
const machineToDeploy = this.deployParams.captainMachine;
|
||||
const deploySource = this.deployParams.deploySource;
|
||||
if (!appName || (!branchToPush && !tarFilePath) || !machineToDeploy) {
|
||||
StdOutUtil_1.default.printError('Default deploy failed. Missing appName or branchToPush/tarFilePath or machineToDeploy.', true);
|
||||
return;
|
||||
}
|
||||
if (branchToPush && tarFilePath) {
|
||||
StdOutUtil_1.default.printError('Default deploy failed. branchToPush/tarFilePath cannot both be present.', true);
|
||||
return;
|
||||
}
|
||||
let tarFileCreatedByCli = false;
|
||||
const tarFileNameToDeploy = tarFilePath ? tarFilePath : 'temporary-captain-to-deploy.tar';
|
||||
const tarFileFullPath = tarFileNameToDeploy.startsWith('/')
|
||||
? tarFileNameToDeploy // absolute path
|
||||
: path.join(process.cwd(), tarFileNameToDeploy); // relative path
|
||||
let gitHash = '';
|
||||
if (branchToPush) {
|
||||
tarFileCreatedByCli = true;
|
||||
StdOutUtil_1.default.printMessage(`Saving tar file to:\n${tarFileFullPath}\n`);
|
||||
gitHash = yield this.gitArchiveFile(tarFileFullPath, branchToPush);
|
||||
}
|
||||
StdOutUtil_1.default.printMessage(`Deploying ${appName} to ${machineToDeploy.name}`);
|
||||
try {
|
||||
StdOutUtil_1.default.printMessage(`Uploading the file to ${machineToDeploy.baseUrl}`);
|
||||
yield CliApiManager_1.default.get(machineToDeploy).uploadAppData(appName, this.getFileStream(tarFileFullPath));
|
||||
StdOutUtil_1.default.printMessage(`Upload done.`);
|
||||
StorageHelper_1.default.get().saveDeployedDirectory({
|
||||
appName: appName,
|
||||
cwd: process.cwd(),
|
||||
deploySource: deploySource,
|
||||
machineNameToDeploy: machineToDeploy.name
|
||||
});
|
||||
if (tarFileCreatedByCli && fs.pathExistsSync(tarFileFullPath))
|
||||
fs.removeSync(tarFileFullPath);
|
||||
this.startFetchingBuildLogs(machineToDeploy, appName);
|
||||
}
|
||||
catch (e) {
|
||||
if (tarFileCreatedByCli && fs.pathExistsSync(tarFileFullPath))
|
||||
fs.removeSync(tarFileFullPath);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
onLogRetrieved(data, machineToDeploy, appName) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const self = this;
|
||||
if (data) {
|
||||
const lines = data.logs.lines;
|
||||
const firstLineNumberOfLogs = data.logs.firstLineNumber;
|
||||
let firstLinesToPrint = 0;
|
||||
if (firstLineNumberOfLogs > this.lastLineNumberPrinted) {
|
||||
if (firstLineNumberOfLogs < 0) {
|
||||
// This is the very first fetch, probably firstLineNumberOfLogs is around -50
|
||||
firstLinesToPrint = -firstLineNumberOfLogs;
|
||||
}
|
||||
else {
|
||||
StdOutUtil_1.default.printMessage('[[ TRUNCATED ]]');
|
||||
}
|
||||
}
|
||||
else {
|
||||
firstLinesToPrint = this.lastLineNumberPrinted - firstLineNumberOfLogs;
|
||||
}
|
||||
this.lastLineNumberPrinted = firstLineNumberOfLogs + lines.length;
|
||||
for (let i = firstLinesToPrint; i < lines.length; i++) {
|
||||
StdOutUtil_1.default.printMessage((lines[i] || '').trim());
|
||||
}
|
||||
}
|
||||
if (data && !data.isAppBuilding) {
|
||||
if (!data.isBuildFailed) {
|
||||
const appUrl = self.deployParams.captainMachine.baseUrl
|
||||
.replace('https://', 'http://')
|
||||
.replace('//captain.', '//' + appName + '.');
|
||||
StdOutUtil_1.default.printGreenMessage(`\n\n\nDeployed successfully: ${appName}`);
|
||||
StdOutUtil_1.default.printMagentaMessage(`App is available at ${appUrl}`, true);
|
||||
}
|
||||
else {
|
||||
StdOutUtil_1.default.printError(`\n\nSomething bad happened. Cannot deploy "${appName}"\n`, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setTimeout(() => {
|
||||
this.startFetchingBuildLogs(machineToDeploy, appName);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
startFetchingBuildLogs(machineToDeploy, appName) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const self = this;
|
||||
try {
|
||||
const data = yield CliApiManager_1.default.get(machineToDeploy).fetchBuildLogs(appName);
|
||||
this.onLogRetrieved(data, machineToDeploy, appName);
|
||||
}
|
||||
catch (error) {
|
||||
StdOutUtil_1.default.printError(`\nSomething while retrieving app build logs.. ${error}\n`);
|
||||
this.onLogRetrieved(undefined, machineToDeploy, appName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.default = DeployHelper;
|
||||
//# sourceMappingURL=DeployHelper.js.map
|
||||
Executable
+1
File diff suppressed because one or more lines are too long
Executable
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class ErrorFactory {
|
||||
constructor() {
|
||||
this.OKAY = 100;
|
||||
this.OKAY_BUILD_STARTED = 101;
|
||||
this.STATUS_ERROR_GENERIC = 1000;
|
||||
this.STATUS_ERROR_CAPTAIN_NOT_INITIALIZED = 1001;
|
||||
this.STATUS_ERROR_USER_NOT_INITIALIZED = 1101;
|
||||
this.STATUS_ERROR_NOT_AUTHORIZED = 1102;
|
||||
this.STATUS_ERROR_ALREADY_EXIST = 1103;
|
||||
this.STATUS_ERROR_BAD_NAME = 1104;
|
||||
this.STATUS_WRONG_PASSWORD = 1105;
|
||||
this.STATUS_AUTH_TOKEN_INVALID = 1106;
|
||||
this.VERIFICATION_FAILED = 1107;
|
||||
this.UNKNOWN_ERROR = 1999;
|
||||
}
|
||||
createError(status, message) {
|
||||
let e = new Error(message);
|
||||
e.captainStatus = status;
|
||||
e.captainMessage = message;
|
||||
return e;
|
||||
}
|
||||
eatUpPromiseRejection() {
|
||||
return function (error) {
|
||||
// nom nom
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.default = new ErrorFactory();
|
||||
//# sourceMappingURL=ErrorFactory.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ErrorFactory.js","sourceRoot":"","sources":["../../src/utils/ErrorFactory.ts"],"names":[],"mappings":";;AACA,MAAM,YAAY;IAgBhB;QAfgB,SAAI,GAAG,GAAG,CAAC;QACX,uBAAkB,GAAG,GAAG,CAAC;QAEzB,yBAAoB,GAAG,IAAI,CAAC;QAC5B,yCAAoC,GAAG,IAAI,CAAC;QAC5C,sCAAiC,GAAG,IAAI,CAAC;QACzC,gCAA2B,GAAG,IAAI,CAAC;QACnC,+BAA0B,GAAG,IAAI,CAAC;QAClC,0BAAqB,GAAG,IAAI,CAAC;QAC7B,0BAAqB,GAAG,IAAI,CAAC;QAC7B,8BAAyB,GAAG,IAAI,CAAC;QACjC,wBAAmB,GAAG,IAAI,CAAC;QAE3B,kBAAa,GAAG,IAAI,CAAC;IAEtB,CAAC;IAEhB,WAAW,CAAC,MAAc,EAAE,OAAe;QACzC,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,OAAO,CAAQ,CAAC;QAClC,CAAC,CAAC,aAAa,GAAG,MAAM,CAAC;QACzB,CAAC,CAAC,cAAc,GAAG,OAAO,CAAC;QAC3B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,qBAAqB;QACnB,OAAO,UAAS,KAAU;YACxB,UAAU;QACZ,CAAC,CAAC;IACJ,CAAC;CACF;AAED,kBAAe,IAAI,YAAY,EAAE,CAAC"}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class Logger {
|
||||
static log(s) {
|
||||
console.log(s);
|
||||
}
|
||||
static error(s) {
|
||||
console.error(s);
|
||||
}
|
||||
static dev(s) {
|
||||
if (process.env.CLI_IS_DEBUG) {
|
||||
console.log(">>> ", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = Logger;
|
||||
//# sourceMappingURL=Logger.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../src/utils/Logger.ts"],"names":[],"mappings":";;AAAA,MAAqB,MAAM;IACzB,MAAM,CAAC,GAAG,CAAC,CAAS;QAClB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,CAAM;QACjB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,GAAG,CAAC,CAAS;QAClB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE;YAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;SACxB;IACH,CAAC;CACF;AAdD,yBAcC"}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ora = require('ora');
|
||||
class SpinnerHelper {
|
||||
start(message) {
|
||||
this.spinner = ora(message).start();
|
||||
}
|
||||
setColor(color) {
|
||||
this.spinner.color = color;
|
||||
}
|
||||
stop() {
|
||||
this.spinner.stop();
|
||||
}
|
||||
succeed() {
|
||||
this.spinner.succeed();
|
||||
}
|
||||
fail() {
|
||||
this.spinner.fail();
|
||||
}
|
||||
}
|
||||
exports.default = new SpinnerHelper();
|
||||
//# sourceMappingURL=SpinnerHelper.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"SpinnerHelper.js","sourceRoot":"","sources":["../../src/utils/SpinnerHelper.ts"],"names":[],"mappings":";;AAAA,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;AAE3B,MAAM,aAAa;IAGlB,KAAK,CAAC,OAAe;QACpB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC;IACrC,CAAC;IAED,QAAQ,CAAC,KAAa;QACrB,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED,IAAI;QACH,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC;IAED,OAAO;QACN,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,IAAI;QACH,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC;CACD;AAED,kBAAe,IAAI,aAAa,EAAE,CAAC"}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const chalk = require('chalk');
|
||||
class StdOutUtils {
|
||||
printMessage(message) {
|
||||
console.log(message);
|
||||
}
|
||||
printMessageAndExit(message) {
|
||||
console.log(message);
|
||||
process.exit(0);
|
||||
}
|
||||
printGreenMessage(message, exit = false) {
|
||||
console.log(`${chalk.green(message)}`);
|
||||
exit && process.exit(0);
|
||||
}
|
||||
printMagentaMessage(message, exit = false) {
|
||||
console.log(`${chalk.magenta(message)}`);
|
||||
exit && process.exit(0);
|
||||
}
|
||||
printError(error, exit = false) {
|
||||
console.log(`${chalk.bold.red(error)}`);
|
||||
exit && process.exit(0);
|
||||
}
|
||||
errorHandler(error) {
|
||||
if (error.captainStatus) {
|
||||
this.printError(`\nError Code: ${error.captainStatus} Message: ${error.captainMessage}`, true);
|
||||
}
|
||||
else if (error.status) {
|
||||
this.printError(`\nError status: ${error.status} Message: ${error.description || error.message}`, true);
|
||||
}
|
||||
else {
|
||||
this.printError(`\nError: ${error}`, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = new StdOutUtils();
|
||||
//# sourceMappingURL=StdOutUtil.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"StdOutUtil.js","sourceRoot":"","sources":["../../src/utils/StdOutUtil.ts"],"names":[],"mappings":";;AAAA,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAC/B,MAAM,WAAW;IAChB,YAAY,CAAC,OAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAED,mBAAmB,CAAC,OAAe;QAClC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAErB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,iBAAiB,CAAC,OAAe,EAAE,IAAI,GAAG,KAAK;QAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEvC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,mBAAmB,CAAC,OAAe,EAAE,IAAI,GAAG,KAAK;QAChD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,IAAI,GAAG,KAAK;QACrC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAExC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,YAAY,CAAC,KAAU;QACtB,IAAI,KAAK,CAAC,aAAa,EAAE;YACxB,IAAI,CAAC,UAAU,CAAC,iBAAiB,KAAK,CAAC,aAAa,eAAe,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,CAAC;SACjG;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE;YACxB,IAAI,CAAC,UAAU,CAAC,mBAAmB,KAAK,CAAC,MAAM,eAAe,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;SAC1G;aAAM;YACN,IAAI,CAAC,UAAU,CAAC,YAAY,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;SAC3C;IACF,CAAC;CACD;AACD,kBAAe,IAAI,WAAW,EAAE,CAAC"}
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ConfigStore = require("configstore");
|
||||
const Utils_1 = require("./Utils");
|
||||
const CAP_MACHINES = 'CapMachines';
|
||||
const DEPLOYED_DIRS = 'DeployedDirs';
|
||||
class StorageHelper {
|
||||
static get() {
|
||||
if (!StorageHelper.instance)
|
||||
StorageHelper.instance = new StorageHelper();
|
||||
return StorageHelper.instance;
|
||||
}
|
||||
constructor() {
|
||||
this.data = new ConfigStore('captainduckduck');
|
||||
this.migrateData();
|
||||
}
|
||||
migrateData() {
|
||||
const self = this;
|
||||
const data = this.data;
|
||||
const oldMachines = data.get('captainMachines') || [];
|
||||
const oldApps = data.get('apps') || [];
|
||||
oldMachines.forEach((m) => {
|
||||
self.saveMachine({
|
||||
authToken: m.authToken,
|
||||
baseUrl: m.baseUrl,
|
||||
name: m.name
|
||||
});
|
||||
});
|
||||
oldApps.forEach((app) => {
|
||||
self.saveDeployedDirectory({
|
||||
appName: app.appName,
|
||||
cwd: app.cwd,
|
||||
machineNameToDeploy: app.machineToDeploy.name,
|
||||
deploySource: {
|
||||
branchToPush: app.branchToPush
|
||||
}
|
||||
});
|
||||
});
|
||||
data.delete('captainMachines');
|
||||
data.delete('apps');
|
||||
}
|
||||
getMachines() {
|
||||
return Utils_1.default.copyObject(this.data.get(CAP_MACHINES) || []);
|
||||
}
|
||||
findMachine(machineName) {
|
||||
return this.getMachines().find((m) => m.name === machineName);
|
||||
}
|
||||
removeMachine(machineName) {
|
||||
const machines = this.getMachines();
|
||||
const removedMachine = machines.filter((machine) => machine.name === machineName)[0];
|
||||
const newMachines = machines.filter((machine) => machine.name !== machineName);
|
||||
this.data.set(CAP_MACHINES, newMachines);
|
||||
return removedMachine;
|
||||
}
|
||||
saveMachine(machineToSaveOrUpdate) {
|
||||
const currMachines = this.getMachines();
|
||||
let updatedMachine = false;
|
||||
for (let index = 0; index < currMachines.length; index++) {
|
||||
const element = currMachines[index];
|
||||
if (element.name === machineToSaveOrUpdate.name) {
|
||||
updatedMachine = true;
|
||||
currMachines[index] = machineToSaveOrUpdate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updatedMachine) {
|
||||
currMachines.push(machineToSaveOrUpdate);
|
||||
}
|
||||
this.data.set(CAP_MACHINES, currMachines);
|
||||
}
|
||||
getDeployedDirectories() {
|
||||
return Utils_1.default.copyObject(this.data.get(DEPLOYED_DIRS) || []);
|
||||
}
|
||||
saveDeployedDirectory(directoryToSaveOrUpdate) {
|
||||
const currDirs = this.getDeployedDirectories();
|
||||
let updatedDir = false;
|
||||
for (let index = 0; index < currDirs.length; index++) {
|
||||
const element = currDirs[index];
|
||||
if (element.cwd === directoryToSaveOrUpdate.cwd) {
|
||||
updatedDir = true;
|
||||
currDirs[index] = directoryToSaveOrUpdate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updatedDir) {
|
||||
currDirs.push(directoryToSaveOrUpdate);
|
||||
}
|
||||
this.data.set(DEPLOYED_DIRS, currDirs);
|
||||
}
|
||||
}
|
||||
exports.default = StorageHelper;
|
||||
//# sourceMappingURL=StorageHelper.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"StorageHelper.js","sourceRoot":"","sources":["../../src/utils/StorageHelper.ts"],"names":[],"mappings":"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"}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = {
|
||||
copyObject(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
},
|
||||
generateUuidV4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
getAnsiColorRegex() {
|
||||
const pattern = [
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|');
|
||||
return new RegExp(pattern, 'g');
|
||||
},
|
||||
cleanUpUrl(urlInput) {
|
||||
if (!urlInput || !urlInput.length)
|
||||
return null;
|
||||
let cleanedUrl = urlInput;
|
||||
if (cleanedUrl.indexOf('#') >= 0)
|
||||
cleanedUrl = cleanedUrl.substr(0, cleanedUrl.indexOf('#'));
|
||||
const hasSlashAtTheEnd = cleanedUrl.substr(cleanedUrl.length - 1, 1) === '/';
|
||||
if (hasSlashAtTheEnd) {
|
||||
// Remove the slash at the end
|
||||
cleanedUrl = cleanedUrl.substr(0, cleanedUrl.length - 1);
|
||||
}
|
||||
cleanedUrl = cleanedUrl.replace('http://', '').replace('https://', '').trim();
|
||||
return cleanedUrl;
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=Utils.js.map
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Utils.js","sourceRoot":"","sources":["../../src/utils/Utils.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACd,UAAU,CAAI,GAAM;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAM,CAAC;IAC7C,CAAC;IAED,cAAc;QACb,OAAO,sCAAsC,CAAC,OAAO,CAAC,OAAO,EAAE,UAAS,CAAC;YACxE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAC/B,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;YACrC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,iBAAiB;QAChB,MAAM,OAAO,GAAG;YACf,+EAA+E;YAC/E,0DAA0D;SAC1D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEZ,OAAO,IAAI,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,QAAgB;QAC1B,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAE/C,IAAI,UAAU,GAAG,QAAQ,CAAC;QAE1B,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAE7F,MAAM,gBAAgB,GAAG,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC;QAE7E,IAAI,gBAAgB,EAAE;YACrB,8BAA8B;YAC9B,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;SACzD;QAED,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAE9E,OAAO,UAAU,CAAC;IACnB,CAAC;CACD,CAAC"}
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const StdOutUtil_1 = require("./StdOutUtil");
|
||||
const CliApiManager_1 = require("../api/CliApiManager");
|
||||
const requestLogin_1 = require("../commands/requestLogin");
|
||||
const fs = require('fs-extra');
|
||||
function validateIsGitRepository() {
|
||||
const gitFolderExists = fs.pathExistsSync('./.git');
|
||||
if (!gitFolderExists) {
|
||||
StdOutUtil_1.default.printError('\n**** ERROR: You are not in a git root directory. This command will only deploys the current directory ****\n', true);
|
||||
}
|
||||
return !!gitFolderExists;
|
||||
}
|
||||
exports.validateIsGitRepository = validateIsGitRepository;
|
||||
function validateDefinitionFile() {
|
||||
const captainDefinitionExists = fs.pathExistsSync('./captain-definition');
|
||||
if (!captainDefinitionExists) {
|
||||
StdOutUtil_1.default.printError('\n**** ERROR: captain-definition file cannot be found. Please see docs! ****\n', true);
|
||||
}
|
||||
else {
|
||||
const contents = fs.readFileSync('./captain-definition', 'utf8');
|
||||
let contentsJson = null;
|
||||
try {
|
||||
contentsJson = JSON.parse(contents);
|
||||
}
|
||||
catch (e) {
|
||||
StdOutUtil_1.default.printError(`**** ERROR: captain-definition file is not a valid JSON! ****\n Error:${e}`, true);
|
||||
}
|
||||
if (contentsJson) {
|
||||
if (!contentsJson.schemaVersion) {
|
||||
StdOutUtil_1.default.printError('**** ERROR: captain-definition needs schemaVersion. Please see docs! ****', true);
|
||||
}
|
||||
else if (!contentsJson.templateId && !contentsJson.dockerfileLines) {
|
||||
StdOutUtil_1.default.printError('**** ERROR: captain-definition needs templateId or dockerfileLines. Please see docs! ****', true);
|
||||
}
|
||||
else if (contentsJson.templateId && contentsJson.dockerfileLines) {
|
||||
StdOutUtil_1.default.printError('**** ERROR: captain-definition needs templateId or dockerfileLines, NOT BOTH! Please see docs! ****', true);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.validateDefinitionFile = validateDefinitionFile;
|
||||
function isIpAddress(ipaddress) {
|
||||
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.isIpAddress = isIpAddress;
|
||||
function ensureAuthentication(machine) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let isAuthenticated = false;
|
||||
let allApps = undefined;
|
||||
try {
|
||||
allApps = yield CliApiManager_1.default.get(machine).getAllApps();
|
||||
}
|
||||
catch (e) {
|
||||
// ignore
|
||||
}
|
||||
if (!allApps) {
|
||||
const loggedInStatus = yield requestLogin_1.default(machine);
|
||||
allApps = yield CliApiManager_1.default.get(machine).getAllApps();
|
||||
}
|
||||
return allApps;
|
||||
});
|
||||
}
|
||||
exports.ensureAuthentication = ensureAuthentication;
|
||||
//# sourceMappingURL=ValidationsHandler.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ValidationsHandler.js","sourceRoot":"","sources":["../../src/utils/ValidationsHandler.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,6CAAsC;AAEtC,wDAAiD;AACjD,2DAAoD;AAEpD,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAE/B,SAAgB,uBAAuB;IACtC,MAAM,eAAe,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEpD,IAAI,CAAC,eAAe,EAAE;QACrB,oBAAU,CAAC,UAAU,CACpB,gHAAgH,EAChH,IAAI,CACJ,CAAC;KACF;IAED,OAAO,CAAC,CAAC,eAAe,CAAC;AAC1B,CAAC;AAXD,0DAWC;AAED,SAAgB,sBAAsB;IACrC,MAAM,uBAAuB,GAAG,EAAE,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAC;IAE1E,IAAI,CAAC,uBAAuB,EAAE;QAC7B,oBAAU,CAAC,UAAU,CAAC,gFAAgF,EAAE,IAAI,CAAC,CAAC;KAC9G;SAAM;QACN,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;QACjE,IAAI,YAAY,GAAG,IAAI,CAAC;QAExB,IAAI;YACH,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;SACpC;QAAC,OAAO,CAAC,EAAE;YACX,oBAAU,CAAC,UAAU,CAAC,yEAAyE,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;SAC1G;QAED,IAAI,YAAY,EAAE;YACjB,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE;gBAChC,oBAAU,CAAC,UAAU,CACpB,2EAA2E,EAC3E,IAAI,CACJ,CAAC;aACF;iBAAM,IAAI,CAAC,YAAY,CAAC,UAAU,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE;gBACrE,oBAAU,CAAC,UAAU,CACpB,2FAA2F,EAC3F,IAAI,CACJ,CAAC;aACF;iBAAM,IAAI,YAAY,CAAC,UAAU,IAAI,YAAY,CAAC,eAAe,EAAE;gBACnE,oBAAU,CAAC,UAAU,CACpB,qGAAqG,EACrG,IAAI,CACJ,CAAC;aACF;iBAAM;gBACN,OAAO,IAAI,CAAC;aACZ;SACD;KACD;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAtCD,wDAsCC;AAED,SAAgB,WAAW,CAAC,SAAiB;IAC5C,IACC,kKAAkK,CAAC,IAAI,CACtK,SAAS,CACT,EACA;QACD,OAAO,IAAI,CAAC;KACZ;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAVD,kCAUC;AACD,SAAsB,oBAAoB,CAAC,OAAiB;;QAC3D,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,OAAO,GAAG,SAAS,CAAC;QACxB,IAAI;YACH,OAAO,GAAG,MAAM,uBAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;SACxD;QAAC,OAAO,CAAC,EAAE;YACX,SAAS;SACT;QAED,IAAI,CAAC,OAAO,EAAE;YACb,MAAM,cAAc,GAAG,MAAM,sBAAY,CAAC,OAAO,CAAC,CAAC;YACnD,OAAO,GAAG,MAAM,uBAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;SACxD;QAED,OAAO,OAAO,CAAA;IACf,CAAC;CAAA;AAfD,oDAeC"}
|
||||
@@ -1,748 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const program = require('commander');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
const configstore = require('configstore');
|
||||
const request = require('request');
|
||||
const commandExistsSync = require('command-exists').sync;
|
||||
const ProgressBar = require('progress');
|
||||
const ora = require('ora');
|
||||
const {exec} = require('child_process');
|
||||
|
||||
const packagejson = require('./package.json');
|
||||
|
||||
const configs = new configstore(packagejson.name, {
|
||||
captainMachines: [],
|
||||
apps: []
|
||||
});
|
||||
|
||||
const BRANCH_TO_PUSH = 'branchToPush';
|
||||
const APP_NAME = 'appName';
|
||||
const MACHINE_TO_DEPLOY = 'machineToDeploy';
|
||||
const EMPTY_STRING = '';
|
||||
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
|
||||
program
|
||||
.description('Deploy current directory to a Captain machine.')
|
||||
.option('-t, --tarFile <value>', 'Specify file to be uploaded (rather than using git archive)')
|
||||
.option('-d, --default', 'Run with default options')
|
||||
.option('-s, --stateless', 'Run deploy stateless')
|
||||
.option('-h, --host <value>', 'Only for stateless mode: Host of the captain machine')
|
||||
.option('-a, --appName <value>', 'Only for stateless mode: Name of the app')
|
||||
.option('-p, --pass <value>', 'Only for stateless mode: Password for Captain')
|
||||
.option('-b, --branch <value>', 'Only for stateless mode: Branch name (default master)')
|
||||
.parse(process.argv);
|
||||
|
||||
|
||||
if (program.args.length) {
|
||||
console.error(chalk.red('Unrecognized commands:'));
|
||||
program.args.forEach(function (arg) {
|
||||
console.log(chalk.red(arg));
|
||||
});
|
||||
console.error(chalk.red('Deploy does not require any options. '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function printErrorAndExit(error) {
|
||||
console.log(chalk.bold.red(error));
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function getSuppliedTarFile() {
|
||||
return program.tarFile;
|
||||
}
|
||||
|
||||
if (!getSuppliedTarFile()) {
|
||||
|
||||
if (!fs.pathExistsSync('./.git')) {
|
||||
printErrorAndExit('**** ERROR: You are not in a git root directory. This command will only deploys the current directory ****');
|
||||
}
|
||||
|
||||
if (!fs.pathExistsSync('./captain-definition')) {
|
||||
printErrorAndExit('**** ERROR: captain-definition file cannot be found. Please see docs! ****');
|
||||
}
|
||||
|
||||
const contents = fs.readFileSync('./captain-definition', 'utf8');
|
||||
let contentsJson = null;
|
||||
|
||||
try {
|
||||
contentsJson = JSON.parse(contents);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log('');
|
||||
printErrorAndExit('**** ERROR: captain-definition file is not a valid JSON! ****');
|
||||
}
|
||||
|
||||
if (!contentsJson.schemaVersion) {
|
||||
printErrorAndExit('**** ERROR: captain-definition needs schemaVersion. Please see docs! ****');
|
||||
}
|
||||
|
||||
if (!contentsJson.templateId && !contentsJson.dockerfileLines) {
|
||||
printErrorAndExit('**** ERROR: captain-definition needs templateId or dockerfileLines. Please see docs! ****');
|
||||
}
|
||||
|
||||
if (contentsJson.templateId && contentsJson.dockerfileLines) {
|
||||
printErrorAndExit('**** ERROR: captain-definition needs templateId or dockerfileLines, NOT BOTH! Please see docs! ****');
|
||||
}
|
||||
}
|
||||
|
||||
let listOfMachines = [{
|
||||
name: '-- CANCEL --',
|
||||
value: EMPTY_STRING,
|
||||
short: EMPTY_STRING
|
||||
}];
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
let m = machines[i];
|
||||
listOfMachines.push({
|
||||
name: m.name + ' at ' + m.baseUrl,
|
||||
value: m.name,
|
||||
short: m.name + ' at ' + m.baseUrl
|
||||
})
|
||||
}
|
||||
|
||||
// Gets default value for propType that is stored in a directory.
|
||||
// Replaces getAppForDirectory
|
||||
function getPropForDirectory(propType) {
|
||||
let apps = configs.get('apps');
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
if (app.cwd === process.cwd()) {
|
||||
return app[propType];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Sets default value for propType that is stored in a directory to propValue.
|
||||
// Replaces saveAppForDirectory
|
||||
function savePropForDirectory(propType, propValue) {
|
||||
let apps = configs.get('apps');
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
if (app.cwd === process.cwd()) {
|
||||
app[propType] = propValue;
|
||||
configs.set('apps', apps);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
apps.push({
|
||||
cwd: process.cwd(),
|
||||
[propType]: propValue
|
||||
});
|
||||
|
||||
configs.set('apps', apps);
|
||||
}
|
||||
|
||||
|
||||
function getDefaultMachine() {
|
||||
let machine = getPropForDirectory(MACHINE_TO_DEPLOY);
|
||||
if (machine) {
|
||||
return machine.name;
|
||||
}
|
||||
if (listOfMachines.length == 2) {
|
||||
return 1;
|
||||
}
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
console.log('Preparing deployment to Captain...');
|
||||
console.log(' ');
|
||||
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'captainNameToDeploy',
|
||||
default: getDefaultMachine(),
|
||||
message: 'Select the Captain Machine you want to deploy to:',
|
||||
choices: listOfMachines
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default: getPropForDirectory(BRANCH_TO_PUSH) || 'master',
|
||||
name: BRANCH_TO_PUSH,
|
||||
message: 'Enter the "git" branch you would like to deploy:',
|
||||
when: function (answers) {
|
||||
return !!answers.captainNameToDeploy;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default: getPropForDirectory(APP_NAME),
|
||||
name: APP_NAME,
|
||||
message: 'Enter the Captain app name this directory will be deployed to:',
|
||||
when: function (answers) {
|
||||
return !!answers.captainNameToDeploy;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmedToDeploy',
|
||||
message: 'Note that uncommitted files and files in gitignore (if any) will not be pushed to server. Please confirm so that deployment process can start.',
|
||||
default: true,
|
||||
when: function (answers) {
|
||||
return !!answers.captainNameToDeploy;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
let defaultInvalid = false;
|
||||
|
||||
if (program.default) {
|
||||
|
||||
if (!getDefaultMachine() || !getPropForDirectory(BRANCH_TO_PUSH) || !getPropForDirectory(APP_NAME)) {
|
||||
console.log('Default deploy failed. Please select deploy options.');
|
||||
defaultInvalid = true;
|
||||
}
|
||||
else {
|
||||
console.log('Deploying to ' + getPropForDirectory(MACHINE_TO_DEPLOY).name);
|
||||
deployTo(getPropForDirectory(MACHINE_TO_DEPLOY), getPropForDirectory(BRANCH_TO_PUSH), getPropForDirectory(APP_NAME));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const isStateless = program.stateless && program.host && program.appName && program.pass;
|
||||
|
||||
if (isStateless) {
|
||||
// login first
|
||||
console.log('Trying to login to', program.host)
|
||||
requestLoginAuth(program.host, program.pass, function (authToken) {
|
||||
// deploy
|
||||
console.log('Starting stateless deploy to', program.host, program.branch, program.appName);
|
||||
deployTo({
|
||||
baseUrl: program.host,
|
||||
authToken,
|
||||
}, program.branch || 'master', program.appName);
|
||||
});
|
||||
}
|
||||
else if (!program.default || defaultInvalid) {
|
||||
|
||||
inquirer.prompt(questions).then(function (answers) {
|
||||
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
|
||||
if (!answers.confirmedToDeploy) {
|
||||
console.log('Operation cancelled by the user...');
|
||||
console.log(' ');
|
||||
}
|
||||
else {
|
||||
let machines = configs.get('captainMachines');
|
||||
let machineToDeploy = null;
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
if (machines[i].name === answers.captainNameToDeploy) {
|
||||
console.log('Deploying to ' + answers.captainNameToDeploy);
|
||||
machineToDeploy = machines[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ');
|
||||
deployTo(machineToDeploy, answers.branchToPush, answers.appName);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function deployTo(machineToDeploy, branchToPush, appName) {
|
||||
|
||||
function checkAuthAndSendFile(zipFileFullPath, gitHash) {
|
||||
function isAuthTokenValidCallback(isValid) {
|
||||
if (isValid) {
|
||||
sendFileToCaptain(machineToDeploy, zipFileFullPath, appName, gitHash, branchToPush);
|
||||
}
|
||||
else {
|
||||
|
||||
requestLogin(machineToDeploy.name, machineToDeploy.baseUrl, function callback(machineToDeployNew) {
|
||||
|
||||
deployTo(machineToDeployNew, branchToPush, appName);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isAuthTokenValid(machineToDeploy, appName, isAuthTokenValidCallback);
|
||||
}
|
||||
|
||||
if (getSuppliedTarFile()) {
|
||||
checkAuthAndSendFile(path.join(process.cwd(), getSuppliedTarFile()), 'sendviatarfile');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!commandExistsSync('git')) {
|
||||
console.log(chalk.red('"git" command not found...'));
|
||||
console.log(chalk.red('Captain needs "git" to create tar file of your source files...'));
|
||||
console.log(' ');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let zipFileNameToDeploy = 'temporary-captain-to-deploy.tar';
|
||||
let zipFileFullPath = path.join(process.cwd(), zipFileNameToDeploy);
|
||||
|
||||
console.log('Saving tar file to:');
|
||||
console.log(zipFileFullPath);
|
||||
console.log(' ');
|
||||
|
||||
try {
|
||||
fs.removeSync(zipFileFullPath);
|
||||
} catch (ignoreError) {
|
||||
|
||||
}
|
||||
|
||||
exec('git archive --format tar --output "' + zipFileFullPath + '" ' + branchToPush, function (err, stdout, stderr) {
|
||||
if (err) {
|
||||
console.log(chalk.red('TAR file failed'));
|
||||
console.log(chalk.red(err + ' '));
|
||||
console.log(' ');
|
||||
fs.removeSync(zipFileFullPath);
|
||||
return;
|
||||
}
|
||||
|
||||
exec('git rev-parse ' + branchToPush, function (err, stdout, stderr) {
|
||||
|
||||
const gitHash = (stdout || '').trim();
|
||||
|
||||
if (err || !(/^[a-f0-9]{40}$/.test(gitHash))) {
|
||||
console.log(chalk.red('Cannot find hash of last commit on this branch: ' + branchToPush));
|
||||
console.log(chalk.red(gitHash + ' '));
|
||||
console.log(chalk.red(err + ' '));
|
||||
console.log(' ');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Pushing last commit on ' + branchToPush + ': ' + gitHash);
|
||||
|
||||
checkAuthAndSendFile(zipFileFullPath, gitHash);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function sendFileToCaptain(machineToDeploy, zipFileFullPath, appName, gitHash, branchToPush) {
|
||||
|
||||
console.log('Uploading file to ' + machineToDeploy.baseUrl);
|
||||
|
||||
const fileSize = fs.statSync(zipFileFullPath).size;
|
||||
const fileStream = fs.createReadStream(zipFileFullPath);
|
||||
|
||||
const barOpts = {
|
||||
width: 20,
|
||||
total: fileSize,
|
||||
clear: true
|
||||
};
|
||||
const bar = new ProgressBar(' uploading [:bar] :percent (ETA :etas)', barOpts);
|
||||
fileStream.on('data', function (chunk) {
|
||||
bar.tick(chunk.length);
|
||||
});
|
||||
|
||||
let spinner;
|
||||
|
||||
fileStream.on('end', function () {
|
||||
console.log(' ');
|
||||
console.log('This might take several minutes. PLEASE BE PATIENT...');
|
||||
spinner = ora('Building your source code...').start();
|
||||
spinner.color = 'yellow';
|
||||
});
|
||||
|
||||
|
||||
let options = {
|
||||
url: machineToDeploy.baseUrl + '/api/v1/user/appData/' + appName + '/?detached=1',
|
||||
headers: {
|
||||
'x-namespace': 'captain',
|
||||
'x-captain-auth': machineToDeploy.authToken
|
||||
},
|
||||
method: 'POST',
|
||||
formData: {
|
||||
sourceFile: fileStream,
|
||||
gitHash: gitHash
|
||||
}
|
||||
};
|
||||
|
||||
function callback(error, response, body) {
|
||||
|
||||
if (spinner) {
|
||||
spinner.stop();
|
||||
}
|
||||
|
||||
if (fs.pathExistsSync(zipFileFullPath)) {
|
||||
if (!getSuppliedTarFile()) {
|
||||
fs.removeSync(zipFileFullPath);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status === 1106) {
|
||||
// expired token
|
||||
requestLogin(machineToDeploy.name, machineToDeploy.baseUrl, function callback(machineToDeployNew) {
|
||||
|
||||
deployTo(machineToDeployNew, branchToPush, appName);
|
||||
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.status !== 100 && data.status !== 101) {
|
||||
throw new Error(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
savePropForDirectory(APP_NAME, appName);
|
||||
savePropForDirectory(BRANCH_TO_PUSH, branchToPush);
|
||||
savePropForDirectory(MACHINE_TO_DEPLOY, machineToDeploy);
|
||||
|
||||
if (data.status === 100) {
|
||||
console.log(chalk.green('Deployed successfully: ') + appName);
|
||||
console.log(' ');
|
||||
} else if (data.status === 101) {
|
||||
console.log(chalk.green('Building started: ') + appName);
|
||||
console.log(' ');
|
||||
startFetchingBuildLogs(machineToDeploy, appName);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
throw new Error(response ? JSON.stringify(response, null, 2) : 'Response NULL');
|
||||
|
||||
} catch (error) {
|
||||
|
||||
console.error(chalk.red('\nSomething bad happened. Cannot deploy "' + appName + '"\n'));
|
||||
|
||||
if (error.message) {
|
||||
try {
|
||||
var errorObj = JSON.parse(error.message);
|
||||
if (errorObj.status) {
|
||||
console.error(chalk.red('\nError code: ' + errorObj.status));
|
||||
console.error(chalk.red('\nError message:\n\n ' + errorObj.description));
|
||||
} else {
|
||||
throw new Error("NOT API ERROR");
|
||||
}
|
||||
} catch (ignoreError) {
|
||||
console.error(chalk.red(error.message));
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red(error));
|
||||
}
|
||||
console.log(' ');
|
||||
}
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
|
||||
}
|
||||
|
||||
var lastLineNumberPrinted = -10000; // we want to show all lines to begin with!
|
||||
|
||||
function startFetchingBuildLogs(machineToDeploy, appName) {
|
||||
|
||||
let options = {
|
||||
url: machineToDeploy.baseUrl + '/api/v1/user/appData/' + appName,
|
||||
headers: {
|
||||
'x-namespace': 'captain',
|
||||
'x-captain-auth': machineToDeploy.authToken
|
||||
},
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
function onLogRetrieved(data) {
|
||||
|
||||
if (data) {
|
||||
var lines = data.logs.lines;
|
||||
var firstLineNumberOfLogs = data.logs.firstLineNumber;
|
||||
var firstLinesToPrint = 0;
|
||||
if (firstLineNumberOfLogs > lastLineNumberPrinted) {
|
||||
|
||||
if (firstLineNumberOfLogs < 0) {
|
||||
// This is the very first fetch, probably firstLineNumberOfLogs is around -50
|
||||
firstLinesToPrint = -firstLineNumberOfLogs;
|
||||
} else {
|
||||
console.log('[[ TRUNCATED ]]');
|
||||
}
|
||||
|
||||
} else {
|
||||
firstLinesToPrint = lastLineNumberPrinted - firstLineNumberOfLogs;
|
||||
}
|
||||
|
||||
lastLineNumberPrinted = firstLineNumberOfLogs + lines.length;
|
||||
|
||||
for (var i = firstLinesToPrint; i < lines.length; i++) {
|
||||
console.log((lines[i] || '').trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (data && !data.isAppBuilding) {
|
||||
console.log(' ');
|
||||
if (!data.isBuildFailed) {
|
||||
console.log(chalk.green('Deployed successfully: ') + appName);
|
||||
console.log(chalk.magenta('App is available at ')
|
||||
+ (machineToDeploy.baseUrl.replace('//captain.', '//' + appName + '.').replace('https://', 'http://'))
|
||||
);
|
||||
} else {
|
||||
console.error(chalk.red('\nSomething bad happened. Cannot deploy "' + appName + '"\n'));
|
||||
}
|
||||
console.log(' ');
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
startFetchingBuildLogs(machineToDeploy, appName);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
|
||||
function callback(error, response, body) {
|
||||
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status !== 100) {
|
||||
throw new Error(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
onLogRetrieved(data.data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
throw new Error(response ? JSON.stringify(response, null, 2) : 'Response NULL');
|
||||
|
||||
} catch (error) {
|
||||
|
||||
console.error(chalk.red('\nSomething while retrieving app build logs.. "' + error + '"\n'));
|
||||
|
||||
onLogRetrieved(null);
|
||||
}
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
}
|
||||
|
||||
function isAuthTokenValid(machineToDeploy, appName, isAuthTokenValidCallback) {
|
||||
|
||||
let options = {
|
||||
url: machineToDeploy.baseUrl + '/api/v1/user/appDefinitions/',
|
||||
headers: {
|
||||
'x-namespace': 'captain',
|
||||
'x-captain-auth': machineToDeploy.authToken
|
||||
},
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
|
||||
function callback(error, response, body) {
|
||||
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status === 1106 || data.status === 1105) {
|
||||
isAuthTokenValidCallback(false);
|
||||
}
|
||||
else {
|
||||
isAuthTokenValidCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// This is just a sanity check. We only fire FALSE (i.e. expired) if we know it's expired or password is wrong
|
||||
isAuthTokenValidCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
}
|
||||
|
||||
function requestLoginAuth(serverAddress, password, authCallback) {
|
||||
let options = {
|
||||
url: serverAddress + '/api/v1/login',
|
||||
headers: {
|
||||
'x-namespace': 'captain'
|
||||
},
|
||||
method: 'POST',
|
||||
form: {
|
||||
password: password
|
||||
}
|
||||
};
|
||||
|
||||
function callback(error, response, body) {
|
||||
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status !== 100) {
|
||||
throw new Error(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
authCallback(data.token);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
throw new Error(response ? JSON.stringify(response, null, 2) : 'Response NULL');
|
||||
|
||||
} catch (error) {
|
||||
|
||||
if (error.message) {
|
||||
try {
|
||||
var errorObj = JSON.parse(error.message);
|
||||
if (errorObj.status) {
|
||||
console.error(chalk.red('\nError code: ' + errorObj.status));
|
||||
console.error(chalk.red('\nError message:\n\n ' + errorObj.description));
|
||||
} else {
|
||||
throw new Error("NOT API ERROR");
|
||||
}
|
||||
} catch (ignoreError) {
|
||||
console.error(chalk.red(error.message));
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red(error));
|
||||
}
|
||||
console.log(' ');
|
||||
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
}
|
||||
|
||||
function requestLogin(serverName, serverAddress, loginCallback) {
|
||||
|
||||
console.log('Your auth token is not valid anymore. Try to login again.');
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainPassword',
|
||||
message: 'Please enter your password for ' + serverAddress,
|
||||
validate: function (value) {
|
||||
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ('Please enter your password for ' + serverAddress);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function updateAuthTokenInConfigStoreAndReturn(authToken) {
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
if (machines[i].name === serverName) {
|
||||
var baseUrl = machines[i].authToken = authToken;
|
||||
configs.set('captainMachines', machines);
|
||||
console.log('You are now logged back in to ' + serverAddress);
|
||||
return machines[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inquirer.prompt(questions).then(function (passwordAnswers) {
|
||||
|
||||
var password = passwordAnswers.captainPassword;
|
||||
|
||||
let options = {
|
||||
url: serverAddress + '/api/v1/login',
|
||||
headers: {
|
||||
'x-namespace': 'captain'
|
||||
},
|
||||
method: 'POST',
|
||||
form: {
|
||||
password: password
|
||||
}
|
||||
};
|
||||
|
||||
function callback(error, response, body) {
|
||||
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status !== 100) {
|
||||
throw new Error(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
var newMachineToDeploy = updateAuthTokenInConfigStoreAndReturn(data.token);
|
||||
|
||||
loginCallback(newMachineToDeploy);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
throw new Error(response ? JSON.stringify(response, null, 2) : 'Response NULL');
|
||||
|
||||
} catch (error) {
|
||||
|
||||
if (error.message) {
|
||||
try {
|
||||
var errorObj = JSON.parse(error.message);
|
||||
if (errorObj.status) {
|
||||
console.error(chalk.red('\nError code: ' + errorObj.status));
|
||||
console.error(chalk.red('\nError message:\n\n ' + errorObj.description));
|
||||
} else {
|
||||
throw new Error("NOT API ERROR");
|
||||
}
|
||||
} catch (ignoreError) {
|
||||
console.error(chalk.red(error.message));
|
||||
}
|
||||
} else {
|
||||
console.error(chalk.red(error));
|
||||
}
|
||||
console.log(' ');
|
||||
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
const program = require('commander');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
const configstore = require('configstore');
|
||||
const request = require('request');
|
||||
|
||||
const packagejson = require('./package.json');
|
||||
|
||||
|
||||
const configs = new configstore(packagejson.name, {
|
||||
captainMachines: []
|
||||
});
|
||||
|
||||
|
||||
program
|
||||
.description('List all Captain machines currently logged in.')
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.args.length) {
|
||||
console.error(chalk.red('Unrecognized commands:'));
|
||||
program.args.forEach(function (arg) {
|
||||
console.log(chalk.red(arg));
|
||||
});
|
||||
console.error(chalk.red('List does not require any options. '));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log();
|
||||
console.log('Logged in Captain Machines:');
|
||||
console.log();
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
|
||||
console.log('>> ' + chalk.greenBright(machines[i].name) + ' at ' + chalk.cyan(machines[i].baseUrl));
|
||||
|
||||
}
|
||||
console.log();
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
const program = require('commander');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
const configstore = require('configstore');
|
||||
const request = require('request');
|
||||
|
||||
const packagejson = require('./package.json');
|
||||
|
||||
|
||||
const configs = new configstore(packagejson.name, {
|
||||
captainMachines: []
|
||||
});
|
||||
|
||||
|
||||
program
|
||||
.description('Login to a CaptainDuckDuck machine. You can be logged in to multiple machines simultaneously.')
|
||||
.parse(process.argv);
|
||||
|
||||
|
||||
function cleanUpUrl(url) {
|
||||
if (!url) {
|
||||
return url;
|
||||
}
|
||||
url = url.trim();
|
||||
url = url.replace('http://', '').replace('https://', '');
|
||||
if (!url || !url.length) {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (url.substr(url.length - 1, 1) === '/') {
|
||||
url = url.substr(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
if (program.args.length) {
|
||||
console.error(chalk.red('Unrecognized commands:'));
|
||||
program.args.forEach(function (arg) {
|
||||
console.log(chalk.red(arg));
|
||||
});
|
||||
console.error(chalk.red('Login does not require any options. '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function findDefaultCaptainName() {
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
let currentSuffix = machines.length + 1;
|
||||
|
||||
function getCaptainFullName(suffix) {
|
||||
if (suffix < 10) {
|
||||
suffix = '0' + suffix;
|
||||
}
|
||||
return 'captain-' + suffix;
|
||||
}
|
||||
|
||||
function isSuffixValid(suffixNumber) {
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
let m = machines[i];
|
||||
if (m.name === getCaptainFullName(suffixNumber)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
while (!isSuffixValid(currentSuffix)) {
|
||||
currentSuffix++;
|
||||
}
|
||||
|
||||
return getCaptainFullName(currentSuffix);
|
||||
}
|
||||
|
||||
console.log('Login to a Captain Machine');
|
||||
|
||||
const SAMPLE_DOMAIN = 'captain.captainroot.yourdomain.com';
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'input',
|
||||
default: SAMPLE_DOMAIN,
|
||||
name: 'captainAddress',
|
||||
message: 'Enter address of the Captain machine. \nIt is captain.[your-captain-root-domain] :',
|
||||
validate: function (value) {
|
||||
|
||||
if (value===SAMPLE_DOMAIN){
|
||||
return 'Enter a valid URL';
|
||||
}
|
||||
|
||||
if (!cleanUpUrl(value)) {
|
||||
return 'This is an invalid URL: ' + value;
|
||||
}
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
if (cleanUpUrl(machines[i].baseUrl) === cleanUpUrl(value)) {
|
||||
return value + ' already exist as ' + machines[i].name + '. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.'
|
||||
}
|
||||
}
|
||||
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid address.';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'captainHasRootSsl',
|
||||
message: 'Is HTTPS activated for this Captain machine?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainPassword',
|
||||
message: 'Enter your password:',
|
||||
validate: function (value) {
|
||||
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter your password.';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainName',
|
||||
message: 'Enter a name for this Captain machine:',
|
||||
default: findDefaultCaptainName(),
|
||||
validate: function (value) {
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
if (machines[i].name === value) {
|
||||
return value + ' already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.'
|
||||
}
|
||||
}
|
||||
|
||||
if (value.match(/^[-\d\w]+$/i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a Captain Name.';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
inquirer.prompt(questions).then(function (answers) {
|
||||
|
||||
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
|
||||
let baseUrl = (answers.captainHasRootSsl ? 'https://' : 'http://') + cleanUpUrl(answers.captainAddress);
|
||||
|
||||
let options = {
|
||||
url: baseUrl + '/api/v1/login',
|
||||
headers: {
|
||||
'x-namespace': 'captain'
|
||||
},
|
||||
method: 'POST',
|
||||
form: {
|
||||
password: answers.captainPassword
|
||||
}
|
||||
};
|
||||
|
||||
function setCaptainMachine(data) {
|
||||
let machines = configs.get('captainMachines');
|
||||
machines.push(data);
|
||||
configs.set('captainMachines', machines);
|
||||
}
|
||||
|
||||
function callback(error, response, body) {
|
||||
|
||||
let captainNameEnterByUser = answers.captainName;
|
||||
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status !== 100) {
|
||||
throw new Error(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
console.log(chalk.green('Logged in successfully to ') + baseUrl);
|
||||
console.log(chalk.green('Authorization token is now saved as ' + captainNameEnterByUser));
|
||||
console.log(' ');
|
||||
|
||||
|
||||
setCaptainMachine({
|
||||
authToken: data.token,
|
||||
baseUrl: baseUrl,
|
||||
name: captainNameEnterByUser
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
throw new Error(response ? JSON.stringify(response, null, 2) : 'Response NULL');
|
||||
|
||||
} catch (error) {
|
||||
console.error(chalk.red('Something bad happened. Cannot save "' + captainNameEnterByUser + '"'));
|
||||
let errorMessage = error.message ? error.message : error;
|
||||
console.error(chalk.red(errorMessage));
|
||||
console.log(' ');
|
||||
}
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
const program = require('commander');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
const configstore = require('configstore');
|
||||
const request = require('request');
|
||||
|
||||
const packagejson = require('./package.json');
|
||||
|
||||
|
||||
const configs = new configstore(packagejson.name, {
|
||||
captainMachines: []
|
||||
});
|
||||
|
||||
|
||||
program
|
||||
.description('Logout from a specific Captain machine.')
|
||||
.parse(process.argv);
|
||||
|
||||
|
||||
if (program.args.length) {
|
||||
console.error(chalk.red('Unrecognized commands:'));
|
||||
program.args.forEach(function (arg) {
|
||||
console.log(chalk.red(arg));
|
||||
});
|
||||
console.error(chalk.red('Logout does not require any options. '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let listOfMachines = [{
|
||||
name: '-- CANCEL --',
|
||||
value: '',
|
||||
short: ''
|
||||
}];
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
let m = machines[i];
|
||||
listOfMachines.push({
|
||||
name: m.name + ' at ' + m.baseUrl,
|
||||
value: m.name,
|
||||
short: m.name + ' at ' + m.baseUrl
|
||||
})
|
||||
}
|
||||
|
||||
console.log('Logout from a Captain Machine and clear auth info');
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'captainNameToLogout',
|
||||
message: 'Select the Captain Machine you want to logout from:',
|
||||
choices: listOfMachines
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmedToLogout',
|
||||
message: 'Are you sure you want to logout from this Captain machine?',
|
||||
default: false,
|
||||
when: function (answers) {
|
||||
return !!answers.captainNameToLogout;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
inquirer.prompt(questions).then(function (answers) {
|
||||
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
|
||||
if (!answers.captainNameToLogout) {
|
||||
console.log('Operation cancelled by the user...');
|
||||
}
|
||||
else {
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
if (machines[i].name === answers.captainNameToLogout) {
|
||||
var baseUrl = machines[i].baseUrl;
|
||||
machines.splice(i,1);
|
||||
configs.set('captainMachines', machines);
|
||||
console.log('You are now logged out from ' + answers.captainNameToLogout + ' at ' + baseUrl + '...');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(' ');
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,466 +0,0 @@
|
||||
const program = require('commander');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
const configstore = require('configstore');
|
||||
const request = require('request');
|
||||
const spinnerUtil = require('./utils/spinner');
|
||||
|
||||
const packagejson = require('./package.json');
|
||||
|
||||
|
||||
const configs = new configstore(packagejson.name, {
|
||||
captainMachines: []
|
||||
});
|
||||
|
||||
|
||||
program
|
||||
.description('Easy setup for your Captain server.')
|
||||
.parse(process.argv);
|
||||
|
||||
|
||||
if (program.args.length) {
|
||||
console.error(chalk.red('Unrecognized commands:'));
|
||||
program.args.forEach(function (arg) {
|
||||
console.log(chalk.red(arg));
|
||||
});
|
||||
console.error(chalk.red('This command does not require any options. '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function findDefaultCaptainName() {
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
let currentSuffix = machines.length + 1;
|
||||
|
||||
function getCaptainFullName(suffix) {
|
||||
if (suffix < 10) {
|
||||
suffix = '0' + suffix;
|
||||
}
|
||||
return 'captain-' + suffix;
|
||||
}
|
||||
|
||||
function isSuffixValid(suffixNumber) {
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
let m = machines[i];
|
||||
if (m.name === getCaptainFullName(suffixNumber)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
while (!isSuffixValid(currentSuffix)) {
|
||||
currentSuffix++;
|
||||
}
|
||||
|
||||
return getCaptainFullName(currentSuffix);
|
||||
}
|
||||
|
||||
function isIpAddress(ipaddress) {
|
||||
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress)) {
|
||||
return (true)
|
||||
}
|
||||
return (false)
|
||||
}
|
||||
|
||||
|
||||
console.log(' ');
|
||||
console.log('Setup your Captain server');
|
||||
console.log(' ');
|
||||
|
||||
const SAMPLE_IP = '123.123.123.123';
|
||||
|
||||
var authTokenFromLogin = null;
|
||||
var ipAddressOfServer = null;
|
||||
var customDomainFromUser = null;
|
||||
var newPasswordFirstTry = null;
|
||||
var oldPassword = 'captain42';
|
||||
|
||||
function setCustomDomain(baseApiUrl, customDomain) {
|
||||
|
||||
return apiEndpointWithLoading('Changing Domain...')('POST', baseApiUrl, '/api/v1/user/system/changerootdomain/', {
|
||||
rootDomain: customDomain
|
||||
});
|
||||
}
|
||||
|
||||
function enableHttps(baseApiUrl, emailAddress) {
|
||||
|
||||
return apiEndpointWithLoading('Enabling SSL...')('POST', baseApiUrl, '/api/v1/user/system/enablessl/', {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function changePass(baseApiUrl, newPass) {
|
||||
|
||||
return apiEndpointWithLoading('Changing Password...')('POST', baseApiUrl, '/api/v1/user/changepassword/', {
|
||||
oldPassword: oldPassword,
|
||||
newPassword: newPass
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function forceHttps(baseApiUrl) {
|
||||
|
||||
return apiEndpointWithLoading('Forcing SSL...')('POST', baseApiUrl, '/api/v1/user/system/forcessl/', {
|
||||
isEnabled: true
|
||||
});
|
||||
}
|
||||
|
||||
// returns promise that resolves to auth token
|
||||
// rejects with ErrorCode or null
|
||||
function login(baseApiUrl, password) {
|
||||
|
||||
return apiEndpointWithLoading('Login...')('POST', baseApiUrl, '/api/v1/login', {
|
||||
password: password
|
||||
}).then(function (data) {
|
||||
return data.token;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function apiEndpointWithLoading(message) {
|
||||
return (method, baseApiUrl, endpoint, dataToSend) => {
|
||||
return apiEndpoint(method, baseApiUrl, endpoint, dataToSend, message)
|
||||
}
|
||||
}
|
||||
|
||||
function apiEndpoint(method, baseApiUrl, endpoint, dataToSend, message) {
|
||||
|
||||
let options = {
|
||||
url: baseApiUrl + endpoint,
|
||||
headers: {
|
||||
'x-namespace': 'captain',
|
||||
'x-captain-auth': authTokenFromLogin
|
||||
},
|
||||
method: method,
|
||||
form: dataToSend
|
||||
};
|
||||
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
let spinner;
|
||||
if (message) {
|
||||
spinner = spinnerUtil.start(message);
|
||||
}
|
||||
var callback = function (error, response, body) {
|
||||
try {
|
||||
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
let data = JSON.parse(body);
|
||||
|
||||
if (data.status !== 100) {
|
||||
if (spinner) {
|
||||
spinnerUtil.fail(spinner);
|
||||
}
|
||||
rej(data);
|
||||
return;
|
||||
}
|
||||
if (spinner) {
|
||||
spinnerUtil.succeed(spinner);
|
||||
}
|
||||
res(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
throw new Error(response ? JSON.stringify(response, null, 2) : 'Response NULL');
|
||||
|
||||
} catch (error) {
|
||||
if (spinner) {
|
||||
spinnerUtil.fail(spinner);
|
||||
}
|
||||
console.error(chalk.red('Something bad happened. Cannot connect to "' + baseApiUrl + endpoint + '"'));
|
||||
let errorMessage = error.message ? error.message : error;
|
||||
console.error(chalk.red(errorMessage));
|
||||
console.log(' ');
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
request(options, callback);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'hasInstalledCaptain',
|
||||
message: 'Have you already installed Captain on your server by running the following line:'
|
||||
+ '\nmkdir /captain && docker run -v /var/run/docker.sock:/var/run/docker.sock dockersaturn/captainduckduck ?',
|
||||
default: 'Yes',
|
||||
choices: ['Yes', 'No'],
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
if (value === 'Yes') {
|
||||
res(true);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('');
|
||||
console.log(' Cannot start the setup process if Captain is not installed.');
|
||||
console.log(' Please read tutorial on CaptainDuckDuck.com'
|
||||
+ ' to learn how to install CaptainDuckDuck on a server.');
|
||||
console.log('');
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default: SAMPLE_IP,
|
||||
name: 'captainAddress',
|
||||
message: 'Enter IP address of your captain server:',
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
if (value === SAMPLE_IP) {
|
||||
rej('Enter a valid IP Address');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isIpAddress(value.trim())) {
|
||||
rej('This is an invalid IP Address: ' + value);
|
||||
return;
|
||||
}
|
||||
|
||||
ipAddressOfServer = value.trim();
|
||||
|
||||
//login using captain42.
|
||||
|
||||
return login('http://' + ipAddressOfServer + ':3000', oldPassword)
|
||||
.then(function (authTokenFetched) {
|
||||
authTokenFromLogin = authTokenFetched;
|
||||
res(ipAddressOfServer);
|
||||
})
|
||||
.catch(function (error) {
|
||||
|
||||
// if error is anything but password wrong, exit here...
|
||||
if (error.status == 1105) {
|
||||
authTokenFromLogin = null;
|
||||
res(ipAddressOfServer);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
if (error.status) {
|
||||
console.log(chalk.red('Error: ' + error.status));
|
||||
console.log(chalk.red('Error: ' + error.description));
|
||||
} else {
|
||||
console.log(chalk.red('Error: ' + error));
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainOriginalPassword',
|
||||
message: 'Enter your current password:',
|
||||
when: function () {
|
||||
return !authTokenFromLogin;
|
||||
},
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
console.log('');
|
||||
|
||||
return login('http://' + ipAddressOfServer + ':3000', value)
|
||||
.then(function (authTokenFetched) {
|
||||
authTokenFromLogin = authTokenFetched;
|
||||
oldPassword = value;
|
||||
res(value);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('');
|
||||
|
||||
if (error.status) {
|
||||
console.log(chalk.red('Error: ' + error.status));
|
||||
console.log(chalk.red('Error: ' + error.description));
|
||||
} else {
|
||||
console.log(chalk.red('Error: ' + error));
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainRootDomain',
|
||||
message: 'Enter a root domain for this Captain server. For example, enter test.yourdomain.com if you' +
|
||||
' setup your DNS to point *.test.yourdomain.com to ip address of your server' + ': ',
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
value = value.trim();
|
||||
|
||||
customDomainFromUser = 'captain.' + value;
|
||||
|
||||
return setCustomDomain('http://' + ipAddressOfServer + ':3000', value)
|
||||
.then(function () {
|
||||
res(customDomainFromUser);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('');
|
||||
if (error.status) {
|
||||
console.log(chalk.red('Error: ' + error.status));
|
||||
console.log(chalk.red('Error: ' + error.description));
|
||||
} else {
|
||||
console.log(chalk.red('Error: ' + error));
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'emailAddress',
|
||||
message: 'Enter your "valid" email address to enable HTTPS: ',
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
console.log('');
|
||||
value = value.trim();
|
||||
|
||||
return enableHttps('http://' + customDomainFromUser, value)
|
||||
.then(function () {
|
||||
return forceHttps('https://' + customDomainFromUser);
|
||||
})
|
||||
.then(function () {
|
||||
res(value);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('');
|
||||
if (error.status) {
|
||||
console.log(chalk.red('Error: ' + error.status));
|
||||
console.log(chalk.red('Error: ' + error.description));
|
||||
} else {
|
||||
console.log(chalk.red('Error: ' + error));
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'newPasswordFirstTry',
|
||||
message: 'Enter a new password:',
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
newPasswordFirstTry = value;
|
||||
res(value);
|
||||
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'newPassword',
|
||||
message: 'Enter a new password:',
|
||||
filter: function (value) {
|
||||
return new Promise(function (res, rej) {
|
||||
|
||||
if (newPasswordFirstTry !== value) {
|
||||
rej('Passwords do not match');
|
||||
//process.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
return changePass('https://' + customDomainFromUser, value)
|
||||
.then(function () {
|
||||
return login('https://' + customDomainFromUser, value)
|
||||
})
|
||||
.then(function (token) {
|
||||
authTokenFromLogin = token;
|
||||
res(value);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('');
|
||||
if (error.status) {
|
||||
console.log(chalk.red('Error: ' + error.status));
|
||||
console.log(chalk.red('Error: ' + error.description));
|
||||
} else {
|
||||
console.log(chalk.red('Error: ' + error));
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log(chalk.red('IMPORTANT!! Server setup is completed by password is not changed.'));
|
||||
console.log(chalk.red('You CANNOT use serversetup anymore. To continue: '));
|
||||
console.log(chalk.red('- Go to https://' + customDomainFromUser
|
||||
+ ' login with default password and change the password in settings.'));
|
||||
console.log(chalk.red('- In terminal (here), type captainduckduck login and enter this as your root domain: ' + customDomainFromUser));
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainName',
|
||||
message: 'Enter a name for this Captain machine:',
|
||||
default: findDefaultCaptainName(),
|
||||
validate: function (value) {
|
||||
|
||||
let machines = configs.get('captainMachines');
|
||||
for (let i = 0; i < machines.length; i++) {
|
||||
if (machines[i].name === value) {
|
||||
return value + ' already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.'
|
||||
}
|
||||
}
|
||||
|
||||
if (value.match(/^[-\d\w]+$/i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a Captain Name.';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
inquirer.prompt(questions).then(function (answers) {
|
||||
|
||||
|
||||
var captainAddress = 'https://' + customDomainFromUser;
|
||||
let machines = configs.get('captainMachines');
|
||||
machines.push({
|
||||
authToken: authTokenFromLogin,
|
||||
baseUrl: captainAddress,
|
||||
name: answers.captainName
|
||||
});
|
||||
configs.set('captainMachines', machines);
|
||||
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.log('Captain is available at ' + captainAddress);
|
||||
console.log(' ');
|
||||
console.log('For more details and docs see http://www.captainduckduck.com');
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const packagejson = require('./package.json');
|
||||
const updateNotifier = require('update-notifier');
|
||||
|
||||
updateNotifier({ pkg: packagejson }).notify({ isGlobal: true });
|
||||
|
||||
|
||||
const program = require('commander');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
|
||||
program
|
||||
.version(packagejson.version + '')
|
||||
.description(packagejson.description)
|
||||
.command('serversetup', 'Performs necessary actions and prepares your Captain server.')
|
||||
.command('login', 'Login to a CaptainDuckDuck machine. You can be logged in to multiple machines simultaneously.')
|
||||
.command('logout', 'Logout from a specific Captain machine.')
|
||||
.command('list', 'List all Captain machines currently logged in.')
|
||||
.command('deploy', 'Deploy your app (current directory) to a specific Captain machine. You\'ll be prompted to choose your Captain machine.')
|
||||
.parse(process.argv);
|
||||
Generated
-1138
File diff suppressed because it is too large
Load Diff
+32
-4
@@ -1,13 +1,19 @@
|
||||
{
|
||||
"name": "captainduckduck",
|
||||
"version": "1.0.16",
|
||||
"version": "1.1.0",
|
||||
"description": "CLI tool for CaptainDuckDuck. See CaptainDuckDuck.com for more details.",
|
||||
"main": "captainduckduck.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "jest",
|
||||
"build": "rm -rf ./built && npx tsc && chmod +x ./built -R"
|
||||
},
|
||||
"bin": {
|
||||
"captainduckduck": "./captainduckduck.js"
|
||||
"captainduckduck": "./built/commands/captainduckduck.js",
|
||||
"captainduckduck-deploy": "./built/commands/deploy.js",
|
||||
"captainduckduck-list": "./built/commands/list.js",
|
||||
"captainduckduck-login": "./built/commands/login.js",
|
||||
"captainduckduck-logout": "./built/commands/logout.js",
|
||||
"captainduckduck-serversetup": "./built/commands/serversetup.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,16 +33,38 @@
|
||||
],
|
||||
"author": "Kasra Bigdeli",
|
||||
"license": "Apache-2.0",
|
||||
"lint-staged": {
|
||||
"./**/*.{js}": [
|
||||
"co-eslint",
|
||||
"co-prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.1",
|
||||
"@types/configstore": "^4.0.0",
|
||||
"@types/fs-extra": "^5.0.4",
|
||||
"@types/inquirer": "^0.0.43",
|
||||
"@types/request-promise": "^4.1.42",
|
||||
"@types/update-notifier": "^2.5.0",
|
||||
"chalk": "^2.4.2",
|
||||
"command-exists": "^1.2.8",
|
||||
"commander": "^2.19.0",
|
||||
"configstore": "^4.0.0",
|
||||
"fs-extra": "^7.0.1",
|
||||
"inquirer": "^6.2.1",
|
||||
"npm": "^6.5.0",
|
||||
"ora": "^3.0.0",
|
||||
"progress": "^2.0.3",
|
||||
"request": "^2.88.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"typescript": "^3.2.2",
|
||||
"update-notifier": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^10.12.18",
|
||||
"eslint": "^5.12.0",
|
||||
"eslint-plugin-jest": "^21.27.2",
|
||||
"jest": "^23.6.0",
|
||||
"prettier": "^1.15.3"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ You will then see your application being uploaded, after that, your application
|
||||
|
||||
You can also deploy directly with one command:
|
||||
```bash
|
||||
captainduckduck deploy -s -h https://captain.root.domain.com -a app-name -p password -b branchName
|
||||
captainduckduck deploy -h https://captain.root.domain.com -a app-name -p password -b branchName
|
||||
```
|
||||
|
||||
This can be useful if you want to integrate CI/CD pipeline.
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
import HttpClient from './HttpClient';
|
||||
import Logger from '../utils/Logger';
|
||||
import { IRegistryInfo } from '../models/IRegistryInfo';
|
||||
import { ICaptainDefinition } from '../models/ICaptainDefinition';
|
||||
import { IVersionInfo } from '../models/IVersionInfo';
|
||||
import { IAppDef } from '../models/AppDef';
|
||||
import * as fs from 'fs-extra';
|
||||
import IBuildLogs from '../models/IBuildLogs';
|
||||
|
||||
export default class ApiManager {
|
||||
private static lastKnownPassword: string = process.env.REACT_APP_DEFAULT_PASSWORD
|
||||
? process.env.REACT_APP_DEFAULT_PASSWORD + ''
|
||||
: 'captain42';
|
||||
private static authToken: string = !!process.env.REACT_APP_IS_DEBUG
|
||||
? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Im5hbWVzcGFjZSI6ImNhcHRhaW4iLCJ0b2tlblZlcnNpb24iOiI5NmRjM2U1MC00ZDk3LTRkNmItYTIzMS04MmNiZjY0ZTA2NTYifSwiaWF0IjoxNTQ1OTg0MDQwLCJleHAiOjE1ODE5ODQwNDB9.uGJyhb2JYsdw9toyMKX28bLVuB0PhnS2POwEjKpchww'
|
||||
: '';
|
||||
|
||||
private http: HttpClient;
|
||||
|
||||
constructor(baseUrl: string, private authTokenSaver: (authToken: string) => Promise<void>) {
|
||||
const self = this;
|
||||
|
||||
this.http = new HttpClient(baseUrl, ApiManager.authToken, function() {
|
||||
return self.getAuthToken(ApiManager.lastKnownPassword);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.http.destroy();
|
||||
}
|
||||
|
||||
setAuthToken(authToken: string) {
|
||||
ApiManager.authToken = authToken;
|
||||
this.http.setAuthToken(authToken);
|
||||
}
|
||||
|
||||
static isLoggedIn() {
|
||||
return !!ApiManager.authToken;
|
||||
}
|
||||
|
||||
getAuthToken(password: string) {
|
||||
const http = this.http;
|
||||
ApiManager.lastKnownPassword = password;
|
||||
let authTokenFetched = '';
|
||||
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/login', { password }))
|
||||
.then(function(data) {
|
||||
authTokenFetched = data.token;
|
||||
self.setAuthToken(authTokenFetched);
|
||||
return authTokenFetched;
|
||||
})
|
||||
.then(self.authTokenSaver)
|
||||
.then(function() {
|
||||
return authTokenFetched;
|
||||
});
|
||||
}
|
||||
|
||||
getCaptainInfo() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/info', {}));
|
||||
}
|
||||
|
||||
updateRootDomain(rootDomain: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/changerootdomain', { rootDomain }));
|
||||
}
|
||||
|
||||
enableRootSsl(emailAddress: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/enablessl', { emailAddress }));
|
||||
}
|
||||
|
||||
forceSsl(isEnabled: boolean) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/forcessl', { isEnabled }));
|
||||
}
|
||||
|
||||
getAllApps() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/appDefinitions', {})); // TODO user/apps/appDefinitions
|
||||
}
|
||||
|
||||
fetchBuildLogs(appName: string): Promise<IBuildLogs> {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/appData/' + appName, {})); // TODO user/apps/appData
|
||||
}
|
||||
|
||||
uploadAppData(appName: string, file: fs.ReadStream) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST_DATA, '/user/appData/' + appName + '?detached=1', { sourceFile: file })); // TODO user/apps/appData
|
||||
}
|
||||
|
||||
uploadCaptainDefinitionContent(
|
||||
appName: string,
|
||||
captainDefinition: ICaptainDefinition,
|
||||
gitHash: string,
|
||||
detached: boolean
|
||||
) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appData/' + appName + (detached ? '?detached=1' : ''), {
|
||||
captainDefinitionContent: JSON.stringify(captainDefinition),
|
||||
gitHash
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateConfigAndSave(appName: string, appDefinition: IAppDef) {
|
||||
var instanceCount = appDefinition.instanceCount;
|
||||
var envVars = appDefinition.envVars;
|
||||
var notExposeAsWebApp = appDefinition.notExposeAsWebApp;
|
||||
var forceSsl = appDefinition.forceSsl;
|
||||
var volumes = appDefinition.volumes;
|
||||
var ports = appDefinition.ports;
|
||||
var nodeId = appDefinition.nodeId;
|
||||
var appPushWebhook = appDefinition.appPushWebhook;
|
||||
var customNginxConfig = appDefinition.customNginxConfig;
|
||||
var preDeployFunction = appDefinition.preDeployFunction;
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/update', {
|
||||
appName: appName,
|
||||
instanceCount: instanceCount,
|
||||
notExposeAsWebApp: notExposeAsWebApp,
|
||||
forceSsl: forceSsl,
|
||||
volumes: volumes,
|
||||
ports: ports,
|
||||
customNginxConfig: customNginxConfig,
|
||||
appPushWebhook: appPushWebhook,
|
||||
nodeId: nodeId,
|
||||
preDeployFunction: preDeployFunction,
|
||||
envVars: envVars
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
registerNewApp(appName: string, hasPersistentData: boolean) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/register', {
|
||||
appName,
|
||||
hasPersistentData
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
deleteApp(appName: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/delete', {
|
||||
appName
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
enableSslForBaseDomain(appName: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/enablebasedomainssl', {
|
||||
appName
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
attachNewCustomDomainToApp(appName: string, customDomain: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/customdomain', {
|
||||
appName,
|
||||
customDomain
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
enableSslForCustomDomain(appName: string, customDomain: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/enablecustomdomainssl', {
|
||||
appName,
|
||||
customDomain
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
removeCustomDomain(appName: string, customDomain: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/removecustomdomain', {
|
||||
appName,
|
||||
customDomain
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getLoadBalancerInfo() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/loadbalancerinfo', {}));
|
||||
}
|
||||
|
||||
getNetDataInfo() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/netdata', {}));
|
||||
}
|
||||
|
||||
updateNetDataInfo(netDataInfo: any) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/netdata', { netDataInfo }));
|
||||
}
|
||||
|
||||
changePass(oldPassword: string, newPassword: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/changepassword', {
|
||||
oldPassword,
|
||||
newPassword
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getVersionInfo(): Promise<IVersionInfo> {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/versioninfo', {}));
|
||||
}
|
||||
|
||||
performUpdate(latestVersion: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/versioninfo', { latestVersion }));
|
||||
}
|
||||
|
||||
getNginxConfig() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/nginxconfig', {}));
|
||||
}
|
||||
|
||||
setNginxConfig(customBase: string, customCaptain: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/system/nginxconfig', {
|
||||
baseConfig: { customValue: customBase },
|
||||
captainConfig: { customValue: customCaptain }
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getUnusedImages(mostRecentLimit: number) {
|
||||
const http = this.http;
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.GET, '/user/apps/appDefinitions/unusedImages', {
|
||||
mostRecentLimit: mostRecentLimit + ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
deleteImages(imageIds: string[]) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/apps/appDefinitions/deleteImages', {
|
||||
imageIds
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getDockerRegistries() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/registries', {}));
|
||||
}
|
||||
|
||||
enableSelfHostedDockerRegistry() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/selfhostregistry/enableregistry', {}));
|
||||
}
|
||||
|
||||
disableSelfHostedDockerRegistry() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/system/selfhostregistry/disableregistry', {}));
|
||||
}
|
||||
|
||||
addDockerRegistry(dockerRegistry: IRegistryInfo) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/registries/insert', { ...dockerRegistry }));
|
||||
}
|
||||
|
||||
updateDockerRegistry(dockerRegistry: IRegistryInfo) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.POST, '/user/registries/update', { ...dockerRegistry }));
|
||||
}
|
||||
|
||||
deleteDockerRegistry(registryId: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/registries/delete', {
|
||||
registryId
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
setDefaultPushDockerRegistry(registryId: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/registries/setpush', {
|
||||
registryId
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getAllNodes() {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(http.fetch(http.GET, '/user/system/nodes', {}));
|
||||
}
|
||||
|
||||
addDockerNode(nodeType: string, privateKey: string, remoteNodeIpAddress: string, captainIpAddress: string) {
|
||||
const http = this.http;
|
||||
|
||||
return Promise.resolve() //
|
||||
.then(
|
||||
http.fetch(http.POST, '/user/system/nodes', {
|
||||
nodeType,
|
||||
privateKey,
|
||||
remoteNodeIpAddress,
|
||||
captainIpAddress
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import ApiManager from './ApiManager';
|
||||
import { IHashMapGeneric } from '../models/IHashMapGeneric';
|
||||
import StorageHelper from '../utils/StorageHelper';
|
||||
import { IMachine } from '../models/storage/StoredObjects';
|
||||
|
||||
function hashCode(str: string) {
|
||||
var hash = 0,
|
||||
i,
|
||||
chr;
|
||||
if (str.length === 0) return hash;
|
||||
for (i = 0; i < str.length; i++) {
|
||||
chr = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
export default class CliApiManager {
|
||||
static instances: IHashMapGeneric<ApiManager> = {};
|
||||
|
||||
static get(capMachine: IMachine) {
|
||||
const hashKey = 'v' + hashCode(capMachine.baseUrl);
|
||||
if (!CliApiManager.instances[hashKey])
|
||||
CliApiManager.instances[hashKey] = new ApiManager(capMachine.baseUrl + '/api/v1', function(token) {
|
||||
capMachine.authToken = token;
|
||||
if (capMachine.name) StorageHelper.get().saveMachine(capMachine);
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
CliApiManager.instances[hashKey].setAuthToken(capMachine.authToken);
|
||||
|
||||
return CliApiManager.instances[hashKey];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import ErrorFactory from '../utils/ErrorFactory';
|
||||
import Logger from '../utils/Logger';
|
||||
import * as Request from 'request-promise';
|
||||
|
||||
var TOKEN_HEADER = 'x-captain-auth';
|
||||
var NAMESPACE = 'x-namespace';
|
||||
var CAPTAIN = 'captain';
|
||||
|
||||
export default class HttpClient {
|
||||
public readonly GET = 'GET';
|
||||
public readonly POST = 'POST';
|
||||
public readonly POST_DATA = 'POST_DATA';
|
||||
public isDestroyed = false;
|
||||
|
||||
constructor(private baseUrl: string, private authToken: string, private onAuthFailure: () => Promise<any>) {
|
||||
//
|
||||
}
|
||||
|
||||
createHeaders() {
|
||||
let headers: any = {};
|
||||
if (this.authToken) headers[TOKEN_HEADER] = this.authToken;
|
||||
headers[NAMESPACE] = CAPTAIN;
|
||||
|
||||
// check user/appData or apiManager.uploadAppData before changing this signature.
|
||||
return headers;
|
||||
}
|
||||
|
||||
setAuthToken(authToken: string) {
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.isDestroyed = true;
|
||||
}
|
||||
|
||||
fetch(method: 'GET' | 'POST' | 'POST_DATA', endpoint: string, variables: any) {
|
||||
const self = this;
|
||||
return function(): Promise<any> {
|
||||
return Promise.resolve() //
|
||||
.then(function() {
|
||||
if (!process.env.REACT_APP_IS_DEBUG) return Promise.resolve();
|
||||
return new Promise<void>(function(res) {
|
||||
setTimeout(res, 500);
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
return self.fetchInternal(method, endpoint, variables); //
|
||||
})
|
||||
.then(function(requestResponse) {
|
||||
const data = JSON.parse(requestResponse);
|
||||
if (data.status === ErrorFactory.STATUS_AUTH_TOKEN_INVALID) {
|
||||
return self
|
||||
.onAuthFailure() //
|
||||
.then(function() {
|
||||
return self
|
||||
.fetchInternal(method, endpoint, variables)
|
||||
.then(function(newRequestResponse) {
|
||||
return newRequestResponse;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data.status !== ErrorFactory.OKAY && data.status !== ErrorFactory.OKAY_BUILD_STARTED) {
|
||||
throw ErrorFactory.createError(
|
||||
data.status || ErrorFactory.UNKNOWN_ERROR,
|
||||
data.description || ''
|
||||
);
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.then(function(data) {
|
||||
// These two blocks are clearly memory leaks! But I don't have time to fix them now... I need to CANCEL the promise, but since I don't
|
||||
// have CANCEL method on the native Promise, I return a promise that will never RETURN if the HttpClient is destroyed.
|
||||
// Will fix them later... but it shouldn't be a big deal anyways as it's only a problem when user navigates away from a page before the
|
||||
// network request returns back.
|
||||
return new Promise(function(resolve, reject) {
|
||||
// data.data here is the "data" field inside the API response! {status: 100, description: "Login succeeded", data: {…}}
|
||||
if (!self.isDestroyed) return resolve(data.data || { token: data.token }); // TODO remove || for API V2
|
||||
Logger.dev('Destroyed then not called');
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
// Logger.log('');
|
||||
// Logger.error(error.message || error);
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (!self.isDestroyed) return reject(error);
|
||||
Logger.dev('Destroyed catch not called');
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
fetchInternal(method: 'GET' | 'POST' | 'POST_DATA', endpoint: string, variables: any) {
|
||||
if (method === this.GET) return this.getReq(endpoint, variables);
|
||||
|
||||
if (method === this.POST || method === this.POST_DATA) return this.postReq(endpoint, variables, method);
|
||||
|
||||
throw new Error('Unknown method: ' + method);
|
||||
}
|
||||
|
||||
getReq(endpoint: string, variables: any) {
|
||||
const self = this;
|
||||
|
||||
return Request.get(this.baseUrl + endpoint, {
|
||||
headers: self.createHeaders(),
|
||||
qs: variables
|
||||
}).then(function(data) {
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
postReq(endpoint: string, variables: any, method: 'GET' | 'POST' | 'POST_DATA') {
|
||||
const self = this;
|
||||
|
||||
if (method === this.POST_DATA)
|
||||
return Request.post(this.baseUrl + endpoint, {
|
||||
headers: self.createHeaders(),
|
||||
formData: variables
|
||||
}).then(function(data) {
|
||||
return data;
|
||||
});
|
||||
|
||||
return Request.post(this.baseUrl + endpoint, {
|
||||
headers: self.createHeaders(),
|
||||
form: variables
|
||||
}).then(function(data) {
|
||||
return data;
|
||||
});
|
||||
}
|
||||
}
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const packagejson = require('../../package.json');
|
||||
import * as updateNotifier from 'update-notifier';
|
||||
updateNotifier({ pkg: packagejson }).notify({ isGlobal: true });
|
||||
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import * as program from 'commander';
|
||||
|
||||
// Command actions
|
||||
import login from './login';
|
||||
import list from './list';
|
||||
import logout from './logout';
|
||||
import deploy from './deploy';
|
||||
import serversetup from './serversetup';
|
||||
|
||||
// Setup
|
||||
program.version(packagejson.version).description(packagejson.description);
|
||||
|
||||
// Commands
|
||||
|
||||
program
|
||||
.command('login')
|
||||
.description('Login to a CaptainDuckDuck machine. You can be logged in to multiple machines simultaneously.')
|
||||
.action(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
program.command('list').alias('ls').description('List all Captain machines currently logged in.').action(() => {
|
||||
list();
|
||||
});
|
||||
|
||||
program.command('logout').description('Logout from a specific Captain machine.').action(() => {
|
||||
logout();
|
||||
});
|
||||
|
||||
program
|
||||
.command('serversetup')
|
||||
.description('Performs necessary actions and prepares your Captain server.')
|
||||
.action(() => {
|
||||
serversetup();
|
||||
});
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.description(
|
||||
"Deploy your app (current directory) to a specific Captain machine. You'll be prompted to choose your Captain machine.\n\n" +
|
||||
'For use in scripts, i.e. non-interactive mode, you can use --host --pass --appName and -- branch flags.'
|
||||
)
|
||||
.option('-d, --default', 'Use previously entered values for the current directory, avoid asking.')
|
||||
.option('-t, --tarFile <value>', 'Specify the tar file to be uploaded (rather than using git archive)')
|
||||
.option('-h, --host <value>', 'Specify th URL of the captain machine in command line')
|
||||
.option('-a, --appName <value>', 'Specify Name of the app to be deployed in command line')
|
||||
.option('-p, --pass <value>', 'Specify password for Captain in command line')
|
||||
.option('-b, --branch <value>', 'Specify branch name (default master)')
|
||||
.action((options: any) => {
|
||||
deploy(options);
|
||||
});
|
||||
|
||||
// Error on unknown commands
|
||||
program.on('command:*', () => {
|
||||
const wrongCommands = program.args.join(' ');
|
||||
|
||||
StdOutUtil.printError(`\nInvalid command: ${wrongCommands}\nSee --help for a list of available commands.`, true);
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
||||
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as inquirer from 'inquirer';
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import { validateIsGitRepository, validateDefinitionFile, ensureAuthentication } from '../utils/ValidationsHandler';
|
||||
import { IMachine, IDeployedDirectory, IDeploySource, IDeployParams } from '../models/storage/StoredObjects';
|
||||
import StorageHelper from '../utils/StorageHelper';
|
||||
import CliHelper from '../utils/CliHelper';
|
||||
import { IHashMapGeneric } from '../models/IHashMapGeneric';
|
||||
import DeployHelper from '../utils/DeployHelper';
|
||||
import CliApiManager from '../api/CliApiManager';
|
||||
|
||||
async function deploy(options: any) {
|
||||
const possibleApp = StorageHelper.get()
|
||||
.getDeployedDirectories()
|
||||
.find((dir: IDeployedDirectory) => dir.cwd === process.cwd());
|
||||
|
||||
StdOutUtil.printMessage('Preparing deployment to Captain...\n');
|
||||
|
||||
let deployParams: IDeployParams = { deploySource: {} };
|
||||
|
||||
if (options.default) {
|
||||
deployParams = {
|
||||
captainMachine: possibleApp ? StorageHelper.get().findMachine(possibleApp.machineNameToDeploy) : undefined,
|
||||
deploySource: possibleApp ? possibleApp.deploySource : {},
|
||||
appName: possibleApp ? possibleApp.appName : undefined
|
||||
};
|
||||
} else if (possibleApp) {
|
||||
StdOutUtil.printMessage(
|
||||
`\n\n**********\n\nProtip: You seem to have deployed ${possibleApp.appName} from this directory in the past, use --default flag to avoid having to re-enter the information.\n\n**********\n\n`
|
||||
);
|
||||
}
|
||||
|
||||
if (options.appName) {
|
||||
deployParams.appName = options.appName;
|
||||
}
|
||||
|
||||
if (options.branch) {
|
||||
deployParams.deploySource.branchToPush = options.branch;
|
||||
}
|
||||
|
||||
if (options.tarFile) {
|
||||
deployParams.deploySource.tarFilePath = options.tarFile;
|
||||
}
|
||||
|
||||
if (!deployParams.deploySource.tarFilePath) {
|
||||
if (!validateIsGitRepository() || !validateDefinitionFile()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.pass || options.host) {
|
||||
if (options.pass && options.host) {
|
||||
deployParams.captainMachine = {
|
||||
authToken: '',
|
||||
baseUrl: options.host,
|
||||
name: ''
|
||||
};
|
||||
await CliApiManager.get(deployParams.captainMachine).getAuthToken(options.pass);
|
||||
} else {
|
||||
StdOutUtil.printError('host and pass should be either both defined or both undefined', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Show questions for what is being missing in deploy params
|
||||
let allApps: any = undefined;
|
||||
if (deployParams.captainMachine) {
|
||||
allApps = await ensureAuthentication(deployParams.captainMachine);
|
||||
}
|
||||
|
||||
const allParametersAreSupplied =
|
||||
!!deployParams.appName &&
|
||||
!!deployParams.captainMachine &&
|
||||
(!!deployParams.deploySource.branchToPush || !!deployParams.deploySource.tarFilePath);
|
||||
|
||||
if (!allParametersAreSupplied) {
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'captainNameToDeploy',
|
||||
default: possibleApp ? possibleApp.machineNameToDeploy : '',
|
||||
message: 'Select the Captain Machine you want to deploy to:',
|
||||
choices: CliHelper.get().getMachinesAsOptions(),
|
||||
when: () => !deployParams.captainMachine,
|
||||
filter: async (capName: string) => {
|
||||
deployParams.captainMachine = StorageHelper.get().findMachine(capName);
|
||||
if (deployParams.captainMachine) allApps = await ensureAuthentication(deployParams.captainMachine);
|
||||
return capName;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default:
|
||||
possibleApp && possibleApp.deploySource.branchToPush
|
||||
? possibleApp.deploySource.branchToPush
|
||||
: 'master',
|
||||
name: 'branchToPush',
|
||||
message: "Enter the 'git' branch you would like to deploy:",
|
||||
filter: async (branchToPushEntered: string) => {
|
||||
deployParams.deploySource.branchToPush = branchToPushEntered;
|
||||
return branchToPushEntered;
|
||||
},
|
||||
when: (answers: IHashMapGeneric<string>) =>
|
||||
!deployParams.deploySource.branchToPush &&
|
||||
!deployParams.deploySource.tarFilePath &&
|
||||
!!deployParams.captainMachine
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
default: possibleApp ? possibleApp.appName : '',
|
||||
name: 'appName',
|
||||
message: 'Enter the Captain app name this directory will be deployed to:',
|
||||
choices: (answers: IHashMapGeneric<string>) => {
|
||||
return CliHelper.get().getAppsAsOptions(allApps);
|
||||
},
|
||||
filter: async (appNameEntered: string) => {
|
||||
deployParams.appName = appNameEntered;
|
||||
return appNameEntered;
|
||||
},
|
||||
when: (answers: IHashMapGeneric<string>) =>
|
||||
(!!deployParams.deploySource.branchToPush || !!deployParams.deploySource.tarFilePath) &&
|
||||
!deployParams.appName
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmedToDeploy',
|
||||
message:
|
||||
'Note that uncommitted files and files in gitignore (if any) will not be pushed to server. \n Please confirm so that deployment process can start.',
|
||||
default: true,
|
||||
when: (answers: IHashMapGeneric<string>) =>
|
||||
!!deployParams.appName &&
|
||||
!!deployParams.captainMachine &&
|
||||
(!!deployParams.deploySource.branchToPush || !!deployParams.deploySource.tarFilePath)
|
||||
}
|
||||
];
|
||||
const answersToIgnore = (await inquirer.prompt(questions)) as IHashMapGeneric<string>;
|
||||
|
||||
if (!answersToIgnore.confirmedToDeploy) {
|
||||
StdOutUtil.printMessage('\nOperation cancelled by the user...\n');
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await new DeployHelper(deployParams) //
|
||||
.startDeploy();
|
||||
} catch (e) {
|
||||
StdOutUtil.printError(e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
export default deploy;
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import chalk from 'chalk';
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import StorageHelper from '../utils/StorageHelper';
|
||||
import { IMachine } from '../models/storage/StoredObjects';
|
||||
|
||||
function _displayMachine(machine: IMachine) {
|
||||
console.log('>> ' + chalk.greenBright(machine.name) + ' at ' + chalk.cyan(machine.baseUrl));
|
||||
}
|
||||
|
||||
function list() {
|
||||
StdOutUtil.printMessage('\nLogged in Captain Machines:\n');
|
||||
|
||||
StorageHelper.get().getMachines().map((machine) => {
|
||||
_displayMachine(machine);
|
||||
});
|
||||
|
||||
StdOutUtil.printMessage('');
|
||||
}
|
||||
|
||||
export default list;
|
||||
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as inquirer from 'inquirer';
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import StorageHelper from '../utils/StorageHelper';
|
||||
import Constants from '../utils/Constants';
|
||||
import Utils from '../utils/Utils';
|
||||
import CliHelper from '../utils/CliHelper';
|
||||
import { IHashMapGeneric } from '../models/IHashMapGeneric';
|
||||
import CliApiManager from '../api/CliApiManager';
|
||||
|
||||
const SAMPLE_DOMAIN = Constants.SAMPLE_DOMAIN;
|
||||
const cleanUpUrl = Utils.cleanUpUrl;
|
||||
|
||||
async function login() {
|
||||
StdOutUtil.printMessage('Login to a Captain Machine');
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'input',
|
||||
default: SAMPLE_DOMAIN,
|
||||
name: 'captainAddress',
|
||||
message: '\nEnter address of the Captain machine. \nIt is captain.[your-captain-root-domain] :',
|
||||
validate: (value: string) => {
|
||||
if (value === SAMPLE_DOMAIN) {
|
||||
return 'Enter a valid URL';
|
||||
}
|
||||
|
||||
if (!cleanUpUrl(value)) return 'This is an invalid URL: ' + value;
|
||||
|
||||
let found = undefined;
|
||||
StorageHelper.get().getMachines().map((machine) => {
|
||||
if (cleanUpUrl(machine.baseUrl) === cleanUpUrl(value)) {
|
||||
found = machine.name;
|
||||
}
|
||||
});
|
||||
|
||||
if (found) {
|
||||
return `${value} already exist as ${found} in your currently logged in machines. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
|
||||
}
|
||||
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid address.';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'captainHasRootSsl',
|
||||
message: 'Is HTTPS activated for this Captain machine?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainPassword',
|
||||
message: 'Enter your password:',
|
||||
validate: (value: string) => {
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter your password.';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainName',
|
||||
message: 'Enter a name for this Captain machine:',
|
||||
default: CliHelper.get().findDefaultCaptainName(),
|
||||
validate: (value: string) => {
|
||||
value = value.trim();
|
||||
|
||||
if (StorageHelper.get().findMachine(value)) {
|
||||
return `${value} already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
|
||||
}
|
||||
|
||||
if (CliHelper.get().isNameValid(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a Captain Name.';
|
||||
}
|
||||
}
|
||||
];
|
||||
const answers = (await inquirer.prompt(questions)) as IHashMapGeneric<string>;
|
||||
const { captainHasRootSsl, captainPassword, captainAddress, captainName } = answers;
|
||||
const handleHttp = captainHasRootSsl ? 'https://' : 'http://';
|
||||
const baseUrl = `${handleHttp}${cleanUpUrl(captainAddress)}`;
|
||||
|
||||
try {
|
||||
const tokenToIgnore = await CliApiManager.get({
|
||||
authToken: '',
|
||||
baseUrl,
|
||||
name: captainName
|
||||
}).getAuthToken(captainPassword);
|
||||
|
||||
StdOutUtil.printGreenMessage(`\nLogged in successfully to ${baseUrl}`);
|
||||
StdOutUtil.printGreenMessage(`Authorization token is now saved as ${captainName} \n`);
|
||||
} catch (error) {
|
||||
const errorMessage = error.message ? error.message : error;
|
||||
|
||||
StdOutUtil.printError(`Something bad happened. Cannot save "${captainName}" \n${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default login;
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as inquirer from 'inquirer';
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import CliHelper from '../utils/CliHelper';
|
||||
|
||||
function generateQuestions() {
|
||||
const listOfMachines = CliHelper.get().getMachinesAsOptions();
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'captainNameToLogout',
|
||||
message: 'Select the Captain Machine you want to logout from:',
|
||||
choices: listOfMachines
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmedToLogout',
|
||||
message: 'Are you sure you want to logout from this Captain machine?',
|
||||
default: false,
|
||||
when: (answers: any) => answers.captainNameToLogout
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
const questions = generateQuestions();
|
||||
|
||||
StdOutUtil.printMessage('Logout from a Captain Machine and clear auth info');
|
||||
|
||||
const answers = await inquirer.prompt(questions);
|
||||
const { captainNameToLogout, confirmedToLogout } = answers;
|
||||
|
||||
if (!captainNameToLogout || !confirmedToLogout) {
|
||||
StdOutUtil.printMessage('\nOperation cancelled by the user...\n');
|
||||
return;
|
||||
}
|
||||
|
||||
CliHelper.get().logoutMachine(captainNameToLogout);
|
||||
}
|
||||
|
||||
export default logout;
|
||||
@@ -0,0 +1,29 @@
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import * as inquirer from 'inquirer';
|
||||
import { IMachine } from '../models/storage/StoredObjects';
|
||||
import CliApiManager from '../api/CliApiManager';
|
||||
|
||||
// In case the token is expired
|
||||
export default async function requestLogin(machine: IMachine) {
|
||||
const { baseUrl } = machine;
|
||||
|
||||
StdOutUtil.printMessage('Your auth token is not valid anymore. Try to login again.');
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainPassword',
|
||||
message: 'Please enter your password for ' + baseUrl,
|
||||
validate: (value: string) => {
|
||||
if (value && value.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter your password for ' + baseUrl;
|
||||
}
|
||||
}
|
||||
];
|
||||
const loginPassword = (await inquirer.prompt(questions)) as any;
|
||||
const password = loginPassword.captainPassword;
|
||||
const responseIgnore = await CliApiManager.get(machine).getAuthToken(password);
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as inquirer from 'inquirer';
|
||||
import Constants from '../utils/Constants';
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
import { isIpAddress } from '../utils/ValidationsHandler';
|
||||
import { IMachine } from '../models/storage/StoredObjects';
|
||||
import CliApiManager from '../api/CliApiManager';
|
||||
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';
|
||||
|
||||
let newPasswordFirstTry: string | undefined = undefined;
|
||||
let lastWorkingPassword: string = Constants.DEFAULT_PASSWORD;
|
||||
let serverIpAddress = '';
|
||||
|
||||
let captainMachine: IMachine = {
|
||||
authToken: '',
|
||||
baseUrl: '',
|
||||
name: ''
|
||||
};
|
||||
|
||||
const questions = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'hasInstalledCaptain',
|
||||
message:
|
||||
'Have you already installed Captain on your server by running the following line:' +
|
||||
'\nmkdir /captain && docker run -p 80:80 -p 443:443 -p 3000:3000 -v /var/run/docker.sock:/var/run/docker.sock dockersaturn/captainduckduck ?',
|
||||
default: 'Yes',
|
||||
choices: [ 'Yes', 'No' ],
|
||||
filter: (value: string) => {
|
||||
const answerFromUser = value.trim();
|
||||
|
||||
if (answerFromUser === 'Yes') return answerFromUser;
|
||||
|
||||
StdOutUtil.printMessage('\n\nCannot start the setup process if Captain is not installed.');
|
||||
|
||||
StdOutUtil.printMessageAndExit(
|
||||
'Please read tutorial on CaptainDuckDuck.com to learn how to install CaptainDuckDuck on a server.'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
default: Constants.SAMPLE_IP,
|
||||
name: 'captainAddress',
|
||||
message: 'Enter IP address of your captain server:',
|
||||
filter: async (value: string) => {
|
||||
const ipFromUser = value.trim();
|
||||
|
||||
if (ipFromUser === Constants.SAMPLE_IP || !isIpAddress(ipFromUser)) {
|
||||
StdOutUtil.printError(`\nThis is an invalid IP Address: ${ipFromUser}`, true);
|
||||
}
|
||||
|
||||
try {
|
||||
// login using captain42. and set the ipAddressToServer
|
||||
captainMachine.baseUrl = `http://${ipFromUser}:3000`;
|
||||
await CliApiManager.get(captainMachine).getAuthToken(lastWorkingPassword);
|
||||
serverIpAddress = ipFromUser;
|
||||
} catch (e) {
|
||||
// User may have used a different default password
|
||||
if (e.captainStatus === ErrorFactory.STATUS_WRONG_PASSWORD) return '';
|
||||
StdOutUtil.errorHandler(e);
|
||||
}
|
||||
|
||||
return ipFromUser;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'captainOriginalPassword',
|
||||
message: 'Enter your current password:',
|
||||
when: () => !captainMachine.authToken, // The default password didn't work
|
||||
filter: async (value: string) => {
|
||||
try {
|
||||
await CliApiManager.get(captainMachine).getAuthToken(value);
|
||||
lastWorkingPassword = value;
|
||||
return '';
|
||||
} catch (e) {
|
||||
StdOutUtil.errorHandler(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainRootDomain',
|
||||
message:
|
||||
'Enter a root domain for this Captain server. For example, enter test.yourdomain.com if you' +
|
||||
' setup your DNS to point *.test.yourdomain.com to ip address of your server.',
|
||||
filter: async (value: string) => {
|
||||
const captainRootDomainFromUser = value.trim();
|
||||
try {
|
||||
await CliApiManager.get(captainMachine).updateRootDomain(captainRootDomainFromUser);
|
||||
captainMachine = Utils.copyObject(captainMachine);
|
||||
captainMachine.baseUrl = `http://captain.${captainRootDomainFromUser}`;
|
||||
} catch (e) {
|
||||
StdOutUtil.printError('\n\n');
|
||||
if (e.captainStatus === ErrorFactory.VERIFICATION_FAILED) {
|
||||
if (captainRootDomainFromUser.indexOf('/') >= 0) {
|
||||
StdOutUtil.printError(
|
||||
'DO NOT include http in your base domain, it should be just plain domain, e.g., test.domain.com'
|
||||
);
|
||||
}
|
||||
|
||||
if (captainRootDomainFromUser.indexOf('*') >= 0) {
|
||||
StdOutUtil.printError(
|
||||
'DO NOT include * in your base domain, it should be just plain domain, e.g., test.domain.com'
|
||||
);
|
||||
}
|
||||
|
||||
StdOutUtil.printError(
|
||||
`\n\nCannot verify that http://captain.${captainRootDomainFromUser} points to your server IP.\n` +
|
||||
`\nAre you sure that you set *.${captainRootDomainFromUser} points to ${serverIpAddress}\n\n` +
|
||||
`Double check your DNS. If everything looks correct, note that, DNS changes take up to 24 hrs to work properly. Check with your Domain Provider.`
|
||||
);
|
||||
}
|
||||
StdOutUtil.errorHandler(e);
|
||||
}
|
||||
|
||||
return captainRootDomainFromUser;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'newPasswordFirstTry',
|
||||
message: 'Enter a new password:',
|
||||
filter: (value: string) => {
|
||||
newPasswordFirstTry = value;
|
||||
|
||||
if (!newPasswordFirstTry) {
|
||||
StdOutUtil.printError('Password empty.', true);
|
||||
throw new Error('Password empty');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'newPassword',
|
||||
message: 'Enter your new password again:',
|
||||
filter: async (value: string) => {
|
||||
const confirmPasswordValueFromUser = value;
|
||||
|
||||
if ((newPasswordFirstTry !== confirmPasswordValueFromUser)) {
|
||||
StdOutUtil.printError('Passwords do not match. Try serversetup again.', true);
|
||||
throw new Error('Password mismatch');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'emailAddress',
|
||||
message: "Enter your 'valid' email address to enable HTTPS: ",
|
||||
filter: async (value: string) => {
|
||||
const emailAddressFromUser = value.trim();
|
||||
let forcedSsl = false;
|
||||
try {
|
||||
SpinnerHelper.start('Enabling SSL... Takes a few seconds...');
|
||||
await CliApiManager.get(captainMachine).enableRootSsl(emailAddressFromUser);
|
||||
|
||||
captainMachine = Utils.copyObject(captainMachine);
|
||||
captainMachine.baseUrl = captainMachine.baseUrl.replace('http://', 'https://');
|
||||
|
||||
await CliApiManager.get(captainMachine).forceSsl(true);
|
||||
forcedSsl = true;
|
||||
await CliApiManager.get(captainMachine).changePass(lastWorkingPassword, newPasswordFirstTry!);
|
||||
lastWorkingPassword = newPasswordFirstTry!;
|
||||
await CliApiManager.get(captainMachine).getAuthToken(lastWorkingPassword);
|
||||
SpinnerHelper.stop();
|
||||
} catch (e) {
|
||||
if (forcedSsl) {
|
||||
StdOutUtil.printError(
|
||||
'Server is setup, but password was not changed due to an error. You cannot use serversetup again.'
|
||||
);
|
||||
StdOutUtil.printError(
|
||||
`Instead, go to ${captainMachine.baseUrl} and change your password on settings page.`
|
||||
);
|
||||
StdOutUtil.printError(
|
||||
`Then, Use captainduckduck login on your local machine to connect to your server.`
|
||||
);
|
||||
}
|
||||
SpinnerHelper.fail();
|
||||
StdOutUtil.errorHandler(e);
|
||||
}
|
||||
|
||||
return emailAddressFromUser;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'captainName',
|
||||
message: 'Enter a name for this Captain machine:',
|
||||
default: CliHelper.get().findDefaultCaptainName(),
|
||||
validate: (value: string) => {
|
||||
const newMachineName = value.trim();
|
||||
|
||||
let errorMessage = undefined;
|
||||
if (StorageHelper.get().findMachine(newMachineName)) {
|
||||
return `${newMachineName} already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
|
||||
}
|
||||
|
||||
if (CliHelper.get().isNameValid(newMachineName)) {
|
||||
captainMachine.name = newMachineName;
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid Captain Name. Small letters, numbers, single hyphen.';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
async function serversetup() {
|
||||
StdOutUtil.printMessage('\nSetup your Captain server\n');
|
||||
|
||||
const answersIgnore = await inquirer.prompt(questions);
|
||||
|
||||
StorageHelper.get().saveMachine(captainMachine);
|
||||
|
||||
StdOutUtil.printMessage(`\n\nCaptain is available at ${captainMachine.baseUrl}`);
|
||||
|
||||
StdOutUtil.printMessage('\nFor more details and docs see http://www.captainduckduck.com\n\n');
|
||||
}
|
||||
|
||||
export default serversetup;
|
||||
@@ -0,0 +1,93 @@
|
||||
//COPIED FROM BACKEND CODE
|
||||
interface IHashMapGeneric<T> {
|
||||
[id: string]: T;
|
||||
}
|
||||
|
||||
type IAllAppDefinitions = IHashMapGeneric<IAppDef>;
|
||||
|
||||
export interface IAppEnvVar {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface IAppVolume {
|
||||
containerPath: string;
|
||||
volumeName?: string;
|
||||
hostPath?: string;
|
||||
}
|
||||
|
||||
interface IAppPort {
|
||||
containerPort: number;
|
||||
hostPort: number;
|
||||
protocol?: "udp" | "tcp";
|
||||
|
||||
publishMode?: "ingress" | "host";
|
||||
}
|
||||
|
||||
export interface RepoInfo {
|
||||
repo: string;
|
||||
branch: string;
|
||||
user: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface RepoInfoEncrypted {
|
||||
repo: string;
|
||||
branch: string;
|
||||
user: string;
|
||||
passwordEncrypted: string;
|
||||
}
|
||||
|
||||
export interface IAppVersion {
|
||||
version: number;
|
||||
deployedImageName?: string; // empty if the deploy is not completed
|
||||
timeStamp: string;
|
||||
gitHash: string | undefined;
|
||||
}
|
||||
|
||||
interface IAppCustomDomain {
|
||||
publicDomain: string;
|
||||
hasSsl: boolean;
|
||||
}
|
||||
|
||||
interface IAppDefinitionBase {
|
||||
deployedVersion: number;
|
||||
notExposeAsWebApp: boolean;
|
||||
hasPersistentData: boolean;
|
||||
hasDefaultSubDomainSsl: boolean;
|
||||
|
||||
forceSsl: boolean;
|
||||
nodeId?: string;
|
||||
instanceCount: number;
|
||||
preDeployFunction?: string;
|
||||
customNginxConfig?: string;
|
||||
networks: string[];
|
||||
customDomain: IAppCustomDomain[];
|
||||
|
||||
ports: IAppPort[];
|
||||
volumes: IAppVolume[];
|
||||
envVars: IAppEnvVar[];
|
||||
|
||||
versions: IAppVersion[];
|
||||
}
|
||||
|
||||
export interface IAppDef extends IAppDefinitionBase {
|
||||
appPushWebhook?: {
|
||||
repoInfo: RepoInfo;
|
||||
tokenVersion?: string; // On FrontEnd, these values are null, until they are assigned.
|
||||
pushWebhookToken?: string; // On FrontEnd, these values are null, until they are assigned.
|
||||
};
|
||||
appName?: string;
|
||||
isAppBuilding?: boolean;
|
||||
}
|
||||
|
||||
interface IAppDefSaved extends IAppDefinitionBase {
|
||||
appPushWebhook:
|
||||
| {
|
||||
tokenVersion: string;
|
||||
repoInfo: RepoInfoEncrypted;
|
||||
pushWebhookToken: string;
|
||||
}
|
||||
| undefined;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export default interface IBuildLogs {
|
||||
isAppBuilding: boolean;
|
||||
isBuildFailed: boolean;
|
||||
logs: {
|
||||
firstLineNumber: number;
|
||||
lines: string[];
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface ICaptainDefinition {
|
||||
schemaVersion: number
|
||||
dockerfileLines?: string[]
|
||||
imageName?: string
|
||||
templateId?: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface IHashMapGeneric<T> {
|
||||
[id: string]: T
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { IHashMapGeneric } from "./IHashMapGeneric";
|
||||
|
||||
export interface IOneClickAppIdentifier {
|
||||
name: string;
|
||||
download_url: string;
|
||||
}
|
||||
|
||||
export interface IOneClickVariable {
|
||||
id: string;
|
||||
label: string;
|
||||
defaultValue?: string;
|
||||
validRegex?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface IDockerComposeService {
|
||||
image?: string;
|
||||
dockerFileLines?: string[]; // This is our property, not DockerCompose. We use this instead of image if we need to extend the image.
|
||||
volumes?: string[];
|
||||
ports?: string[];
|
||||
environment?: IHashMapGeneric<string>;
|
||||
depends_on?: string[];
|
||||
}
|
||||
|
||||
export interface IOneClickTemplate {
|
||||
captainVersion: number;
|
||||
dockerCompose: {
|
||||
version: string;
|
||||
services: IHashMapGeneric<IDockerComposeService>;
|
||||
};
|
||||
instructions: {
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
variables: IOneClickVariable[];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export interface IRegistryApi {
|
||||
registries: IRegistryInfo[];
|
||||
defaultPushRegistryId: string | undefined;
|
||||
}
|
||||
|
||||
export class IRegistryTypes {
|
||||
static readonly LOCAL_REG = "LOCAL_REG";
|
||||
static readonly REMOTE_REG = "REMOTE_REG";
|
||||
}
|
||||
|
||||
type IRegistryType = "LOCAL_REG" | "REMOTE_REG";
|
||||
|
||||
export interface IRegistryInfo {
|
||||
id: string;
|
||||
registryUser: string;
|
||||
registryPassword: string;
|
||||
registryDomain: string;
|
||||
registryImagePrefix: string;
|
||||
registryType: IRegistryType;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface IVersionInfo {
|
||||
currentVersion: string;
|
||||
latestVersion: string;
|
||||
canUpdate: boolean;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
export interface IMachine {
|
||||
authToken: string;
|
||||
baseUrl: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IOldSavedApp {
|
||||
cwd: string;
|
||||
appName: string;
|
||||
branchToPush: string;
|
||||
machineToDeploy: IMachine;
|
||||
}
|
||||
|
||||
export interface IDeploySource {
|
||||
branchToPush?: string;
|
||||
tarFilePath?: string;
|
||||
}
|
||||
|
||||
export interface IDeployedDirectory {
|
||||
cwd: string;
|
||||
appName: string;
|
||||
deploySource: IDeploySource;
|
||||
machineNameToDeploy: string;
|
||||
}
|
||||
|
||||
export interface IDeployParams {
|
||||
deploySource: IDeploySource;
|
||||
captainMachine?: IMachine;
|
||||
appName?: string;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import StorageHelper from './StorageHelper';
|
||||
import { IMachine } from '../models/storage/StoredObjects';
|
||||
import StdOutUtil from './StdOutUtil';
|
||||
|
||||
export default class CliHelper {
|
||||
static instance: CliHelper;
|
||||
|
||||
static get() {
|
||||
if (!CliHelper.instance) CliHelper.instance = new CliHelper();
|
||||
return CliHelper.instance;
|
||||
}
|
||||
|
||||
isNameValid(value: string) {
|
||||
value = value || '';
|
||||
if (!!value && value.match(/^[-\d\w]+$/i) && value.indexOf('--') < 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getAppsAsOptions(apps: any[]) {
|
||||
const firstItemInOption = [
|
||||
{
|
||||
name: '-- CANCEL --',
|
||||
value: '',
|
||||
short: ''
|
||||
}
|
||||
];
|
||||
const listOfApps = apps.map((app) => {
|
||||
return {
|
||||
name: `${app.appName}`,
|
||||
value: `${app.appName}`,
|
||||
short: `${app.appName}`
|
||||
};
|
||||
});
|
||||
|
||||
return [ ...firstItemInOption, ...listOfApps ];
|
||||
}
|
||||
|
||||
getMachinesAsOptions() {
|
||||
const machines = StorageHelper.get().getMachines();
|
||||
const firstItemInOption = [
|
||||
{
|
||||
name: '-- CANCEL --',
|
||||
value: '',
|
||||
short: ''
|
||||
}
|
||||
];
|
||||
const listOfMachines = machines.map((machine) => {
|
||||
return {
|
||||
name: `${machine.name} at ${machine.baseUrl}`,
|
||||
value: `${machine.name}`,
|
||||
short: `${machine.name} at ${machine.baseUrl}`
|
||||
};
|
||||
});
|
||||
|
||||
return [ ...firstItemInOption, ...listOfMachines ];
|
||||
}
|
||||
|
||||
logoutMachine(machineName: string) {
|
||||
const removedMachine = StorageHelper.get().removeMachine(machineName);
|
||||
StdOutUtil.printMessage(`You are now logged out from ${removedMachine.name} at ${removedMachine.baseUrl}...\n`);
|
||||
}
|
||||
|
||||
findDefaultCaptainName() {
|
||||
let currentSuffix = StorageHelper.get().getMachines().length + 1;
|
||||
const self = this;
|
||||
|
||||
while (!self.isSuffixValid(currentSuffix)) {
|
||||
currentSuffix++;
|
||||
}
|
||||
|
||||
return self.getCaptainFullName(currentSuffix);
|
||||
}
|
||||
|
||||
getCaptainFullName(suffix: number) {
|
||||
const formatSuffix = suffix < 10 ? `0${suffix}` : suffix;
|
||||
|
||||
return `captain-${formatSuffix}`;
|
||||
}
|
||||
|
||||
isSuffixValid(suffixNumber: number) {
|
||||
const self = this;
|
||||
let valid = true;
|
||||
StorageHelper.get().getMachines().map((machine: IMachine) => {
|
||||
if (machine.name === self.getCaptainFullName(suffixNumber)) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
const SAMPLE_DOMAIN = "captain.captainroot.yourdomain.com"
|
||||
const SAMPLE_IP = "123.123.123.123"
|
||||
const DEFAULT_PASSWORD = "captain42"
|
||||
const DEFAULT_BRANCH_TO_PUSH = "branchToPush"
|
||||
const DEFAULT_APP_NAME = "appName"
|
||||
const EMPTY_STRING = ""
|
||||
|
||||
export default {
|
||||
SAMPLE_DOMAIN,
|
||||
SAMPLE_IP,
|
||||
DEFAULT_PASSWORD,
|
||||
DEFAULT_BRANCH_TO_PUSH,
|
||||
DEFAULT_APP_NAME,
|
||||
EMPTY_STRING
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { exec } from 'child_process';
|
||||
import StdOutUtil from '../utils/StdOutUtil';
|
||||
const ProgressBar = require('progress');
|
||||
const commandExistsSync = require('command-exists').sync;
|
||||
import { IMachine, IDeployParams } from '../models/storage/StoredObjects';
|
||||
import CliApiManager from '../api/CliApiManager';
|
||||
import SpinnerHelper from '../utils/SpinnerHelper';
|
||||
import IBuildLogs from '../models/IBuildLogs';
|
||||
import StorageHelper from './StorageHelper';
|
||||
|
||||
export default class DeployHelper {
|
||||
private lastLineNumberPrinted = -10000; // we want to show all lines to begin with!
|
||||
|
||||
constructor(private deployParams: IDeployParams) {
|
||||
//
|
||||
}
|
||||
|
||||
private gitArchiveFile(zipFileFullPath: string, branchToPush: string) {
|
||||
const self = this;
|
||||
return new Promise<string>(function(resolve, reject) {
|
||||
// Removes the temporary file created
|
||||
if (fs.pathExistsSync(zipFileFullPath)) fs.removeSync(zipFileFullPath);
|
||||
|
||||
if (!commandExistsSync('git')) {
|
||||
StdOutUtil.printError(
|
||||
"'git' command not found...\nCaptain needs 'git' to create tar file of your source files...",
|
||||
true
|
||||
);
|
||||
reject("Captain needs 'git' to create tar file of your source files...");
|
||||
return;
|
||||
}
|
||||
|
||||
exec(`git archive --format tar --output "${zipFileFullPath}" ${branchToPush}`, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
StdOutUtil.printError(`TAR file failed\n${err}\n`);
|
||||
|
||||
fs.removeSync(zipFileFullPath);
|
||||
|
||||
reject(new Error('TAR file failed'));
|
||||
return;
|
||||
}
|
||||
|
||||
exec(`git rev-parse ${branchToPush}`, (err, stdout, stderr) => {
|
||||
const gitHash = (stdout || '').trim();
|
||||
|
||||
if (err || !/^[a-f0-9]{40}$/.test(gitHash)) {
|
||||
StdOutUtil.printError(
|
||||
`Cannot find hash of last commit on this branch: ${branchToPush}\n${gitHash}\n${err}\n`
|
||||
);
|
||||
reject(new Error('rev-parse failed'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
StdOutUtil.printMessage(`Pushing last commit on ${branchToPush}: ${gitHash}`);
|
||||
resolve(gitHash);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getFileStream(zipFileFullPath: string) {
|
||||
const fileSize = fs.statSync(zipFileFullPath).size;
|
||||
const fileStream = fs.createReadStream(zipFileFullPath);
|
||||
const barOpts = {
|
||||
width: 20,
|
||||
total: fileSize,
|
||||
clear: false
|
||||
};
|
||||
const bar = new ProgressBar(' uploading [:bar] :percent (ETA :etas)', barOpts);
|
||||
|
||||
fileStream.on('data', (chunk) => {
|
||||
bar.tick(chunk.length);
|
||||
});
|
||||
|
||||
fileStream.on('end', () => {
|
||||
StdOutUtil.printMessage('This might take several minutes. PLEASE BE PATIENT...');
|
||||
|
||||
SpinnerHelper.start('Building your source code...\n');
|
||||
|
||||
SpinnerHelper.setColor('yellow');
|
||||
});
|
||||
|
||||
return fileStream;
|
||||
}
|
||||
|
||||
async startDeploy() {
|
||||
const appName = this.deployParams.appName;
|
||||
const branchToPush = this.deployParams.deploySource.branchToPush;
|
||||
const tarFilePath = this.deployParams.deploySource.tarFilePath;
|
||||
const machineToDeploy = this.deployParams.captainMachine;
|
||||
const deploySource = this.deployParams.deploySource;
|
||||
|
||||
if (!appName || (!branchToPush && !tarFilePath) || !machineToDeploy) {
|
||||
StdOutUtil.printError(
|
||||
'Default deploy failed. Missing appName or branchToPush/tarFilePath or machineToDeploy.',
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (branchToPush && tarFilePath) {
|
||||
StdOutUtil.printError('Default deploy failed. branchToPush/tarFilePath cannot both be present.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
let tarFileCreatedByCli = false;
|
||||
const tarFileNameToDeploy = tarFilePath ? tarFilePath : 'temporary-captain-to-deploy.tar';
|
||||
|
||||
const tarFileFullPath = tarFileNameToDeploy.startsWith('/')
|
||||
? tarFileNameToDeploy // absolute path
|
||||
: path.join(process.cwd(), tarFileNameToDeploy); // relative path
|
||||
|
||||
let gitHash = '';
|
||||
|
||||
if (branchToPush) {
|
||||
tarFileCreatedByCli = true;
|
||||
|
||||
StdOutUtil.printMessage(`Saving tar file to:\n${tarFileFullPath}\n`);
|
||||
|
||||
gitHash = await this.gitArchiveFile(tarFileFullPath, branchToPush);
|
||||
}
|
||||
|
||||
StdOutUtil.printMessage(`Deploying ${appName} to ${machineToDeploy.name}`);
|
||||
|
||||
try {
|
||||
StdOutUtil.printMessage(`Uploading the file to ${machineToDeploy.baseUrl}`);
|
||||
|
||||
await CliApiManager.get(machineToDeploy).uploadAppData(appName, this.getFileStream(tarFileFullPath));
|
||||
|
||||
StdOutUtil.printMessage(`Upload done.`);
|
||||
|
||||
StorageHelper.get().saveDeployedDirectory({
|
||||
appName: appName,
|
||||
cwd: process.cwd(),
|
||||
deploySource: deploySource,
|
||||
machineNameToDeploy: machineToDeploy.name
|
||||
});
|
||||
|
||||
if (tarFileCreatedByCli && fs.pathExistsSync(tarFileFullPath)) fs.removeSync(tarFileFullPath);
|
||||
|
||||
this.startFetchingBuildLogs(machineToDeploy, appName);
|
||||
} catch (e) {
|
||||
if (tarFileCreatedByCli && fs.pathExistsSync(tarFileFullPath)) fs.removeSync(tarFileFullPath);
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private async onLogRetrieved(data: IBuildLogs | undefined, machineToDeploy: IMachine, appName: string) {
|
||||
const self = this;
|
||||
if (data) {
|
||||
const lines = data.logs.lines;
|
||||
const firstLineNumberOfLogs = data.logs.firstLineNumber;
|
||||
let firstLinesToPrint = 0;
|
||||
|
||||
if (firstLineNumberOfLogs > this.lastLineNumberPrinted) {
|
||||
if (firstLineNumberOfLogs < 0) {
|
||||
// This is the very first fetch, probably firstLineNumberOfLogs is around -50
|
||||
firstLinesToPrint = -firstLineNumberOfLogs;
|
||||
} else {
|
||||
StdOutUtil.printMessage('[[ TRUNCATED ]]');
|
||||
}
|
||||
} else {
|
||||
firstLinesToPrint = this.lastLineNumberPrinted - firstLineNumberOfLogs;
|
||||
}
|
||||
|
||||
this.lastLineNumberPrinted = firstLineNumberOfLogs + lines.length;
|
||||
|
||||
for (let i = firstLinesToPrint; i < lines.length; i++) {
|
||||
StdOutUtil.printMessage((lines[i] || '').trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (data && !data.isAppBuilding) {
|
||||
if (!data.isBuildFailed) {
|
||||
const appUrl = self.deployParams.captainMachine!.baseUrl
|
||||
.replace('https://', 'http://')
|
||||
.replace('//captain.', '//' + appName + '.');
|
||||
StdOutUtil.printGreenMessage(`\n\n\nDeployed successfully: ${appName}`);
|
||||
StdOutUtil.printMagentaMessage(`App is available at ${appUrl}`, true);
|
||||
} else {
|
||||
StdOutUtil.printError(`\n\nSomething bad happened. Cannot deploy "${appName}"\n`, true);
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.startFetchingBuildLogs(machineToDeploy, appName);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
private async startFetchingBuildLogs(machineToDeploy: IMachine, appName: string) {
|
||||
const self = this;
|
||||
try {
|
||||
const data = await CliApiManager.get(machineToDeploy).fetchBuildLogs(appName);
|
||||
this.onLogRetrieved(data, machineToDeploy, appName);
|
||||
} catch (error) {
|
||||
StdOutUtil.printError(`\nSomething while retrieving app build logs.. ${error}\n`);
|
||||
this.onLogRetrieved(undefined, machineToDeploy, appName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
class ErrorFactory {
|
||||
public readonly OKAY = 100;
|
||||
public readonly OKAY_BUILD_STARTED = 101;
|
||||
|
||||
public readonly STATUS_ERROR_GENERIC = 1000;
|
||||
public readonly STATUS_ERROR_CAPTAIN_NOT_INITIALIZED = 1001;
|
||||
public readonly STATUS_ERROR_USER_NOT_INITIALIZED = 1101;
|
||||
public readonly STATUS_ERROR_NOT_AUTHORIZED = 1102;
|
||||
public readonly STATUS_ERROR_ALREADY_EXIST = 1103;
|
||||
public readonly STATUS_ERROR_BAD_NAME = 1104;
|
||||
public readonly STATUS_WRONG_PASSWORD = 1105;
|
||||
public readonly STATUS_AUTH_TOKEN_INVALID = 1106;
|
||||
public readonly VERIFICATION_FAILED = 1107;
|
||||
|
||||
public readonly UNKNOWN_ERROR = 1999;
|
||||
|
||||
constructor() {}
|
||||
|
||||
createError(status: number, message: string) {
|
||||
let e = new Error(message) as any;
|
||||
e.captainStatus = status;
|
||||
e.captainMessage = message;
|
||||
return e;
|
||||
}
|
||||
|
||||
eatUpPromiseRejection() {
|
||||
return function(error: any) {
|
||||
// nom nom
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new ErrorFactory();
|
||||
@@ -0,0 +1,15 @@
|
||||
export default class Logger {
|
||||
static log(s: string) {
|
||||
console.log(s);
|
||||
}
|
||||
|
||||
static error(s: any) {
|
||||
console.error(s);
|
||||
}
|
||||
|
||||
static dev(s: string) {
|
||||
if (process.env.CLI_IS_DEBUG) {
|
||||
console.log(">>> ", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
const ora = require('ora');
|
||||
|
||||
class SpinnerHelper {
|
||||
private spinner: any;
|
||||
|
||||
start(message: string) {
|
||||
this.spinner = ora(message).start();
|
||||
}
|
||||
|
||||
setColor(color: string) {
|
||||
this.spinner.color = color;
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.spinner.stop();
|
||||
}
|
||||
|
||||
succeed() {
|
||||
this.spinner.succeed();
|
||||
}
|
||||
|
||||
fail() {
|
||||
this.spinner.fail();
|
||||
}
|
||||
}
|
||||
|
||||
export default new SpinnerHelper();
|
||||
@@ -0,0 +1,41 @@
|
||||
const chalk = require('chalk');
|
||||
class StdOutUtils {
|
||||
printMessage(message: string) {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
printMessageAndExit(message: string) {
|
||||
console.log(message);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
printGreenMessage(message: string, exit = false) {
|
||||
console.log(`${chalk.green(message)}`);
|
||||
|
||||
exit && process.exit(0);
|
||||
}
|
||||
|
||||
printMagentaMessage(message: string, exit = false) {
|
||||
console.log(`${chalk.magenta(message)}`);
|
||||
|
||||
exit && process.exit(0);
|
||||
}
|
||||
|
||||
printError(error: string, exit = false) {
|
||||
console.log(`${chalk.bold.red(error)}`);
|
||||
|
||||
exit && process.exit(0);
|
||||
}
|
||||
|
||||
errorHandler(error: any) {
|
||||
if (error.captainStatus) {
|
||||
this.printError(`\nError Code: ${error.captainStatus} Message: ${error.captainMessage}`, true);
|
||||
} else if (error.status) {
|
||||
this.printError(`\nError status: ${error.status} Message: ${error.description || error.message}`, true);
|
||||
} else {
|
||||
this.printError(`\nError: ${error}`, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new StdOutUtils();
|
||||
@@ -0,0 +1,109 @@
|
||||
import { IMachine, IDeployedDirectory, IOldSavedApp } from '../models/storage/StoredObjects';
|
||||
import * as ConfigStore from 'configstore';
|
||||
import Utils from './Utils';
|
||||
|
||||
const CAP_MACHINES = 'CapMachines';
|
||||
const DEPLOYED_DIRS = 'DeployedDirs';
|
||||
|
||||
export default class StorageHelper {
|
||||
static instance: StorageHelper;
|
||||
|
||||
static get() {
|
||||
if (!StorageHelper.instance) StorageHelper.instance = new StorageHelper();
|
||||
return StorageHelper.instance;
|
||||
}
|
||||
|
||||
private data: ConfigStore;
|
||||
|
||||
constructor() {
|
||||
this.data = new ConfigStore('captainduckduck');
|
||||
this.migrateData();
|
||||
}
|
||||
|
||||
migrateData() {
|
||||
const self = this;
|
||||
const data = this.data;
|
||||
const oldMachines: any[] = data.get('captainMachines') || [];
|
||||
const oldApps: IOldSavedApp[] = data.get('apps') || [];
|
||||
oldMachines.forEach((m) => {
|
||||
self.saveMachine({
|
||||
authToken: m.authToken,
|
||||
baseUrl: m.baseUrl,
|
||||
name: m.name
|
||||
});
|
||||
});
|
||||
|
||||
oldApps.forEach((app) => {
|
||||
self.saveDeployedDirectory({
|
||||
appName: app.appName,
|
||||
cwd: app.cwd,
|
||||
machineNameToDeploy: app.machineToDeploy.name,
|
||||
deploySource: {
|
||||
branchToPush: app.branchToPush
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
data.delete('captainMachines');
|
||||
data.delete('apps');
|
||||
}
|
||||
|
||||
getMachines(): IMachine[] {
|
||||
return Utils.copyObject(this.data.get(CAP_MACHINES) || []);
|
||||
}
|
||||
|
||||
findMachine(machineName: string) {
|
||||
return this.getMachines().find((m) => m.name === machineName);
|
||||
}
|
||||
|
||||
removeMachine(machineName: string) {
|
||||
const machines = this.getMachines();
|
||||
const removedMachine = machines.filter((machine) => machine.name === machineName)[0];
|
||||
const newMachines = machines.filter((machine) => machine.name !== machineName);
|
||||
this.data.set(CAP_MACHINES, newMachines);
|
||||
|
||||
return removedMachine;
|
||||
}
|
||||
|
||||
saveMachine(machineToSaveOrUpdate: IMachine) {
|
||||
const currMachines = this.getMachines();
|
||||
let updatedMachine = false;
|
||||
for (let index = 0; index < currMachines.length; index++) {
|
||||
const element = currMachines[index];
|
||||
if (element.name === machineToSaveOrUpdate.name) {
|
||||
updatedMachine = true;
|
||||
currMachines[index] = machineToSaveOrUpdate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updatedMachine) {
|
||||
currMachines.push(machineToSaveOrUpdate);
|
||||
}
|
||||
|
||||
this.data.set(CAP_MACHINES, currMachines);
|
||||
}
|
||||
|
||||
getDeployedDirectories(): IDeployedDirectory[] {
|
||||
return Utils.copyObject(this.data.get(DEPLOYED_DIRS) || []);
|
||||
}
|
||||
|
||||
saveDeployedDirectory(directoryToSaveOrUpdate: IDeployedDirectory) {
|
||||
const currDirs = this.getDeployedDirectories();
|
||||
let updatedDir = false;
|
||||
for (let index = 0; index < currDirs.length; index++) {
|
||||
const element = currDirs[index];
|
||||
if (element.cwd === directoryToSaveOrUpdate.cwd) {
|
||||
updatedDir = true;
|
||||
currDirs[index] = directoryToSaveOrUpdate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updatedDir) {
|
||||
currDirs.push(directoryToSaveOrUpdate);
|
||||
}
|
||||
|
||||
this.data.set(DEPLOYED_DIRS, currDirs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
export default {
|
||||
copyObject<T>(obj: T): T {
|
||||
return JSON.parse(JSON.stringify(obj)) as T;
|
||||
},
|
||||
|
||||
generateUuidV4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
|
||||
getAnsiColorRegex() {
|
||||
const pattern = [
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|');
|
||||
|
||||
return new RegExp(pattern, 'g');
|
||||
},
|
||||
|
||||
cleanUpUrl(urlInput: string) {
|
||||
if (!urlInput || !urlInput.length) return null;
|
||||
|
||||
let cleanedUrl = urlInput;
|
||||
|
||||
if (cleanedUrl.indexOf('#') >= 0) cleanedUrl = cleanedUrl.substr(0, cleanedUrl.indexOf('#'));
|
||||
|
||||
const hasSlashAtTheEnd = cleanedUrl.substr(cleanedUrl.length - 1, 1) === '/';
|
||||
|
||||
if (hasSlashAtTheEnd) {
|
||||
// Remove the slash at the end
|
||||
cleanedUrl = cleanedUrl.substr(0, cleanedUrl.length - 1);
|
||||
}
|
||||
|
||||
cleanedUrl = cleanedUrl.replace('http://', '').replace('https://', '').trim();
|
||||
|
||||
return cleanedUrl;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import StdOutUtil from './StdOutUtil';
|
||||
import { IMachine } from '../models/storage/StoredObjects';
|
||||
import CliApiManager from '../api/CliApiManager';
|
||||
import requestLogin from '../commands/requestLogin';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
|
||||
export function validateIsGitRepository() {
|
||||
const gitFolderExists = fs.pathExistsSync('./.git');
|
||||
|
||||
if (!gitFolderExists) {
|
||||
StdOutUtil.printError(
|
||||
'\n**** ERROR: You are not in a git root directory. This command will only deploys the current directory ****\n',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return !!gitFolderExists;
|
||||
}
|
||||
|
||||
export function validateDefinitionFile() {
|
||||
const captainDefinitionExists = fs.pathExistsSync('./captain-definition');
|
||||
|
||||
if (!captainDefinitionExists) {
|
||||
StdOutUtil.printError('\n**** ERROR: captain-definition file cannot be found. Please see docs! ****\n', true);
|
||||
} else {
|
||||
const contents = fs.readFileSync('./captain-definition', 'utf8');
|
||||
let contentsJson = null;
|
||||
|
||||
try {
|
||||
contentsJson = JSON.parse(contents);
|
||||
} catch (e) {
|
||||
StdOutUtil.printError(`**** ERROR: captain-definition file is not a valid JSON! ****\n Error:${e}`, true);
|
||||
}
|
||||
|
||||
if (contentsJson) {
|
||||
if (!contentsJson.schemaVersion) {
|
||||
StdOutUtil.printError(
|
||||
'**** ERROR: captain-definition needs schemaVersion. Please see docs! ****',
|
||||
true
|
||||
);
|
||||
} else if (!contentsJson.templateId && !contentsJson.dockerfileLines) {
|
||||
StdOutUtil.printError(
|
||||
'**** ERROR: captain-definition needs templateId or dockerfileLines. Please see docs! ****',
|
||||
true
|
||||
);
|
||||
} else if (contentsJson.templateId && contentsJson.dockerfileLines) {
|
||||
StdOutUtil.printError(
|
||||
'**** ERROR: captain-definition needs templateId or dockerfileLines, NOT BOTH! Please see docs! ****',
|
||||
true
|
||||
);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isIpAddress(ipaddress: string) {
|
||||
if (
|
||||
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
|
||||
ipaddress
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
export async function ensureAuthentication(machine: IMachine) {
|
||||
let isAuthenticated = false;
|
||||
let allApps = undefined;
|
||||
try {
|
||||
allApps = await CliApiManager.get(machine).getAllApps();
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (!allApps) {
|
||||
const loggedInStatus = await requestLogin(machine);
|
||||
allApps = await CliApiManager.get(machine).getAllApps();
|
||||
}
|
||||
|
||||
return allApps
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./built",
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"target": "es6"
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"rules": {
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
],
|
||||
"no-var-keyword": true,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single",
|
||||
"avoid-escape"
|
||||
],
|
||||
"semicolon": [
|
||||
true,
|
||||
"never",
|
||||
"ignore-bound-class-methods"
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-module",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
],
|
||||
"no-internal-module": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-null-keyword": true,
|
||||
//"prefer-const": true,
|
||||
"jsdoc-format": true
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
const ora = require('ora');
|
||||
|
||||
function start(message) {
|
||||
return ora(message).start()
|
||||
}
|
||||
|
||||
function stop(spinner) {
|
||||
spinner.stop();
|
||||
}
|
||||
|
||||
function succeed(spinner) {
|
||||
spinner.succeed();
|
||||
}
|
||||
|
||||
function fail(spinner) {
|
||||
spinner.fail();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
start: start,
|
||||
stop: stop,
|
||||
succeed: succeed,
|
||||
fail: fail,
|
||||
}
|
||||
+5869
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -9,7 +9,9 @@ Note that this is an advanced process. Some of the concepts used in this section
|
||||
|
||||
- Docker installed on your machine.
|
||||
- A local DNS server on your machine. You need to point `*.captain.x` to `127.0.0.1` or `192.168.1.2` (your local ip). **NOTE** that `etc/hosts` won't be enough as Captain needs a wildcard entry and `etc/hosts` does not allow wildcards, i.e. `*.something`.
|
||||
- On ubuntu 16, `dnsmasq` (a local DNS server) is built-in. So, it's as simple of editing this file: `/etc/NetworkManager/dnsmasq.d/dnsmasq-localhost.conf` (create if does not exist) And add this line to it: `address=/captain.x/192.168.1.2` where `192.168.1.2` is your local IP address. To make sure you have `dnsmasq`, you can run `which dnsmasq` on your terminal, if it's available, path of it will be printed on the terminal, otherwise, there won't be anything printed on your terminal
|
||||
- On ubuntu 16, `dnsmasq` (a local DNS server) is built-in. So, it's as simple of editing this file: `/etc/NetworkManager/dnsmasq.d/dnsmasq-localhost.conf` (create if does not exist) And add this line to it: `address=/captain.x/192.168.1.2` where `192.168.1.2` is your local IP address. To make sure you have `dnsmasq`, you can run `which dnsmasq` on your terminal, if it's available, path of it will be printed on the terminal, otherwise, there won't be anything printed on your terminal.
|
||||
|
||||
Note: For Ubuntu 18, read https://askubuntu.com/questions/1029882/how-can-i-set-up-local-wildcard-127-0-0-1-domain-resolution-on-18-04
|
||||
|
||||
To verify you have both prerequisites mentioned above:
|
||||
- Run `docker version` and make sure your version is at least the version mentioned in the [docs](get-started.md#c-install-docker-on-server-at-least-version-1706x)
|
||||
|
||||
Reference in New Issue
Block a user