@yahoo/uds 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/README.md +21 -0
- package/cli/cli.ts +27 -4
- package/cli/commands/login.ts +28 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/uds.ts +4 -1
- package/cli/env.d.ts +5 -0
- package/cli/exec.ts +1 -1
- package/cli/preload.ts +16 -0
- package/cli/uds-cli +3 -3
- package/cli/utils/auth.test.ts +107 -0
- package/cli/utils/auth.ts +158 -0
- package/cli/utils/client_secrets.json.enc +0 -0
- package/cli/utils/secrets.ts +40 -0
- package/cli/utils/types.ts +8 -0
- package/package.json +163 -155
- package/cli/bin/uds-darwin-arm64-baseline +0 -0
- package/cli/bin/uds-linux-x64-baseline +0 -0
- package/dist/chunk-2JIYRTEY.js +0 -3
- package/dist/chunk-3VTRSTDI.cjs +0 -2
- package/dist/chunk-4J7ZJHCN.js +0 -2
- package/dist/chunk-5X3JN6IH.cjs +0 -3
- package/dist/chunk-6K5IORZA.cjs +0 -2
- package/dist/chunk-AIRVDEVS.cjs +0 -3
- package/dist/chunk-BQTYVXIC.js +0 -3
- package/dist/chunk-CE5NDFHI.js +0 -2
- package/dist/chunk-E3Z3A4MR.cjs +0 -2
- package/dist/chunk-OJR4RBBW.js +0 -3
- package/dist/chunk-PCSDVJE6.js +0 -2
- package/dist/chunk-SLCISFAV.cjs +0 -3
- package/dist/chunk-TWTUXY76.cjs +0 -2
- package/dist/chunk-TYKFUQUM.cjs +0 -1
- package/dist/chunk-W5AGPUNC.cjs +0 -4
- package/dist/chunk-XFTF7IHG.js +0 -2
- package/dist/chunk-Y2TGGXMH.js +0 -3
- package/dist/chunk-ZJE5XRN2.js +0 -4
- package/dist/client/index.cjs +0 -4
- package/dist/client/index.d.cts +0 -97
- package/dist/client/index.d.ts +0 -97
- package/dist/client/index.js +0 -6
- package/dist/experimental/index.cjs +0 -3
- package/dist/experimental/index.d.cts +0 -451
- package/dist/experimental/index.d.ts +0 -451
- package/dist/experimental/index.js +0 -3
- package/dist/fixtures.cjs +0 -1506
- package/dist/fixtures.d.cts +0 -94
- package/dist/fixtures.d.ts +0 -94
- package/dist/fixtures.js +0 -1449
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +0 -300
- package/dist/index.d.ts +0 -300
- package/dist/index.js +0 -2
- package/dist/metafile-cjs.json +0 -1
- package/dist/metafile-esm.json +0 -1
- package/dist/motionFeatures-M3DOZMRC.cjs +0 -1
- package/dist/motionFeatures-QDISAARM.js +0 -1
- package/dist/styles/toast.css +0 -3
- package/dist/styles/toast.d.cts +0 -2
- package/dist/styles/toast.d.ts +0 -2
- package/dist/tailwind/plugin.cjs +0 -3
- package/dist/tailwind/plugin.d.cts +0 -60
- package/dist/tailwind/plugin.d.ts +0 -60
- package/dist/tailwind/plugin.js +0 -3
- package/dist/tailwind/purger.cjs +0 -6
- package/dist/tailwind/purger.d.cts +0 -17
- package/dist/tailwind/purger.d.ts +0 -17
- package/dist/tailwind/purger.js +0 -6
- package/dist/tailwind/tsMorph.cjs +0 -3
- package/dist/tailwind/tsMorph.d.cts +0 -31
- package/dist/tailwind/tsMorph.d.ts +0 -31
- package/dist/tailwind/tsMorph.js +0 -2
- package/dist/tailwind/utils.cjs +0 -1
- package/dist/tailwind/utils.d.cts +0 -99
- package/dist/tailwind/utils.d.ts +0 -99
- package/dist/tailwind/utils.js +0 -1
- package/dist/tokens/index.cjs +0 -1
- package/dist/tokens/index.d.cts +0 -1127
- package/dist/tokens/index.d.ts +0 -1127
- package/dist/tokens/index.js +0 -1
- package/dist/tokens/parseTokens.cjs +0 -1
- package/dist/tokens/parseTokens.d.cts +0 -59
- package/dist/tokens/parseTokens.d.ts +0 -59
- package/dist/tokens/parseTokens.js +0 -1
- package/dist/types-CpZ2ERLZ.d.cts +0 -949
- package/dist/types-CpZ2ERLZ.d.ts +0 -949
package/cli/README.md
CHANGED
@@ -128,6 +128,27 @@ Any file added to commands/codemod will be available in prompt land.
|
|
128
128
|
uds codemod
|
129
129
|
```
|
130
130
|
|
131
|
+
### Login
|
132
|
+
|
133
|
+
The `uds login` command is used to authenticate the user to the CLI. We use
|
134
|
+
Google's OAuth2 flow, which is the same login flow used by the [Configurator](/docs/getting-started/using-configurator) and the documentation site. Logging in to the CLI helps us identify and track CLI usage and metrics.
|
135
|
+
|
136
|
+
#### Flags
|
137
|
+
|
138
|
+
| Flag | Description | Default | Required |
|
139
|
+
| ---------- | ----------------------------------------- | ------- | -------- |
|
140
|
+
| `skipAuth` | Skip auth checks (for CI/CD environments) | `false` | |
|
141
|
+
|
142
|
+
**Example:**
|
143
|
+
|
144
|
+
```shell
|
145
|
+
uds login
|
146
|
+
uds login --skipAuth
|
147
|
+
|
148
|
+
# Sign out current user.
|
149
|
+
uds logout
|
150
|
+
```
|
151
|
+
|
131
152
|
### Expo (WIP)
|
132
153
|
|
133
154
|
The `uds expo` command is for building and launching React Native apps using Expo.
|
package/cli/cli.ts
CHANGED
@@ -2,25 +2,48 @@
|
|
2
2
|
// Start bluebun to run the correct CLI command
|
3
3
|
import { parseArgs } from 'node:util';
|
4
4
|
|
5
|
-
import {
|
5
|
+
import { getFeatureFlags } from '@yahoo/uds/flags';
|
6
|
+
import { print, red, run } from 'bluebun';
|
6
7
|
|
7
|
-
|
8
|
+
import { getAuthenticatedUser } from './utils/auth';
|
9
|
+
|
10
|
+
async function main() {
|
8
11
|
// We need to pass the path from Node to the Bun binaries
|
9
12
|
// because there's an issue where the Bun binaries struggle to resolve
|
10
|
-
const { values } = parseArgs({
|
13
|
+
const { values, positionals } = parseArgs({
|
11
14
|
args: Bun.argv,
|
12
15
|
options: {
|
13
16
|
commandsPath: {
|
14
17
|
type: 'string',
|
15
18
|
},
|
19
|
+
skipAuth: {
|
20
|
+
type: 'boolean',
|
21
|
+
default: false,
|
22
|
+
},
|
16
23
|
},
|
17
24
|
strict: false,
|
18
25
|
allowPositionals: true,
|
19
26
|
});
|
20
27
|
|
28
|
+
const cliPath = values.commandsPath!.toString();
|
29
|
+
const isLoginCmd = positionals.includes('login');
|
30
|
+
const { useCLIAuth } = getFeatureFlags();
|
31
|
+
|
32
|
+
if (useCLIAuth && !values.skipAuth && !isLoginCmd) {
|
33
|
+
const user = await getAuthenticatedUser(cliPath);
|
34
|
+
if (!user) {
|
35
|
+
print(
|
36
|
+
red(
|
37
|
+
'🚨 Sign-in required. Please run `uds login` and use a @yahooinc.com email during sign-in.',
|
38
|
+
),
|
39
|
+
);
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
21
44
|
run({
|
22
45
|
name: 'uds',
|
23
|
-
cliPath
|
46
|
+
cliPath,
|
24
47
|
});
|
25
48
|
}
|
26
49
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { magenta, print, red } from 'bluebun';
|
2
|
+
|
3
|
+
import { login } from '../utils/auth';
|
4
|
+
|
5
|
+
export default {
|
6
|
+
name: 'login',
|
7
|
+
description: '👤 Log in to UDS CLI',
|
8
|
+
alias: ['auth'],
|
9
|
+
run: async () => {
|
10
|
+
try {
|
11
|
+
const user = await login();
|
12
|
+
if (user) {
|
13
|
+
print(magenta(`🔒 Logged in as ${user.email}`));
|
14
|
+
}
|
15
|
+
} catch (error) {
|
16
|
+
if (error instanceof Error) {
|
17
|
+
print(red(error.message));
|
18
|
+
} else {
|
19
|
+
print(
|
20
|
+
red(
|
21
|
+
`❌ An error occurred while logging in. Please ask in the #uds-ask channel for support.`,
|
22
|
+
),
|
23
|
+
);
|
24
|
+
process.exitCode = 1;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
},
|
28
|
+
};
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { magenta, print, red } from 'bluebun';
|
2
|
+
|
3
|
+
import { logout } from '../utils/auth';
|
4
|
+
|
5
|
+
export default {
|
6
|
+
name: 'logout',
|
7
|
+
description: '👤 Sign out of UDS CLI',
|
8
|
+
run: async () => {
|
9
|
+
try {
|
10
|
+
await logout();
|
11
|
+
print(magenta('👋 You have been logged out.'));
|
12
|
+
} catch (error) {
|
13
|
+
if (error instanceof Error) {
|
14
|
+
print(red(error.message));
|
15
|
+
} else {
|
16
|
+
print(
|
17
|
+
red(
|
18
|
+
`❌ An error occurred while logging out. Please ask in the #uds-ask channel for support.`,
|
19
|
+
),
|
20
|
+
);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
},
|
24
|
+
};
|
package/cli/commands/uds.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { print, type Props, red } from 'bluebun';
|
2
2
|
|
3
|
+
import { getAuthenticatedUser } from '../utils/auth';
|
3
4
|
import { getCommandHelp } from '../utils/getCommandHelp';
|
4
5
|
|
5
6
|
export default {
|
@@ -9,6 +10,8 @@ export default {
|
|
9
10
|
if (props.first) {
|
10
11
|
print(red(`Unknown command: ${props.first}`));
|
11
12
|
}
|
12
|
-
await
|
13
|
+
const user = await getAuthenticatedUser();
|
14
|
+
const notes = user ? `🔒 Logged in as ${user.email}` : undefined;
|
15
|
+
await getCommandHelp({ ...props, notes });
|
13
16
|
},
|
14
17
|
};
|
package/cli/env.d.ts
CHANGED
@@ -14,6 +14,11 @@ declare module 'bun' {
|
|
14
14
|
EXPO_USE_METRO_WORKSPACE_ROOT?: string;
|
15
15
|
EXPO_BUNDLE_APP?: string;
|
16
16
|
ESLINT_USE_FLAT_CONFIG?: string;
|
17
|
+
/** UDS purge command - which color and scale modes to keep */
|
17
18
|
ENABLED_SCALE_AND_COLOR_MODES?: string;
|
19
|
+
/** UDS config id */
|
20
|
+
UDS_ID?: string;
|
21
|
+
/** UDS sync command - filename to write the config output */
|
22
|
+
UDS_OUT_FILE?: string;
|
18
23
|
}
|
19
24
|
}
|
package/cli/exec.ts
CHANGED
package/cli/preload.ts
CHANGED
@@ -44,3 +44,19 @@ mock.module('@yahoo/uds/tailwind/purger', () => ({
|
|
44
44
|
Button: '',
|
45
45
|
},
|
46
46
|
}));
|
47
|
+
|
48
|
+
mock.module('googleapis', () => ({
|
49
|
+
google: {
|
50
|
+
auth: {
|
51
|
+
OAuth2: mock(() => {}).mockImplementation(() => ({
|
52
|
+
getToken: () => Promise.resolve({ tokens: 'test' }),
|
53
|
+
setCredentials: mock(() => {}),
|
54
|
+
})),
|
55
|
+
},
|
56
|
+
oauth2: () => ({
|
57
|
+
userinfo: {
|
58
|
+
get: () => Promise.resolve({ data: { email: 'foo@yahooinc.com' } }),
|
59
|
+
},
|
60
|
+
}),
|
61
|
+
},
|
62
|
+
}));
|
package/cli/uds-cli
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
// Note: This file is generated, do not edit directly! Edit exec.ts instead.
|
4
4
|
// cli/exec.ts
|
5
|
-
import {execFileSync} from "child_process";
|
5
|
+
import { execFileSync } from "child_process";
|
6
6
|
import path from "path";
|
7
|
-
import {fileURLToPath} from "url";
|
7
|
+
import { fileURLToPath } from "url";
|
8
8
|
|
9
9
|
// cli/consts.ts
|
10
10
|
var SUPPORTED_BUN_PLATFORMS = ["linux-x64-baseline", "darwin-arm64-baseline"];
|
@@ -23,7 +23,7 @@ var detectPlatform = () => {
|
|
23
23
|
stdio: "inherit"
|
24
24
|
});
|
25
25
|
} else {
|
26
|
-
throw new Error("Unsupported platform \uD83D\uDE22,
|
26
|
+
throw new Error("Unsupported platform \uD83D\uDE22, please reach out to the UDS team for support #uds-ask.");
|
27
27
|
}
|
28
28
|
};
|
29
29
|
detectPlatform();
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import path from 'node:path';
|
2
|
+
|
3
|
+
import { afterAll, describe, expect, it, mock } from 'bun:test';
|
4
|
+
import http from 'http';
|
5
|
+
// eslint-disable-next-line n/no-unpublished-import
|
6
|
+
import httpMocks from 'node-mocks-http';
|
7
|
+
|
8
|
+
import {
|
9
|
+
getAuthenticatedUser,
|
10
|
+
isYahooEmployee,
|
11
|
+
login,
|
12
|
+
logout,
|
13
|
+
onRequestHandler,
|
14
|
+
type User,
|
15
|
+
} from './auth';
|
16
|
+
|
17
|
+
const __dirname = path.resolve(import.meta.dir);
|
18
|
+
const filepath = path.resolve(__dirname, '.uds/user.json');
|
19
|
+
|
20
|
+
const writeCache = async (filePath: string, user: User) => {
|
21
|
+
return Bun.write(filePath, JSON.stringify(user));
|
22
|
+
};
|
23
|
+
|
24
|
+
describe('auth', () => {
|
25
|
+
describe('isYahooEmployee', () => {
|
26
|
+
it('does not return a user on first use', async () => {
|
27
|
+
expect(isYahooEmployee({ email: 'user@yahooinc.com' })).toBe(true);
|
28
|
+
expect(isYahooEmployee({ email: 'user@test.com' })).toBe(false);
|
29
|
+
});
|
30
|
+
});
|
31
|
+
|
32
|
+
describe('getAuthenticatedUser', () => {
|
33
|
+
it('does not return a user on first use', async () => {
|
34
|
+
expect(await getAuthenticatedUser(__dirname)).toBeUndefined();
|
35
|
+
});
|
36
|
+
|
37
|
+
it('reads user info from cache when one exists', async () => {
|
38
|
+
const user: User = { email: 'user@yahooinc.com', name: 'User' };
|
39
|
+
await writeCache(filepath, user);
|
40
|
+
const userInfo = await getAuthenticatedUser(__dirname);
|
41
|
+
expect(userInfo).toEqual(user);
|
42
|
+
});
|
43
|
+
|
44
|
+
it('resets user when non @yahooinc.com email is used', async () => {
|
45
|
+
await writeCache(filepath, { email: 'user@test.com', name: 'User' });
|
46
|
+
expect(await Bun.file(filepath).exists()).toBeTrue();
|
47
|
+
const userInfo = await getAuthenticatedUser(__dirname);
|
48
|
+
expect(userInfo).toBeUndefined();
|
49
|
+
expect(await Bun.file(filepath).exists()).toBeFalse();
|
50
|
+
});
|
51
|
+
});
|
52
|
+
|
53
|
+
describe('getAuthenticatedClient', () => {
|
54
|
+
// mock.module('open', () => ({}));
|
55
|
+
const mockServer = Object.assign(Object.create(http.Server.prototype), {
|
56
|
+
listen: mock(),
|
57
|
+
on: mock(),
|
58
|
+
close: mock(),
|
59
|
+
});
|
60
|
+
const resolve = mock();
|
61
|
+
const reject = mock();
|
62
|
+
|
63
|
+
it('returns error when no code is on callback url', async () => {
|
64
|
+
const request = httpMocks.createRequest({ method: 'GET', url: '/oauth2callback' });
|
65
|
+
const response = httpMocks.createResponse();
|
66
|
+
await onRequestHandler.bind(mockServer)(request, response, resolve, reject);
|
67
|
+
expect(response._getData()).toInclude('no code parameter');
|
68
|
+
});
|
69
|
+
|
70
|
+
it('can successfully login in user', async () => {
|
71
|
+
const request = httpMocks.createRequest({ method: 'GET', url: '/oauth2callback?code=123' });
|
72
|
+
const response = httpMocks.createResponse();
|
73
|
+
await onRequestHandler.bind(mockServer)(request, response, resolve, reject);
|
74
|
+
expect(response._getData()).toInclude('Authentication successful!');
|
75
|
+
expect(resolve).toHaveBeenCalled();
|
76
|
+
});
|
77
|
+
});
|
78
|
+
|
79
|
+
describe('logout', () => {
|
80
|
+
it('removes the user cache', async () => {
|
81
|
+
await writeCache(filepath, { email: 'user@yahooinc.com', name: 'User' });
|
82
|
+
expect(await Bun.file(filepath).exists()).toBeTrue();
|
83
|
+
await logout(filepath);
|
84
|
+
expect(await Bun.file(filepath).exists()).toBeFalse();
|
85
|
+
});
|
86
|
+
});
|
87
|
+
|
88
|
+
describe('login', () => {
|
89
|
+
afterAll(async () => {
|
90
|
+
await logout(); // cleanup
|
91
|
+
});
|
92
|
+
|
93
|
+
const mockFunc = mock(() => Promise.resolve({ email: 'foo@yahooinc.com' }));
|
94
|
+
|
95
|
+
mock.module('./auth', () => ({
|
96
|
+
getAuthenticatedClient: mockFunc,
|
97
|
+
}));
|
98
|
+
|
99
|
+
it('uses cache when user previously logged in', async () => {
|
100
|
+
// Test multiple calls to login
|
101
|
+
await login();
|
102
|
+
await login();
|
103
|
+
await login();
|
104
|
+
expect(mockFunc).toHaveBeenCalledTimes(1);
|
105
|
+
});
|
106
|
+
});
|
107
|
+
});
|
@@ -0,0 +1,158 @@
|
|
1
|
+
import { unlink } from 'node:fs/promises';
|
2
|
+
import path from 'node:path';
|
3
|
+
|
4
|
+
import { print, red } from 'bluebun';
|
5
|
+
import Bun from 'bun';
|
6
|
+
import { Auth, google, oauth2_v2 } from 'googleapis';
|
7
|
+
import http from 'http';
|
8
|
+
import open from 'open';
|
9
|
+
|
10
|
+
import clientSecrets from './client_secrets.json';
|
11
|
+
|
12
|
+
const { redirect_uris, client_id, client_secret } = clientSecrets.web;
|
13
|
+
const REDIRECT_URL = redirect_uris[0];
|
14
|
+
const { port: PORT, origin: BASE_URL } = new URL(REDIRECT_URL);
|
15
|
+
|
16
|
+
const cacheFilePath = '.uds/user.json';
|
17
|
+
const DEFAULT_CLI_PATH = path.resolve(import.meta.dir, '..');
|
18
|
+
const CACHED_USER_FILE = path.resolve(DEFAULT_CLI_PATH, cacheFilePath);
|
19
|
+
|
20
|
+
// Credentials can be found in https://console.cloud.google.com/apis/credentials/oauthclient/700524957090-6uju02gp6i0rlm3p17nt1v6vbrretfka.apps.googleusercontent.com?project=uds-poc.
|
21
|
+
const oauth2Client = new google.auth.OAuth2(client_id, client_secret, REDIRECT_URL);
|
22
|
+
|
23
|
+
type User = oauth2_v2.Schema$Userinfo;
|
24
|
+
|
25
|
+
function isYahooEmployee(user?: User) {
|
26
|
+
return user?.email?.endsWith('@yahooinc.com');
|
27
|
+
}
|
28
|
+
|
29
|
+
async function onRequestHandler(
|
30
|
+
this: http.Server,
|
31
|
+
req: http.IncomingMessage,
|
32
|
+
res: http.ServerResponse,
|
33
|
+
resolve: (value: typeof oauth2Client) => void,
|
34
|
+
reject: (reason?: unknown) => void,
|
35
|
+
) {
|
36
|
+
try {
|
37
|
+
const code = new URL(req.url || '', BASE_URL).searchParams.get('code');
|
38
|
+
if (!code) {
|
39
|
+
res.end('There was no code parameter on the url.');
|
40
|
+
this.close();
|
41
|
+
return;
|
42
|
+
}
|
43
|
+
|
44
|
+
res.end('Authentication successful! You can close this window.');
|
45
|
+
|
46
|
+
this.close();
|
47
|
+
|
48
|
+
const { tokens } = await oauth2Client.getToken(code);
|
49
|
+
oauth2Client.setCredentials(tokens);
|
50
|
+
|
51
|
+
resolve(oauth2Client);
|
52
|
+
} catch (e) {
|
53
|
+
reject(e);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
async function getAuthenticatedClient(): Promise<Auth.OAuth2Client> {
|
58
|
+
return new Promise((resolve, reject) => {
|
59
|
+
const authorizeUrl = oauth2Client.generateAuthUrl({
|
60
|
+
access_type: 'offline',
|
61
|
+
scope: [
|
62
|
+
'https://www.googleapis.com/auth/userinfo.profile',
|
63
|
+
'https://www.googleapis.com/auth/userinfo.email',
|
64
|
+
].join(' '),
|
65
|
+
hd: 'yahooinc.com',
|
66
|
+
include_granted_scopes: true,
|
67
|
+
});
|
68
|
+
|
69
|
+
// TODO: If port (3000) is already in use, this will fail.
|
70
|
+
// Setup https://www.npmjs.com/package/find-free-ports, but that won't
|
71
|
+
// play well with the pre-configured redirect_uris in the Google Cloud Console.
|
72
|
+
const server = http
|
73
|
+
.createServer()
|
74
|
+
.listen(PORT, async () => {
|
75
|
+
const childProcess = await open(authorizeUrl, { wait: false });
|
76
|
+
childProcess.unref();
|
77
|
+
})
|
78
|
+
.on('request', (req, res) => onRequestHandler.call(server, req, res, resolve, reject))
|
79
|
+
.on('error', (err: NodeJS.ErrnoException) => {
|
80
|
+
if (err.code && err.code.includes('EADDRINUSE')) {
|
81
|
+
print(
|
82
|
+
red(`🚨 Port ${PORT} already in use. Cannot start local server to handle OAuth flow.`),
|
83
|
+
);
|
84
|
+
server.close();
|
85
|
+
}
|
86
|
+
});
|
87
|
+
});
|
88
|
+
}
|
89
|
+
|
90
|
+
async function logout(cachePath?: string) {
|
91
|
+
// 1. TODO: Revoke token access
|
92
|
+
try {
|
93
|
+
// 2. Remove cached user data.
|
94
|
+
return await unlink(cachePath ?? CACHED_USER_FILE);
|
95
|
+
} catch {
|
96
|
+
// noop
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Takes the user through the OAuth2 flow, creating a server for the oauth callback.
|
102
|
+
* If the user has previously authenticated, it returns the cached user.
|
103
|
+
* @returns
|
104
|
+
*/
|
105
|
+
async function login() {
|
106
|
+
let user = await getAuthenticatedUser();
|
107
|
+
// Authenticate if there's no user cached.
|
108
|
+
if (!user) {
|
109
|
+
try {
|
110
|
+
const auth = await getAuthenticatedClient();
|
111
|
+
const oauth2 = google.oauth2({ version: 'v2', auth });
|
112
|
+
user = (await oauth2.userinfo.get()).data;
|
113
|
+
await Bun.write(CACHED_USER_FILE, JSON.stringify(user, null, 2));
|
114
|
+
} catch (err) {
|
115
|
+
console.error('Error:', err);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
return user;
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* The authenticated user or undefined if the user is not authenticated.
|
124
|
+
* @param cliPath Root folder of cli commands. Defaults to 'packages/uds/cli'.
|
125
|
+
* Note: this is only needed when calling this method in cli.ts. The compiled
|
126
|
+
* platform-specific binaries create a virtual file system and we need
|
127
|
+
* to forward the package libPath from exec.ts.
|
128
|
+
* @returns user info
|
129
|
+
*/
|
130
|
+
async function getAuthenticatedUser(cliPath = DEFAULT_CLI_PATH): Promise<User | undefined> {
|
131
|
+
try {
|
132
|
+
const cachePath = path.resolve(cliPath, cacheFilePath);
|
133
|
+
const file = Bun.file(cachePath);
|
134
|
+
const user = await file.json();
|
135
|
+
|
136
|
+
if (isYahooEmployee(user)) {
|
137
|
+
return user;
|
138
|
+
}
|
139
|
+
|
140
|
+
await logout(cachePath); // remove cached user.
|
141
|
+
} catch (err) {
|
142
|
+
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
|
143
|
+
console.error(err);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
return undefined;
|
148
|
+
}
|
149
|
+
|
150
|
+
export {
|
151
|
+
getAuthenticatedClient,
|
152
|
+
getAuthenticatedUser,
|
153
|
+
isYahooEmployee,
|
154
|
+
login,
|
155
|
+
logout,
|
156
|
+
onRequestHandler,
|
157
|
+
type User,
|
158
|
+
};
|
Binary file
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import path from 'node:path';
|
2
|
+
|
3
|
+
// eslint-disable-next-line n/no-unpublished-import
|
4
|
+
import { loadEnvConfig } from '@next/env';
|
5
|
+
import { $ } from 'bun';
|
6
|
+
|
7
|
+
loadEnvConfig(process.cwd());
|
8
|
+
|
9
|
+
const secretsFile = path.resolve(import.meta.dir, 'client_secrets.json');
|
10
|
+
const encryptedFilename = `${secretsFile}.enc`;
|
11
|
+
|
12
|
+
async function encrypt() {
|
13
|
+
await $`openssl enc -aes-256-cbc -in ${secretsFile} -out ${encryptedFilename}`;
|
14
|
+
}
|
15
|
+
|
16
|
+
async function decrypt() {
|
17
|
+
const f = Bun.file(secretsFile);
|
18
|
+
if (!(await f.exists())) {
|
19
|
+
await $`openssl enc -aes-256-cbc -in ${encryptedFilename} -out ${secretsFile} -d -pass env:UDS_CLI_SECRETS_PW`;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
async function main() {
|
24
|
+
if (!process.env.UDS_CLI_SECRETS_PW) {
|
25
|
+
console.error('Please set UDS_CLI_SECRETS_PW env variable. The password is in LastPass.');
|
26
|
+
process.exitCode = 1;
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
|
30
|
+
const cmd = Bun.argv[2];
|
31
|
+
if (cmd === 'encrypt') {
|
32
|
+
await encrypt();
|
33
|
+
console.log(`Secrets file encrypted to ${encryptedFilename}`);
|
34
|
+
} else if (cmd === 'decrypt') {
|
35
|
+
await decrypt();
|
36
|
+
console.log(`Secrets file saved to ${secretsFile}`);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
main();
|
package/cli/utils/types.ts
CHANGED