import { randomBytes } from 'node:crypto' import { copyFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { parseArgs } from 'node:util' import { JoseKey } from '@atproto/oauth-client-node' const { values } = parseArgs({ options: { write: { type: 'boolean' } } }) const key = await JoseKey.generate(['ES256'], `key-${Date.now()}`) const secrets = { OAUTH_PRIVATE_KEYS: `'${JSON.stringify([key.privateJwk])}'`, SECRET_KEY: randomBytes(32).toString('base64'), } if (!values.write) { for (const [name, value] of Object.entries(secrets)) console.log(`${name}=${value}`) process.exit(0) } // --write fills the repository's .env, creating it from .env.example first. A secret that is // already set is never replaced, since a new SECRET_KEY can't decrypt the stored API keys. const envPath = fileURLToPath(new URL('../../../.env', import.meta.url)) if (!existsSync(envPath)) { copyFileSync(fileURLToPath(new URL('../../../.env.example', import.meta.url)), envPath) console.log('Created .env from .env.example.') } let env = readFileSync(envPath, 'utf8') for (const [name, value] of Object.entries(secrets)) { const line = new RegExp(`^${name}=(.*)$`, 'm') const current = env.match(line) if (current?.[1]) { console.log(`${name} is already set, so it was left as is.`) continue } env = current ? env.replace(line, () => `${name}=${value}`) : `${env.replace(/\n?$/, '\n')}${name}=${value}\n` console.log(`Wrote ${name}.`) } writeFileSync(envPath, env) if (/^ADMIN_DIDS=$/m.test(env)) console.log('Next, put your DID in ADMIN_DIDS in .env.')