Skip to content
Draft
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
149 changes: 149 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"chalk": "^5.3.0",
"cli-table": "^0.3.11",
"commander": "^14.0.0",
"open": "^10.2.0",
"openai": "^5.0.1",
"pdf2pic": "^3.2.0",
"prompt-sync": "^4.2.0",
Expand Down
57 changes: 18 additions & 39 deletions src/commands/login.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import * as commander from 'commander';
import chalk from 'chalk';
import open from 'open';

import { actionRunner, prompt } from '../utils.js';
import { storeConfig, getEmail } from '../config.js';
import { exchangeIdAndTkForAccessToken, requestMagicLink } from '../forma.js';
import { exchangeIdAndTkForAccessToken } from '../forma.js';
import VERSION from '../version.js';

const command = new commander.Command();

interface Arguments {
email?: string;
}

const parseEmailedFormaMagicLink = (input: string): { id: string; tk: string } => {
const parsedUrl = new URL(input);

Expand Down Expand Up @@ -50,61 +47,43 @@ const parseEmailedFormaMagicLink = (input: string): { id: string; tk: string } =
return { id, tk };
};

const EMAIL_REGEX =
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

const promptForEmail = (isFirstRun = true): string => {
const promptMessage = isFirstRun
? 'Enter the email address you use to log on to Forma, then press Enter.'
: chalk.yellow("That doesn't look like a valid email address. Please try again.");
console.log(promptMessage);

const email = prompt('> ');

if (!EMAIL_REGEX.test(email)) {
return promptForEmail(false);
} else {
return email;
}
};

const promptForEmailedMagicLink = (
email: string,
errorMessage: string | null = null,
): { id: string; tk: string } => {
const promptMessage = errorMessage
? chalk.yellow("That doesn't look like a valid magic link. Please try again.")
: `Copy and paste the magic link sent to you at ${email}, then press Enter.`;
: 'Copy and paste the magic link sent to you, then press Enter.';
console.log(promptMessage);

const emailedMagicLink = prompt('> ');

try {
return parseEmailedFormaMagicLink(emailedMagicLink);
} catch (e) {
return promptForEmailedMagicLink(email, e);
return promptForEmailedMagicLink(e);
}
};

command
.name('login')
.version(VERSION)
.description(
'Connect Formanator to your Forma account with a magic link. Your email address will be remembered after logging in for the first time.',
)
.option(
'--email <email>',
'The email address to use to log in to Forma. Defaults to the FORMA_EMAIL environment variable, or the email you last used to log in. If no email address is provided, you will be prompted for your email.',
process.env.FORMA_EMAIL || getEmail(),
)
.description('Connect Formanator to your Forma account with a magic link.')
.action(
actionRunner(async (opts: Arguments) => {
const email = opts.email ?? promptForEmail();
await requestMagicLink(email);
actionRunner(async () => {
console.log(chalk.cyan('\nWe will now open your browser to the Forma login page.'));
console.log('Please enter your email address there and request a magic link.');
console.log(
'Once you receive the magic link in your email, come back to this terminal and paste it below.\n',
);

const { id, tk } = promptForEmailedMagicLink(email);
await open('https://client.joinforma.com/login?type=magic');

const { id, tk } = promptForEmailedMagicLink();
const accessToken = await exchangeIdAndTkForAccessToken(id, tk);
storeConfig({ accessToken, email });

// Preserve existing email if it was previously stored
const existingEmail = getEmail();
storeConfig({ accessToken, email: existingEmail });

console.log(chalk.green('You are now logged in! 🥳'));
}),
Expand Down