Statusphere, but in atcute and SvelteKit
atproto
svelte
sveltekit
drizzle
atcute
typescript
1import { existsSync } from 'node:fs';
2import { copyFile, readFile, writeFile } from 'node:fs/promises';
3import { resolve } from 'node:path';
4
5import { generateClientAssertionKey } from '@atcute/oauth-node-client';
6
7import { nanoid } from 'nanoid';
8
9const ensureEnv = async () => {
10 const cwd = process.cwd();
11
12 const examplePath = resolve(cwd, '.env.example');
13 const envPath = resolve(cwd, '.env');
14
15 if (!existsSync(envPath)) {
16 if (!existsSync(examplePath)) {
17 throw new Error(`missing .env.example (expected at ${examplePath})`);
18 }
19
20 await copyFile(examplePath, envPath);
21 console.log(`created ${envPath}`);
22 }
23
24 return envPath;
25};
26
27const normalizeCurrentValue = (value) => {
28 const trimmed = value.trim();
29
30 if (trimmed === '' || trimmed === `''` || trimmed === `""`) {
31 return '';
32 }
33
34 return trimmed;
35};
36
37const upsertEnvVar = (input, key, value) => {
38 const line = `${key}=${value}`;
39 const re = new RegExp(`^${key}=.*$`, 'm');
40
41 if (re.test(input)) {
42 const match = input.match(re);
43 const current = match ? match[0].slice(key.length + 1) : '';
44
45 if (normalizeCurrentValue(current) === '') {
46 return input.replace(re, line);
47 }
48
49 return input;
50 }
51
52 const suffix = input.endsWith('\n') || input.length === 0 ? '' : '\n';
53 return `${input}${suffix}${line}\n`;
54};
55
56const envPath = await ensureEnv();
57const env = await readFile(envPath, 'utf8');
58
59let updated = env;
60
61{
62 const cookieSecret = nanoid(32);
63 updated = upsertEnvVar(updated, 'COOKIE_SECRET', `'${cookieSecret}'`);
64}
65
66{
67 const jwk = await generateClientAssertionKey('main', 'ES256');
68 updated = upsertEnvVar(updated, 'OAUTH_PRIVATE_KEY_JWK', `'${JSON.stringify(jwk)}'`);
69}
70
71if (updated !== env) {
72 await writeFile(envPath, updated);
73 console.log(`updated ${envPath}`);
74} else {
75 console.log(`no changes to ${envPath}`);
76}