37 lines
1.7 KiB
JavaScript
37 lines
1.7 KiB
JavaScript
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.<environment>')
|
||
|
|
}
|
||
|
|
|
||
|
|
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`)
|