@yahoo/uds 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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