sindri
Version:
The Sindri Labs JavaScript SDK and CLI tool.
132 lines (122 loc) • 3.92 kB
text/typescript
import os from "os";
import process from "process";
import { Command } from "@commander-js/extra-typings";
import {
confirm,
input,
password as passwordInput,
select,
} from "@inquirer/prompts";
import sindri from "lib";
import { ApiError, type TeamMeResponse } from "lib/api";
import { Config } from "lib/config";
export const loginCommand = new Command()
.name("login")
.description("Authorize the client.")
.option(
"-u, --base-url <URL>",
"The base URL for the Sindri API. Mainly useful for development.",
"https://sindri.app",
)
.action(async ({ baseUrl }) => {
const config = new Config();
// Check if they're already authenticated, and prompt for confirmation if so.
const auth = config.auth;
if (!auth) {
let teamMeResponse: TeamMeResponse | undefined;
try {
teamMeResponse = await sindri._client.internal.teamMe();
} catch (error) {
if (error instanceof ApiError && error.status === 401) {
sindri.logger.warn(
"Existing credentials found, but invalid. Please continue logging in to update them.",
);
} else {
sindri.logger.fatal("An unknown error occurred.");
sindri.logger.error(error);
return process.exit(1);
}
}
if (teamMeResponse) {
const proceed = await confirm({
message:
`You are already logged in as ${teamMeResponse.team.slug} on ${sindri.baseUrl}, ` +
"are you sure you want to proceed?",
default: false,
});
if (!proceed) {
sindri.logger.info("Aborting.");
return;
}
}
}
// Collect details for generating an API key.
const username = await input({ message: "Username:" });
const password = await passwordInput({ mask: true, message: "Password:" });
const name = await input({
default: `${os.hostname().substring(0, 28)}-sdk`,
message: "New API Key Name:",
validate: (input) => {
if (input.length > 32) {
return "API key name must 32 characters or fewer.";
}
return true;
},
});
// Generate an API key for one of their teams.
try {
// Generate a JWT token to authenticate the user.
sindri._clientConfig.BASE = baseUrl;
const tokenResult = await sindri._client.token.jwtTokenGenerate({
username,
password,
});
sindri._clientConfig.TOKEN = tokenResult.access;
// Fetch their teams and have the user select one.
const userResult = await sindri._client.internal.userMe();
const teamId = await select({
message: "Select a Organization:",
choices: userResult.teams.map(({ id, slug }) => ({
name: slug,
value: id,
})),
});
const team = userResult.teams.find((team) => team.id === teamId);
if (!team) {
throw new Error("No organization selected.");
}
// Generate an API key.
const apiKeyResult = await sindri._client.authorization.apikeyGenerate(
{
username,
password,
name,
},
`${teamId}`,
);
const apiKey = apiKeyResult.api_key;
const apiKeyId = apiKeyResult.id;
const apiKeyName = apiKeyResult.name;
if (!apiKey || !apiKeyId || !apiKeyName) {
throw new Error("Error generating API key.");
}
// Store the new auth information.
config.update({
auth: {
apiKey,
apiKeyId,
apiKeyName,
baseUrl,
teamId,
teamSlug: team.slug,
},
});
sindri.logger.info(
"You have successfully authorized the client with your Sindri account.",
);
} catch (error) {
sindri.logger.fatal("An irrecoverable error occurred.");
sindri.logger.error(error);
process.exit(1);
}
});