Merge branch 'master' into feature/v1.0

This commit is contained in:
Kasra Bigdeli
2019-01-06 19:34:54 -08:00
100 changed files with 9773 additions and 2760 deletions
+5
View File
@@ -0,0 +1,5 @@
package.json
package-lock.json
node_modules/
coverage/
dist/
+7
View File
@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}
+277
View File
@@ -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
File diff suppressed because one or more lines are too long
+32
View File
@@ -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
+1
View File
@@ -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"}
+128
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"HttpClient.js","sourceRoot":"","sources":["../../src/api/HttpClient.ts"],"names":[],"mappings":";;AAAA,wDAAiD;AACjD,4CAAqC;AACrC,2CAA2C;AAE3C,IAAI,YAAY,GAAG,gBAAgB,CAAC;AACpC,IAAI,SAAS,GAAG,aAAa,CAAC;AAC9B,IAAI,OAAO,GAAG,SAAS,CAAC;AAExB,MAAqB,UAAU;IAM9B,YAAoB,OAAe,EAAU,SAAiB,EAAU,aAAiC;QAArF,YAAO,GAAP,OAAO,CAAQ;QAAU,cAAS,GAAT,SAAS,CAAQ;QAAU,kBAAa,GAAb,aAAa,CAAoB;QALzF,QAAG,GAAG,KAAK,CAAC;QACZ,SAAI,GAAG,MAAM,CAAC;QACd,cAAS,GAAG,WAAW,CAAC;QACjC,gBAAW,GAAG,KAAK,CAAC;QAG1B,EAAE;IACH,CAAC;IAED,aAAa;QACZ,IAAI,OAAO,GAAQ,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3D,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC;QAE7B,iFAAiF;QACjF,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,YAAY,CAAC,SAAiB;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED,OAAO;QACN,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,MAAoC,EAAE,QAAgB,EAAE,SAAc;QAC3E,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,OAAO;YACN,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE;iBACzB,IAAI,CAAC;gBACL,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB;oBAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC9D,OAAO,IAAI,OAAO,CAAO,UAAS,GAAG;oBACpC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACtB,CAAC,CAAC,CAAC;YACJ,CAAC,CAAC;iBACD,IAAI,CAAC;gBACL,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE;YAC3D,CAAC,CAAC;iBACD,IAAI,CAAC,UAAS,eAAe;gBAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;gBACzC,IAAI,IAAI,CAAC,MAAM,KAAK,sBAAY,CAAC,yBAAyB,EAAE;oBAC3D,OAAO,IAAI;yBACT,aAAa,EAAE,CAAC,EAAE;yBAClB,IAAI,CAAC;wBACL,OAAO,IAAI;6BACT,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC;6BAC1C,IAAI,CAAC,UAAS,kBAAkB;4BAChC,OAAO,kBAAkB,CAAC;wBAC3B,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;iBACJ;qBAAM;oBACN,OAAO,IAAI,CAAC;iBACZ;YACF,CAAC,CAAC;iBACD,IAAI,CAAC,UAAS,IAAI;gBAClB,IAAI,IAAI,CAAC,MAAM,KAAK,sBAAY,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,sBAAY,CAAC,kBAAkB,EAAE;oBACzF,MAAM,sBAAY,CAAC,WAAW,CAC7B,IAAI,CAAC,MAAM,IAAI,sBAAY,CAAC,aAAa,EACzC,IAAI,CAAC,WAAW,IAAI,EAAE,CACtB,CAAC;iBACF;gBACD,OAAO,IAAI,CAAC;YACb,CAAC,CAAC;iBACD,IAAI,CAAC,UAAS,IAAI;gBAClB,sIAAsI;gBACtI,sHAAsH;gBACtH,uIAAuI;gBACvI,gCAAgC;gBAChC,OAAO,IAAI,OAAO,CAAC,UAAS,OAAO,EAAE,MAAM;oBAC1C,uHAAuH;oBACvH,IAAI,CAAC,IAAI,CAAC,WAAW;wBAAE,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,4BAA4B;oBACvG,gBAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;gBACzC,CAAC,CAAC,CAAC;YACJ,CAAC,CAAC;iBACD,KAAK,CAAC,UAAS,KAAK;gBACpB,kBAAkB;gBAClB,wCAAwC;gBACxC,OAAO,IAAI,OAAO,CAAC,UAAS,OAAO,EAAE,MAAM;oBAC1C,IAAI,CAAC,IAAI,CAAC,WAAW;wBAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;oBAC5C,gBAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBAC1C,CAAC,CAAC,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;IACH,CAAC;IAED,aAAa,CAAC,MAAoC,EAAE,QAAgB,EAAE,SAAc;QACnF,IAAI,MAAM,KAAK,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAEjE,IAAI,MAAM,KAAK,IAAI,CAAC,IAAI,IAAI,MAAM,KAAK,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAExG,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,MAAM,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,QAAgB,EAAE,SAAc;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC;QAElB,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,QAAQ,EAAE;YAC3C,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE;YAC7B,EAAE,EAAE,SAAS;SACb,CAAC,CAAC,IAAI,CAAC,UAAS,IAAI;YACpB,OAAO,IAAI,CAAC;QACb,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,SAAc,EAAE,MAAoC;QAC7E,MAAM,IAAI,GAAG,IAAI,CAAC;QAElB,IAAI,MAAM,KAAK,IAAI,CAAC,SAAS;YAC5B,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,QAAQ,EAAE;gBAC5C,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE;gBAC7B,QAAQ,EAAE,SAAS;aACnB,CAAC,CAAC,IAAI,CAAC,UAAS,IAAI;gBACpB,OAAO,IAAI,CAAC;YACb,CAAC,CAAC,CAAC;QAEJ,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,QAAQ,EAAE;YAC5C,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE;YAC7B,IAAI,EAAE,SAAS;SACf,CAAC,CAAC,IAAI,CAAC,UAAS,IAAI;YACpB,OAAO,IAAI,CAAC;QACb,CAAC,CAAC,CAAC;IACJ,CAAC;CACD;AA5HD,6BA4HC"}
+55
View File
@@ -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
View File
@@ -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"}
+145
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
+18
View File
@@ -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
+1
View File
@@ -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"}
+105
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
+47
View File
@@ -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
+1
View File
@@ -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"}
+38
View File
@@ -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
+1
View File
@@ -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"}
+203
View File
@@ -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
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=AppDef.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"AppDef.js","sourceRoot":"","sources":["../../src/models/AppDef.ts"],"names":[],"mappings":""}
+4
View File
@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
;
//# sourceMappingURL=IBuildLogs.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"IBuildLogs.js","sourceRoot":"","sources":["../../src/models/IBuildLogs.ts"],"names":[],"mappings":";;AAOC,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ICaptainDefinition.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"ICaptainDefinition.js","sourceRoot":"","sources":["../../src/models/ICaptainDefinition.ts"],"names":[],"mappings":""}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=IHashMapGeneric.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"IHashMapGeneric.js","sourceRoot":"","sources":["../../src/models/IHashMapGeneric.ts"],"names":[],"mappings":""}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=IOneClickAppModels.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"IOneClickAppModels.js","sourceRoot":"","sources":["../../src/models/IOneClickAppModels.ts"],"names":[],"mappings":""}
+8
View File
@@ -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
+1
View File
@@ -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"}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=IVersionInfo.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"IVersionInfo.js","sourceRoot":"","sources":["../../src/models/IVersionInfo.ts"],"names":[],"mappings":""}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=StoredObjects.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"StoredObjects.js","sourceRoot":"","sources":["../../../src/models/storage/StoredObjects.ts"],"names":[],"mappings":""}
+81
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"CliHelper.js","sourceRoot":"","sources":["../../src/utils/CliHelper.ts"],"names":[],"mappings":"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"}
+17
View File
@@ -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
+1
View File
@@ -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"}
+183
View File
@@ -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
File diff suppressed because one or more lines are too long
+31
View File
@@ -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
+1
View File
@@ -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"}
+17
View File
@@ -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
+1
View File
@@ -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"}
+22
View File
@@ -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
+1
View File
@@ -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"}
+37
View File
@@ -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
+1
View File
@@ -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"}
+92
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"StorageHelper.js","sourceRoot":"","sources":["../../src/utils/StorageHelper.ts"],"names":[],"mappings":";;AACA,2CAA2C;AAC3C,mCAA4B;AAE5B,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,aAAa,GAAG,cAAc,CAAC;AAErC,MAAqB,aAAa;IAGjC,MAAM,CAAC,GAAG;QACT,IAAI,CAAC,aAAa,CAAC,QAAQ;YAAE,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QAC1E,OAAO,aAAa,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAID;QACC,IAAI,CAAC,IAAI,GAAG,IAAI,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;IACpB,CAAC;IAED,WAAW;QACV,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,MAAM,WAAW,GAAU,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAmB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACvD,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACzB,IAAI,CAAC,WAAW,CAAC;gBAChB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,IAAI,EAAE,CAAC,CAAC,IAAI;aACZ,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACvB,IAAI,CAAC,qBAAqB,CAAC;gBAC1B,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,mBAAmB,EAAE,GAAG,CAAC,eAAe,CAAC,IAAI;gBAC7C,YAAY,EAAE;oBACb,YAAY,EAAE,GAAG,CAAC,YAAY;iBAC9B;aACD,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC;IAED,WAAW;QACV,OAAO,eAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,WAAW,CAAC,WAAmB;QAC9B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IAC/D,CAAC;IAED,aAAa,CAAC,WAAmB;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACpC,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QACrF,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;QAC/E,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QAEzC,OAAO,cAAc,CAAC;IACvB,CAAC;IAED,WAAW,CAAC,qBAA+B;QAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACzD,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,OAAO,CAAC,IAAI,KAAK,qBAAqB,CAAC,IAAI,EAAE;gBAChD,cAAc,GAAG,IAAI,CAAC;gBACtB,YAAY,CAAC,KAAK,CAAC,GAAG,qBAAqB,CAAC;gBAC5C,MAAM;aACN;SACD;QAED,IAAI,CAAC,cAAc,EAAE;YACpB,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;SACzC;QAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC;IAED,sBAAsB;QACrB,OAAO,eAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,qBAAqB,CAAC,uBAA2C;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC/C,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACrD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,OAAO,CAAC,GAAG,KAAK,uBAAuB,CAAC,GAAG,EAAE;gBAChD,UAAU,GAAG,IAAI,CAAC;gBAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,uBAAuB,CAAC;gBAC1C,MAAM;aACN;SACD;QAED,IAAI,CAAC,UAAU,EAAE;YAChB,QAAQ,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;SACvC;QAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;CACD;AArGD,gCAqGC"}
+35
View File
@@ -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
+1
View File
@@ -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"}
+80
View File
@@ -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
View File
@@ -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"}
-748
View File
@@ -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);
});
}
-40
View File
@@ -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();
-225
View File
@@ -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);
});
-91
View File
@@ -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(' ');
});
-466
View File
@@ -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(' ');
});
-22
View File
@@ -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);
-1138
View File
File diff suppressed because it is too large Load Diff
+32 -4
View File
@@ -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
View File
@@ -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.
+390
View File
@@ -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
})
);
}
}
+35
View File
@@ -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];
}
}
+133
View File
@@ -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;
});
}
}
+67
View File
@@ -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);
+154
View File
@@ -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;
+22
View File
@@ -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;
+108
View File
@@ -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;
+43
View File
@@ -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;
+29
View File
@@ -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);
}
+230
View File
@@ -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;
+93
View File
@@ -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;
}
+8
View File
@@ -0,0 +1,8 @@
export default interface IBuildLogs {
isAppBuilding: boolean;
isBuildFailed: boolean;
logs: {
firstLineNumber: number;
lines: string[];
};
};
+6
View File
@@ -0,0 +1,6 @@
export interface ICaptainDefinition {
schemaVersion: number
dockerfileLines?: string[]
imageName?: string
templateId?: string
}
+3
View File
@@ -0,0 +1,3 @@
export interface IHashMapGeneric<T> {
[id: string]: T
}
+36
View File
@@ -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[];
}
+20
View File
@@ -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;
}
+5
View File
@@ -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;
}
+93
View File
@@ -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;
}
}
+15
View File
@@ -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
}
+206
View File
@@ -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);
}
}
}
+34
View File
@@ -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();
+15
View File
@@ -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);
}
}
}
+27
View File
@@ -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();
+41
View File
@@ -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();
+109
View File
@@ -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);
}
}
+41
View File
@@ -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;
}
};
+87
View File
@@ -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
}
+15
View File
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"strictNullChecks": true,
"outDir": "./built",
"noImplicitAny": true,
"sourceMap": true,
"allowJs": true,
"target": "es6"
},
"include": [
"./src/**/*"
]
}
+60
View File
@@ -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
}
}
-24
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -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)