-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathcredentials.ts
More file actions
169 lines (153 loc) · 4.85 KB
/
credentials.ts
File metadata and controls
169 lines (153 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import { logErrorAndExit } from '../console/logging.js';
import { logger } from '../console/logger.js';
import path from 'node:path';
import fs from 'node:fs';
import { Settings, SupportedFrameworks } from '../types/index.js';
import chalk from 'chalk';
import apiRequest from './fetch.js';
// Type for credentials returned from the dashboard
type Credentials = {
apiKeys: ApiKey[];
projectId: string;
};
type ApiKey = {
key: string;
type: 'development' | 'production';
};
// Fetches project ID and API key by opening the dashboard in the browser
export async function retrieveCredentials(
settings: Settings,
keyType: 'development' | 'production' | 'all'
): Promise<Credentials> {
// Generate a session ID
const { sessionId } = await generateCredentialsSession(
settings.baseUrl,
keyType
);
const urlToOpen = `${settings.dashboardUrl}/cli/wizard/${sessionId}`;
await import('open').then((open) =>
open.default(urlToOpen, {
wait: false,
})
);
logger.message(
`${chalk.dim(
`If the browser window didn't open automatically, please open the following link:`
)}\n\n${chalk.cyan(urlToOpen)}`
);
const spinner = logger.createSpinner('dots');
spinner.start('Waiting for response from dashboard...');
const credentials = await new Promise<Credentials>(
async (resolve, reject) => {
const interval = setInterval(async () => {
// Ping the dashboard to see if the credentials are set
try {
const res = await apiRequest(
settings.baseUrl,
`/cli/wizard/${sessionId}`,
{ method: 'GET' }
);
if (res.status === 200) {
const data = await res.json();
resolve(data as Credentials);
clearInterval(interval);
clearTimeout(timeout);
apiRequest(settings.baseUrl, `/cli/wizard/${sessionId}`, {
method: 'DELETE',
});
}
} catch (err) {
console.error(err);
}
}, 2000);
// timeout after 1 hour
const timeout = setTimeout(
() => {
spinner.stop('Timed out');
clearInterval(interval);
logErrorAndExit('Timed out waiting for response from dashboard');
},
1000 * 60 * 60
);
}
);
spinner.stop('Received credentials');
return credentials;
}
export async function generateCredentialsSession(
url: string,
keyType: 'development' | 'production' | 'all'
): Promise<{
sessionId: string;
}> {
const res = await apiRequest(url, '/cli/wizard/session', {
body: { keyType },
});
if (!res.ok) {
logErrorAndExit('Failed to generate credentials session');
}
return await res.json();
}
// Checks if the credentials are set in the environment variables
export function areCredentialsSet() {
return (
process.env.GT_PROJECT_ID &&
(process.env.GT_API_KEY || process.env.GT_DEV_API_KEY)
);
}
// Sets the credentials in .env.local file
export async function setCredentials(
credentials: Credentials,
framework?: SupportedFrameworks,
cwd: string = process.cwd()
) {
const envFile = path.join(cwd, '.env.local');
let envContent = '';
// Check if .env.local exists, create it if it doesn't
if (!fs.existsSync(envFile)) {
// File doesn't exist, create it
await fs.promises.writeFile(envFile, '', 'utf8');
// Add .env.local to .gitignore if it exists
const gitignoreFile = path.join(cwd, '.gitignore');
if (fs.existsSync(gitignoreFile)) {
const gitignoreContent = await fs.promises.readFile(
gitignoreFile,
'utf8'
);
if (!gitignoreContent.includes('.env.local')) {
await fs.promises.appendFile(gitignoreFile, '\n.env.local\n', 'utf8');
}
} else {
// Create .gitignore file with .env.local
await fs.promises.writeFile(gitignoreFile, '.env.local\n', 'utf8');
}
} else {
// Read existing content
envContent = await fs.promises.readFile(envFile, 'utf8');
}
// Always append the credentials to the file
let prefix = '';
if (framework === 'next-pages') {
prefix = 'NEXT_PUBLIC_';
} else if (framework === 'vite') {
prefix = 'VITE_';
} else if (framework === 'gatsby') {
prefix = 'GATSBY_';
} else if (framework === 'react') {
prefix = 'REACT_APP_';
} else if (framework === 'redwood') {
prefix = 'REDWOOD_ENV_';
}
envContent += `\n${prefix}GT_PROJECT_ID=${credentials.projectId}\n`;
for (const apiKey of credentials.apiKeys) {
if (apiKey.type === 'development') {
envContent += `${prefix || ''}GT_DEV_API_KEY=${apiKey.key}\n`;
} else {
envContent += `${prefix || ''}GT_API_KEY=${apiKey.key}\n`;
}
}
// Ensure we don't have excessive newlines
envContent = envContent.replace(/\n{3,}/g, '\n\n').trim() + '\n';
// Write the updated content back to the file
await fs.promises.writeFile(envFile, envContent, 'utf8');
}