Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/commands/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import { DEFAULT_API_URL, setConfig } from '../config';
export default class Auth extends CliCommand {
static description = `Log in to the Cloud`;

static examples = ['sqd auth -k sqd_xyz123...'];
static examples = ['sqd auth -k sqd_xyz123...', 'sqd auth -k sqd_xyz123... --profile work'];

static flags = {
key: Flags.string({
char: 'k',
description: 'Cloud auth key. Log in to https://cloud.sqd.dev to create or update your key.',
required: true,
}),
profile: Flags.string({
char: 'p',
description: 'Profile name to save credentials under. Defaults to the current active profile.',
required: false,
}),
host: Flags.string({
char: 'h',
hidden: true,
Expand All @@ -25,7 +30,7 @@ export default class Auth extends CliCommand {

async run(): Promise<void> {
const {
flags: { key, host },
flags: { key, host, profile: profileName },
} = await this.parse(Auth);

const { username, email } = await profile({
Expand All @@ -35,8 +40,9 @@ export default class Auth extends CliCommand {
},
});

setConfig(key, host);
setConfig(key, host, profileName);

this.log(`Successfully logged as ${email || username}`);
const savedAs = profileName ? ` (profile: ${profileName})` : '';
this.log(`Successfully logged as ${email || username}${savedAs}`);
}
}
63 changes: 63 additions & 0 deletions src/commands/profiles/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';

import { profile as fetchProfile } from '../../api/profile';
import { CliCommand } from '../../command';
import { getCurrentProfileName, getProfiles } from '../../config';

function field(label: string, value: string) {
return ` ${chalk.dim(label.padEnd(10))}${value}`;
}

export default class ProfileList extends CliCommand {
static description = 'List all saved profiles';

static examples = ['sqd profile list', 'sqd profile list --show-token'];

static flags = {
'show-token': Flags.boolean({
description: 'Show full auth tokens',
default: false,
}),
};

async run(): Promise<void> {
const {
flags: { 'show-token': showToken },
} = await this.parse(ProfileList);

const profiles = getProfiles();
const current = getCurrentProfileName();
const names = Object.keys(profiles);

if (names.length === 0) {
this.log('No profiles found. Use "sqd auth -k <key>" to log in.');
return;
}

for (let i = 0; i < names.length; i++) {
const name = names[i];
const { apiUrl, credentials } = profiles[name];
const isCurrent = name === current;

const marker = isCurrent ? chalk.green('❯ ') : ' ';
const heading = isCurrent ? chalk.bold(name) : name;
this.log(`${marker}${heading}`);
this.log(field('API URL', apiUrl));

try {
const { email, username } = await fetchProfile({ auth: { apiUrl, credentials } });
if (email) this.log(field('Email', email));
if (username) this.log(field('Username', username));
} catch {
this.log(` ${chalk.dim('(invalid credentials)')}`);
}

if (showToken) {
this.log(field('Token', credentials));
}

if (i < names.length - 1) this.log('');
}
}
}
69 changes: 69 additions & 0 deletions src/commands/profiles/remove.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Args } from '@oclif/core';
import chalk from 'chalk';
import inquirer from 'inquirer';

import { CliCommand } from '../../command';
import { getCurrentProfileName, getProfiles, removeProfile } from '../../config';
import { getTTY } from '../../tty';

export default class ProfileRemove extends CliCommand {
static description = 'Remove a saved profile';

static examples = ['sqd profile remove', 'sqd profile remove work'];

static args = {
name: Args.string({
description: 'Profile name to remove',
required: false,
}),
};

async run(): Promise<void> {
const {
args: { name: argName },
flags: { interactive },
} = await this.parse(ProfileRemove);

const profiles = getProfiles();
const current = getCurrentProfileName();
const choices = Object.keys(profiles).filter((n) => n !== current);

if (choices.length === 0) {
return this.log(
chalk.yellow(`No removable profiles. The active profile "${current}" cannot be removed.`),
);
}

let name = argName;

if (!name) {
const { stdin, stdout } = getTTY();
if (!stdin || !stdout || !interactive) {
return this.error(
[
`Please specify a profile name.`,
`Available profiles: ${choices.join(', ')}`,
``,
`Example: sqd profile remove ${choices[0]}`,
].join('\n'),
);
}

const prompt = inquirer.createPromptModule({ input: stdin, output: stdout });
const answer = await prompt([
{
name: 'profile',
type: 'list',
message: 'Remove profile:',
choices,
},
]);
stdin.destroy();
stdout.destroy();
name = answer.profile as string;
}

removeProfile(name!);
this.logSuccess(` Profile ${chalk.bold(name)} has been removed`);
}
}
71 changes: 71 additions & 0 deletions src/commands/profiles/use.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Args } from '@oclif/core';
import chalk from 'chalk';
import inquirer from 'inquirer';

import { CliCommand } from '../../command';
import { getCurrentProfileName, getProfiles, useProfile } from '../../config';
import { getTTY } from '../../tty';

export default class ProfileUse extends CliCommand {
static description = 'Switch the active profile';

static examples = ['sqd profile use', 'sqd profile use work'];

static args = {
name: Args.string({
description: 'Profile name to switch to',
required: false,
}),
};

async run(): Promise<void> {
const {
args: { name: argName },
flags: { interactive },
} = await this.parse(ProfileUse);

const profiles = getProfiles();
const current = getCurrentProfileName();
const choices = Object.keys(profiles).filter((n) => n !== current);

this.log(`${chalk.dim('Current profile:')} ${chalk.bold(current)}`);

if (choices.length === 0) {
return this.log(
chalk.yellow(`No other profiles available. Use "sqd auth -k <key> --profile <name>" to create one.`),
);
}

let name = argName;

if (!name) {
const { stdin, stdout } = getTTY();
if (!stdin || !stdout || !interactive) {
return this.error(
[
`Please specify a profile name.`,
`Available profiles: ${choices.join(', ')}`,
``,
`Example: sqd profile use ${choices[0]}`,
].join('\n'),
);
}

const prompt = inquirer.createPromptModule({ input: stdin, output: stdout });
const answer = await prompt([
{
name: 'profile',
type: 'list',
message: 'Switch to profile:',
choices,
},
]);
stdin.destroy();
stdout.destroy();
name = answer.profile as string;
}

useProfile(name!);
this.logSuccess(` Switched to profile ${chalk.bold(name)}`);
}
}
9 changes: 7 additions & 2 deletions src/commands/whoami.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import chalk from 'chalk';

import { profile } from '../api/profile';
import { CliCommand } from '../command';
import { getConfig } from '../config';

function field(label: string, value: string) {
return `${chalk.dim(label.padEnd(10))}${value}`;
}

export default class Whoami extends CliCommand {
static description = `Show the user details for the current Cloud account`;

Expand All @@ -11,7 +17,7 @@ export default class Whoami extends CliCommand {
await this.parse(Whoami);

const { username, email } = await profile();
const { apiUrl, credentials } = getConfig();
const { apiUrl } = getConfig();

if (email) {
this.log(`Email: ${email}`);
Expand All @@ -20,6 +26,5 @@ export default class Whoami extends CliCommand {
this.log(`Username: ${username}`);
}
this.log(`API URL: ${apiUrl}`);
this.log(`Token: ${'*'.repeat(Math.max(0, credentials.length - 4))}${credentials.slice(-4)}`);
}
}
Loading