Skip to content

Commit a2280ee

Browse files
committed
feat: Added CLI logins without the browser
1 parent 950c4dd commit a2280ee

File tree

3 files changed

+72
-10
lines changed

3 files changed

+72
-10
lines changed

src/api/dashboard/index.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ export const DashboardApiClient = {
1212

1313
const res = await fetch(
1414
`${config.dashboardUrl}/api/v1/documents/${id}`,
15-
{ method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` } },
15+
{
16+
method: 'GET',
17+
headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` }
18+
},
1619
);
1720

1821
if (!res.ok) {
@@ -32,7 +35,10 @@ export const DashboardApiClient = {
3235

3336
const res = await fetch(
3437
`${config.dashboardUrl}/api/v1/documents/default/id`,
35-
{ method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` } },
38+
{
39+
method: 'GET',
40+
headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` }
41+
},
3642
);
3743

3844
if (!res.ok) {
@@ -50,12 +56,30 @@ export const DashboardApiClient = {
5056
return json.defaultDocumentId;
5157
},
5258

59+
async login(email: string, password: string): Promise<string> {
60+
const res = await fetch(
61+
`${config.dashboardUrl}/api/v1/auth/cli`,
62+
{
63+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
64+
email,
65+
password,
66+
})
67+
},
68+
);
69+
70+
if (!res.ok) {
71+
const message = await res.text();
72+
throw new Error(message);
73+
}
74+
75+
const json = await res.json();
76+
return json.accessToken;
77+
},
78+
5379
async saveDocumentUpdate(id: string, contents: string): Promise<void> {
5480
const login = LoginHelper.get()?.credentials;
5581
if (!login) {
5682
throw new Error('Not logged in');
5783
}
58-
59-
6084
}
6185
}

src/commands/login.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
1+
import { Flags } from '@oclif/core';
12
import chalk from 'chalk';
23

34
import { BaseCommand } from '../common/base-command.js';
45
import { LoginOrchestrator } from '../orchestrators/login.js';
56

67
export default class Login extends BaseCommand {
78
static description =
8-
`Logins to Codify cloud account
9+
`Logins to Codify cloud account.
10+
11+
By default opens a browser window to login. If username and password are provided, it will attempt to login via CLI.
912
1013
For more information, visit: https://docs.codifycli.com/commands/login
1114
`
1215

13-
static flags = {}
16+
static baseFlags = {
17+
username: Flags.string({ char: 'u', description: 'Username to login with.' }),
18+
password: Flags.string({ char: 'p', description: 'Password to login with.' }),
19+
}
1420

1521
static examples = [
1622
'<%= config.bin %> <%= command.id %>',
17-
'<%= config.bin %> <%= command.id %> --path=../../import.codify.jsonc',
23+
'<%= config.bin %> <%= command.id %> --username=user@example.com --password=secret',
24+
'<%= config.bin %> <%= command.id %> -p user@example.com -p secret',
1825
]
1926

2027
public async run(): Promise<void> {
2128
const { flags } = await this.parse(Login)
2229

23-
await LoginOrchestrator.run();
30+
await LoginOrchestrator.run({ username: flags.username, password: flags.password });
2431
console.log(chalk.green('\nSuccessfully logged in!'))
2532

2633
process.exit(0);

src/orchestrators/login.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import cors from 'cors';
33
import express, { json } from 'express';
44
import open from 'open';
55

6+
import { DashboardApiClient } from '../api/dashboard/index.js';
67
import { config } from '../config.js';
78
import { LoginHelper } from '../connect/login-helper.js';
89
import { ajv } from '../utils/ajv.js';
@@ -25,8 +26,38 @@ interface Credentials {
2526
expiry: string;
2627
}
2728

29+
export interface LoginArgs {
30+
username?: string;
31+
password?: string;
32+
}
33+
34+
2835
export class LoginOrchestrator {
29-
static async run() {
36+
static async run(args?: LoginArgs) {
37+
if (args?.username && !args?.password) {
38+
console.error(chalk.red('Password is required when providing a username'));
39+
process.exit(1);
40+
}
41+
42+
if (args?.password && !args?.username) {
43+
console.error(chalk.red('Username is required when providing a password'));
44+
process.exit(1);
45+
}
46+
47+
if (args?.username && args?.password) {
48+
return this.loginWithCredentials(args.username, args.password);
49+
}
50+
51+
return this.loginViaBrowser();
52+
}
53+
54+
private static async loginWithCredentials(username: string, password: string) {
55+
const accessToken = await DashboardApiClient.login(username, password);
56+
await LoginHelper.save(accessToken);
57+
}
58+
59+
private static async loginViaBrowser() {
60+
3061
const app = express();
3162

3263
app.use(cors({ origin: config.corsAllowedOrigins }))
@@ -39,7 +70,7 @@ export class LoginOrchestrator {
3970
}
4071

4172
console.log(
42-
`Opening CLI auth page...
73+
`Opening CLI auth page...
4374
Manually open it here: ${config.dashboardUrl}/auth/cli`
4475
)
4576
open(`${config.dashboardUrl}/auth/cli`);

0 commit comments

Comments
 (0)