@cto.ai/ops
Version:
💻 CTO.ai - The CLI built for Teams 🚀
176 lines (175 loc) • 7.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const cli_sdk_1 = require("@cto.ai/cli-sdk");
const parser_1 = require("@oclif/parser");
const fs = tslib_1.__importStar(require("fs-extra"));
const path = tslib_1.__importStar(require("path"));
const base_1 = tslib_1.__importDefault(require("../base"));
const opConfig_1 = require("../constants/opConfig");
const utils_1 = require("../utils");
const runUtils_1 = require("../utils/runUtils");
const CustomErrors_1 = require("../errors/CustomErrors");
class Certs extends base_1.default {
constructor() {
super(...arguments);
this.sendAnalytics = async (inputs) => {
try {
this.services.analytics.track('Ops CLI Certificates Saved', {
certKeyName: inputs.certKeyName,
keyKeyName: inputs.keyKeyName,
username: inputs.config.user.username,
}, inputs.config);
}
catch (err) {
this.debug('%O', err);
throw new CustomErrors_1.AnalyticsError(err);
}
return inputs;
};
this.setSecrets = async (inputs) => {
try {
await this.services.api.create(`/private/teams/${inputs.config.team.name}/secrets`, {
secrets: {
[inputs.certKeyName]: inputs.certFile,
[inputs.keyKeyName]: inputs.keyFile,
},
}, {
headers: { Authorization: this.accessToken },
});
}
catch (err) {
this.debug('%O', err);
switch (err.error[0].code) {
case 400:
throw new CustomErrors_1.InvalidSecretVault(err);
case 401:
throw new CustomErrors_1.UserUnauthorized(err);
case 403:
if (err.error[0].message.includes('invalid secret token')) {
throw new CustomErrors_1.InvalidSecretToken(err);
}
else {
throw new CustomErrors_1.NoSecretsProviderFound(err);
}
default:
throw new CustomErrors_1.SetSecretsProvider(err);
}
}
return inputs;
};
this.loadContents = (key, prompt) => async (inputs) => {
let filepath = inputs[key];
if (!filepath) {
const { providedPath } = await this.ux.prompt({
message: `\n${prompt} ${cli_sdk_1.ux.colors.reset.green('→')}`,
name: 'providedPath',
type: 'input',
validate: this.validateValueInput,
});
filepath = providedPath;
}
const content = await fs.readFile(path.resolve(filepath), 'utf8');
return Object.assign(Object.assign({}, inputs), { [key]: content });
};
this.logMessage = (inputs) => {
this.log(`\n ${cli_sdk_1.ux.colors.white(`🙌 Great job! Certificate ${cli_sdk_1.ux.colors.callOutCyan(inputs.certKeyName)} and key ${cli_sdk_1.ux.colors.callOutCyan(inputs.keyKeyName)} have been added to your team ${cli_sdk_1.ux.colors.blueBright(inputs.config.team.name)}!`)}`);
return inputs;
};
this.validateValueInput = async (input) => {
if (!input) {
return `😞 Sorry, the value cannot be empty`;
}
return true;
};
this.createKeysForSecret = async (inputs) => {
this.debug(`Loading YAML file from dir ${inputs.opsYmlPath}`);
const { services } = await this.parseYamlFile(inputs.opsYmlPath);
if (services == undefined || services.length === 0) {
throw new CustomErrors_1.ServiceRequired();
}
// Yes, only one service allowed
const { domain } = services[0];
if (domain == undefined || domain === '') {
throw new CustomErrors_1.NoDomainConfigured();
}
return Object.assign(Object.assign({}, inputs), { certKeyName: `ctoai-ssl-cert-${domain}`, keyKeyName: `ctoai-ssl-key-${domain}` });
};
this.parseYamlFile = async (opsPath) => {
let content = '';
try {
content = await fs.readFile(path.join(path.resolve(opsPath), opConfig_1.OP_FILE), 'utf8');
}
catch (err) {
console.error('Pretty sure there is a better way to do this..', err);
}
return (0, utils_1.parseYaml)(content);
};
this.customParse = (options, argv) => {
const { args, flags: parsedFlags } = (0, parser_1.parse)(argv, Object.assign(Object.assign({}, options), { context: this }));
if (!args.nameOrPath && !parsedFlags.help) {
throw new CustomErrors_1.MissingRequiredArgument('ops cert');
}
if (!args.nameOrPath) {
this._help();
}
return { args, flags: parsedFlags, opParams: argv.slice(1) };
};
}
async run() {
const config = await this.isLoggedIn();
try {
const { args: { nameOrPath }, flags: { ['cert-file']: certFile, ['key-file']: keyFile }, } = this.customParse(Certs, this.argv);
this.debug('inputs', certFile, keyFile);
const pipelineInput = {
certFile,
certKeyName: '',
config,
keyFile,
keyKeyName: '',
opsYmlPath: (0, runUtils_1.checkPathOpsYmlExists)(nameOrPath)
? nameOrPath
: process.cwd(),
};
await (0, utils_1.asyncPipe)(this.createKeysForSecret, this.loadContents('certFile', 'Enter the path to the SSL certificate'), this.loadContents('keyFile', 'Enter the path to the SSL key'), this.setSecrets, this.sendAnalytics, this.logMessage)(pipelineInput);
}
catch (err) {
this.debug('%O', err);
this.config.runHook('error', {
accessToken: config.tokens.accessToken,
err,
});
}
}
}
exports.default = Certs;
Certs.description = 'Save an SSL certificate and key for your service';
Certs.flags = {
'cert-file': parser_1.flags.string({
description: 'Path to your certificate file',
}),
help: parser_1.flags.boolean({
char: 'h',
description: 'Show help screen',
}),
'key-file': parser_1.flags.string({
description: 'Path to your key file',
}),
};
Certs.args = [
{
description: 'The type of certificate to store',
name: 'certificateType',
options: ['ssl'],
parse: (input) => input,
required: true,
},
{
description: 'Name or path of the service to save SSL for.',
name: 'nameOrPath',
parse: (input) => {
return input.toLowerCase();
},
required: true,
},
];