()
| 13 | /^([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/ |
| 14 | |
| 15 | const ensureNginxBootstrap = async (): Promise<void> => { |
| 16 | const relayDomain = process.env.RELAY_DOMAIN?.trim() |
| 17 | if (!relayDomain) { |
| 18 | throw new Error( |
| 19 | 'RELAY_DOMAIN environment variable is required when using --nginx (example: RELAY_DOMAIN=relay.example.com).', |
| 20 | ) |
| 21 | } |
| 22 | |
| 23 | if (!FQDN_REGEX.test(relayDomain)) { |
| 24 | throw new Error('RELAY_DOMAIN must be a valid fully-qualified domain name when using --nginx.') |
| 25 | } |
| 26 | |
| 27 | const certbotEmail = process.env.CERTBOT_EMAIL?.trim() |
| 28 | if (!certbotEmail) { |
| 29 | throw new Error( |
| 30 | 'CERTBOT_EMAIL environment variable is required when using --nginx (example: CERTBOT_EMAIL=you@example.com).', |
| 31 | ) |
| 32 | } |
| 33 | |
| 34 | const nginxConfDir = getProjectPath('nginx', 'conf.d') |
| 35 | const nginxTemplate = join(nginxConfDir, 'nostream.conf.template') |
| 36 | const nginxConf = join(nginxConfDir, 'nostream.conf') |
| 37 | |
| 38 | const templateContent = fs.readFileSync(nginxTemplate, 'utf-8') |
| 39 | const rendered = templateContent.replaceAll('${RELAY_DOMAIN}', relayDomain) |
| 40 | fs.writeFileSync(nginxConf, rendered, { encoding: 'utf-8' }) |
| 41 | |
| 42 | const sslCertDir = getProjectPath('nginx', 'ssl', 'live', relayDomain) |
| 43 | const fullchainPath = join(sslCertDir, 'fullchain.pem') |
| 44 | const privkeyPath = join(sslCertDir, 'privkey.pem') |
| 45 | |
| 46 | if (!fs.existsSync(fullchainPath) || !fs.existsSync(privkeyPath)) { |
| 47 | fs.mkdirSync(sslCertDir, { recursive: true }) |
| 48 | |
| 49 | const code = await runCommand('openssl', [ |
| 50 | 'req', |
| 51 | '-x509', |
| 52 | '-nodes', |
| 53 | '-newkey', |
| 54 | 'rsa:2048', |
| 55 | '-days', |
| 56 | '1', |
| 57 | '-keyout', |
| 58 | privkeyPath, |
| 59 | '-out', |
| 60 | fullchainPath, |
| 61 | '-subj', |
| 62 | `/CN=${relayDomain}`, |
| 63 | ]) |
| 64 | |
| 65 | if (code !== 0) { |
| 66 | throw new Error('Failed to generate self-signed SSL certificate. Ensure openssl is installed and retry.') |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | export const runStart = async (options: StartOptions, passthrough: string[]): Promise<number> => { |
| 72 | ensureNotRoot() |
no test coverage detected