import { generateKeyPairSync, randomUUID } from 'node:crypto' import { readFileSync, writeFileSync } from 'node:fs' import { basename, dirname, resolve } from 'node:path' const envPath = resolve(process.argv[2] || '') if (!envPath || basename(dirname(envPath)) !== 'env' || !basename(envPath).startsWith('.env.')) { throw new Error('用法: node scripts/generate-overseas-jwk.mjs deploy/env/.env.') } let source = readFileSync(envPath, 'utf8') const existing = source.match(/^OVERSEAS_JWT_RSA_JWK_SET=(.*)$/m)?.[1]?.trim() if (existing) throw new Error('目标环境已经配置 OVERSEAS_JWT_RSA_JWK_SET,拒绝覆盖') const environment = basename(envPath).slice('.env.'.length).replace(/[^a-zA-Z0-9-]/g, '-') const kid = `${environment}-overseas-${new Date().toISOString().slice(0, 10)}-${randomUUID().slice(0, 8)}` const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 3072 }) const privateJwk = privateKey.export({ format: 'jwk' }) const jwkSet = JSON.stringify({ keys: [{ ...privateJwk, use: 'sig', alg: 'RS256', kid }] }) const values = new Map([ ['OVERSEAS_JWT_ENABLED', 'true'], ['OVERSEAS_JWT_ISSUER', 'auth-service'], ['OVERSEAS_JWT_AUDIENCE', 'lastleg-api'], ['OVERSEAS_JWT_RSA_JWK_SET', `'${jwkSet}'`], ['OVERSEAS_JWT_CURRENT_KID', kid], ['OVERSEAS_ACCESS_TOKEN_TTL', '15m'], ]) for (const [key, value] of values) { const pattern = new RegExp(`^${key}=.*$`, 'm') if (pattern.test(source)) source = source.replace(pattern, `${key}=${value}`) else source += `${source.endsWith('\n') ? '' : '\n'}${key}=${value}\n` } writeFileSync(envPath, source, 'utf8') process.stdout.write(`Configured development-only overseas JWK kid: ${kid}\n`)