-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.mjs
215 lines (193 loc) · 5.54 KB
/
setup.mjs
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// @ts-check
import { exec as execOriginal, execSync } from 'node:child_process';
import { join, resolve } from 'node:path';
import { setTimeout as wait } from 'node:timers/promises';
import { promisify } from 'node:util';
import { createPromptModule } from 'inquirer';
import ora from 'ora';
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
const exec = promisify(execOriginal);
const prompt = createPromptModule();
// https://stackoverflow.com/a/29770068
const getRandomString = () => {
return crypto.getRandomValues(new BigUint64Array(1))[0].toString(36);
};
// I'm sorry
const getAvailableAccounts = async () => {
const { stdout } = await exec('npx wrangler whoami');
const lines = stdout.trim().split('\n');
let inTable = false;
const output = [];
for (const line of lines) {
if (line.includes('Account Name') && line.includes('Account ID')) {
inTable = true;
continue;
}
if (inTable) {
if (line.includes('Token Permissions')) {
break;
}
const [name, id] = line
.split('│')
.filter((s) => !!s)
.map((s) => s.trim());
if (name && id) {
output.push({ name, id });
}
}
}
return output;
};
const projects = ['encryption', 'schema', 'api', 'shadower', 'web'];
console.log('Hello! 😀');
console.log('');
console.log('installing npm dependencies 📦');
for (const project of projects) {
const spinner = ora({
text: `${project}: npm clean-install`,
indent: 2,
spinner: 'binary',
}).start();
const cmd = exec('npm clean-install', {
cwd: resolve(project),
});
const cmdOut = await cmd;
if (cmd.child.exitCode !== 0) {
spinner.fail();
console.error(
`failed to install dependencies 🚫\n-------------\n`,
cmdOut.stderr.toString().trim(),
'\n------------'
);
process.exit(1);
}
spinner.stopAndPersist({ suffixText: '✔' });
}
console.log('dependencies installed ✅');
console.log('');
const isLoggedIn = (
await exec('npx wrangler whoami', { cwd: resolve('api') })
).stdout
.toString()
.includes('You are logged in');
if (isLoggedIn) {
console.log("Looks like you've already setup Wrangler, moving on");
} else {
console.log("Next, we're going to setup Wrangler (Cloudflare's CLI)");
console.log(
'Wrangler will open your browser prompting for credentials. Come back here once authorized.'
);
console.log('This setup helper script will never see your credentials');
await wait(4000);
await exec('npx wrangler login', { cwd: resolve('api') });
}
const {
cloudflareAccountId,
connectionString,
authTeamName,
authAudClaim,
bringYourOwnEncryptionSecret,
} = await prompt([
{
name: 'cloudflareAccountId',
message: 'Which Cloudflare account do you want to use?',
type: 'list',
choices: (
await getAvailableAccounts()
).map((account) => ({
name: account.name,
value: account.id,
})),
},
{
message:
'What is your Cloudflare Access team name? <teamName>.cloudflareaccess.com',
type: 'input',
name: 'authTeamName',
},
{
message: 'What is your Cloudflare Access project audience claim?',
type: 'input',
name: 'authAudClaim',
validate: (text) => typeof text === 'string' && /^[a-z0-9]+$/i.test(text),
},
{
message: 'What is your database connection string?',
type: 'password',
name: 'connectionString',
},
{
message: 'Do you have your own encryption secret?',
type: 'confirm',
name: 'hasEncryptionSecret',
},
{
message: 'What is it?',
type: 'password',
name: 'encryptionSecret',
when: (answers) => answers.hasEncryptionSecret,
},
]);
const encryptionSecret =
bringYourOwnEncryptionSecret ?? getRandomString() + getRandomString();
await writeFile(
resolve('api', 'wrangler.toml'),
(await readFile(resolve('api', 'wrangler.toml'), 'utf8'))
.replace('AUTH_AUD_CLAIM = ""', `AUTH_AUD_CLAIM = "${authAudClaim}"`)
.replace('AUTH_TEAM_NAME = ""', `AUTH_TEAM_NAME = "${authTeamName}"`)
);
for (const project of ['api', 'shadower']) {
const content = await readFile(resolve(project, 'wrangler.toml'), 'utf8');
if (!content.includes('account_id')) {
await writeFile(
resolve(project, 'wrangler.toml'),
[`account_id = "${cloudflareAccountId}"`, ...content.split('\n')].join(
'\n'
)
);
}
}
const dir = await mkdtemp(resolve(tmpdir(), 'request-shadowing-config'));
await writeFile(
resolve(dir, 'secret.json'),
JSON.stringify({
DATABASE_CONNECTION_STRING: connectionString,
ENCRYPTION_SECRET: encryptionSecret,
})
);
// Cleanup secrets
process.on('beforeExit', async () => {
try {
await rm(dir, { recursive: true });
} catch (error) {
if (error.code !== 'ENOENT') {
console.error(`‼️ Failed to cleanup secrets. Please delete '${dir}'`);
}
}
});
for (const project of ['api', 'web']) {
const spinner = ora({
text: `${project}: Deploying`,
indent: 2,
}).start();
execSync(`npm run deploy`, {
cwd: project,
});
spinner.stopAndPersist({ suffixText: '✔️' });
}
for (const project of ['api', 'shadower']) {
const spinner = ora({
text: `${project}: Setting secrets`,
indent: 2,
}).start();
execSync(`npx wrangler secret:bulk '${join(dir, 'secret.json')}'`, {
cwd: project,
});
spinner.stopAndPersist({ suffixText: '✔️' });
}
if (!bringYourOwnEncryptionSecret) {
console.log(`🔒 We generated an encryption secret for you`);
console.log('Please store it somewhere safe just in-case');
console.log(encryptionSecret);
}