Skip to content

Commit

Permalink
feat(cubejs-cli): Completely move CLI to TypeScript (#1281)
Browse files Browse the repository at this point in the history
* feat(cubejs-cli): Move token command to TS

* feat(cubejs-cli): Extract auth command to own module

* feat(cubejs-cli): Migra cli.js to TS

* feat(cubejs-cli): Move the last part to TS

* fix(cubejs-cli): expiry/secret are optional

* feat(linter): Improve configuration for TS

* misc(cubejs-cli): Fix lint errors
  • Loading branch information
ovr committed Nov 3, 2020
1 parent cb64118 commit dd5f3e2
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 186 deletions.
88 changes: 0 additions & 88 deletions packages/cubejs-cli/src/cli.js

This file was deleted.

39 changes: 39 additions & 0 deletions packages/cubejs-cli/src/cli.ts
@@ -0,0 +1,39 @@
import program from 'commander';

import { configureDevServerCommand } from './command/dev-server';
import { configureServerCommand } from './command/server';
import { configureDeployCommand } from './command/deploy';
import { configureCreateCommand } from './command/create';
import { configureGenerateCommand } from './command/generate';
import { configureTokenCommand } from './command/token';
import { configureAuthCommand } from './command/auth';
import { loadCliManifest } from './utils';

const packageJson = loadCliManifest();

program.name(Object.keys(packageJson.bin)[0])
.version(packageJson.version);

program
.usage('<command> [options]')
.on('--help', () => {
console.log('');
console.log('Use cubejs <command> --help for more information about a command.');
console.log('');
});

(async () => {
await configureAuthCommand(program);
await configureTokenCommand(program);
await configureCreateCommand(program);
await configureGenerateCommand(program);
await configureDeployCommand(program);
await configureDevServerCommand(program);
await configureServerCommand(program);

if (!process.argv.slice(2).length) {
program.help();
}

program.parse(process.argv);
})();
28 changes: 28 additions & 0 deletions packages/cubejs-cli/src/command/auth.ts
@@ -0,0 +1,28 @@
import type { CommanderStatic } from 'commander';
import { displayError, event } from '../utils';
import { Config } from '../config';

const authenticate = async (currentToken: string) => {
const config = new Config();
await config.addAuthToken(currentToken);

await event('Cube Cloud CLI Authenticate');
console.log('Token successfully added!');
};

export function configureAuthCommand(program: CommanderStatic): void {
program
.command('auth <token>')
.description('Authenticate access to Cube Cloud')
.action(
(currentToken) => authenticate(currentToken)
.catch(e => displayError(e.stack || e))
)
.on('--help', () => {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ cubejs auth eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXBsb3ltZW50SWQiOiIxIiwidXJsIjoiaHR0cHM6Ly9leGFtcGxlcy5jdWJlY2xvdWQuZGV2IiwiaWF0IjoxNTE2MjM5MDIyfQ.La3MiuqfGigfzADl1wpxZ7jlb6dY60caezgqIOoHt-c');
console.log(' $ cubejs deploy');
});
}
4 changes: 2 additions & 2 deletions packages/cubejs-cli/src/command/deploy.ts
Expand Up @@ -21,7 +21,6 @@ const deploy = async ({ directory, auth, uploadEnv, token }: any) => {
}

const config = new Config();
await config.loadDeployAuth();
const bar = new cliProgress.SingleBar({
format: '- Uploading files | {bar} | {percentage}% || {value} / {total} | {file}',
barCompleteChar: '\u2588',
Expand All @@ -31,6 +30,7 @@ const deploy = async ({ directory, auth, uploadEnv, token }: any) => {

const deployDir = new DeployDirectory({ directory });
const fileHashes: any = await deployDir.fileHashes();

const upstreamHashes = await config.cloudReq({
url: (deploymentId: string) => `build/deploy/${deploymentId}/files`,
method: 'GET',
Expand All @@ -54,7 +54,7 @@ const deploy = async ({ directory, auth, uploadEnv, token }: any) => {
auth
});
}

await logStage(`Deploying ${deploymentName}...`, 'Cube Cloud CLI Deploy');

const files = Object.keys(fileHashes);
Expand Down
85 changes: 85 additions & 0 deletions packages/cubejs-cli/src/command/token.ts
@@ -0,0 +1,85 @@
import chalk from 'chalk';
import jwt from 'jsonwebtoken';
import type { CommanderStatic } from 'commander';
import { displayError, event, requireFromPackage } from '../utils';

export const defaultExpiry = '30 days';

const parsePayload = (payloadArray: string[] = []) => {
const result = {};

payloadArray.forEach((entry = '') => {
const [key, value] = entry.split('=');
if (key && value) result[key] = value;
});

return result;
};

type TokenOptions = {
expiry?: string;
secret?: string;
expiresIn?: string
payload: string[]
userContext: string[]
};

export const token = async (options: TokenOptions) => {
event('Generate Token');

const CubejsServer = await requireFromPackage('@cubejs-backend/server');
const { expiry = defaultExpiry, secret = CubejsServer.apiSecret() } = options;

if (!secret) {
throw new Error('No app secret found');
}

const extraOptions: Record<string, string> = {};

if (expiry !== '0') {
extraOptions.expiresIn = expiry;
}

const payload = {
...parsePayload(options.payload),
u: parsePayload(options.userContext),
};

console.log('Generating Cube.js JWT token');
console.log('');
console.log(`${chalk.yellow('-----------------------------------------------------------------------------------------')}`);
console.log(` ${chalk.yellow('Use these manually generated tokens in production with caution.')}`);
console.log(` ${chalk.yellow(`Please refer to ${chalk.cyan('https://cube.dev/docs/security')} for production security best practices.`)}`);
console.log(`${chalk.yellow('-----------------------------------------------------------------------------------------')}`);
console.log('');
console.log(`Expires in: ${chalk.green(expiry)}`);
console.log(`Payload: ${chalk.green(JSON.stringify(payload))}`);
console.log('');

const signedToken = jwt.sign(payload, secret, extraOptions);
console.log(`Token: ${chalk.green(signedToken)}`);
await event('Generate Token Success');
return signedToken;
};

export const collect = (val, memo) => [val, ...memo];

export function configureTokenCommand(program: CommanderStatic) {
program
.command('token')
.option('-e, --expiry [expiry]', 'Token expiry. Set to 0 for no expiry')
.option('-s, --secret [secret]', 'Cube.js app secret. Also can be set via environment variable CUBEJS_API_SECRET')
.option('-p, --payload [values]', 'Payload. Example: -p foo=bar', collect, [])
.option('-u, --user-context [values]', 'USER_CONTEXT. Example: -u baz=qux', collect, [])
.description('Create JWT token')
.action(
(options) => token(options)
.catch(e => displayError(e.stack || e))
)
.on('--help', () => {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ cubejs token -e "1 day" -p foo=bar -p cool=true');
});
}

0 comments on commit dd5f3e2

Please sign in to comment.