forked from wangziqi/gongxue-base
190 lines
6.7 KiB
JavaScript
190 lines
6.7 KiB
JavaScript
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import pg from 'pg';
|
|
|
|
const DEFAULT_TENANT_ID = '00000000-0000-0000-0000-000000000001';
|
|
|
|
function envString(env, key, fallback = '') {
|
|
return typeof env[key] === 'string' && env[key].trim() ? env[key].trim() : fallback;
|
|
}
|
|
|
|
function envNumberString(env, key, fallback) {
|
|
const value = envString(env, key, fallback);
|
|
return /^\d+$/.test(value) ? value : fallback;
|
|
}
|
|
|
|
function envJsonObject(env, key, fallback) {
|
|
const raw = envString(env, key);
|
|
if (!raw) return fallback;
|
|
const parsed = JSON.parse(raw);
|
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
throw new Error(`${key} must be a JSON object`);
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
function buildConfig(env = process.env) {
|
|
const databaseUrl = envString(env, 'DATABASE_URL');
|
|
const tenantId = envString(env, 'PNVS_TENANT_ID', envString(env, 'TENANT_ID', DEFAULT_TENANT_ID));
|
|
const accessKeyId = envString(env, 'ALIYUN_ACCESS_KEY_ID');
|
|
const accessKeySecret = envString(env, 'ALIYUN_ACCESS_KEY_SECRET');
|
|
const signName = envString(env, 'ALIYUN_PNVS_SIGN_NAME', envString(env, 'ALIYUN_SIGN_NAME'));
|
|
const templateCode = envString(env, 'ALIYUN_PNVS_TEMPLATE_CODE', envString(env, 'ALIYUN_TEMPLATE_CODE'));
|
|
|
|
const missing = [];
|
|
if (!databaseUrl) missing.push('DATABASE_URL');
|
|
if (!tenantId) missing.push('PNVS_TENANT_ID or TENANT_ID');
|
|
if (!accessKeyId) missing.push('ALIYUN_ACCESS_KEY_ID');
|
|
if (!accessKeySecret) missing.push('ALIYUN_ACCESS_KEY_SECRET');
|
|
if (!signName) missing.push('ALIYUN_PNVS_SIGN_NAME or ALIYUN_SIGN_NAME');
|
|
if (!templateCode) missing.push('ALIYUN_PNVS_TEMPLATE_CODE or ALIYUN_TEMPLATE_CODE');
|
|
if (missing.length > 0) throw new Error(`Missing required PNVS config env: ${missing.join(', ')}`);
|
|
|
|
const validTime = envNumberString(env, 'ALIYUN_PNVS_VALID_TIME', '300');
|
|
const interval = envNumberString(env, 'ALIYUN_PNVS_INTERVAL', '60');
|
|
const templateParam = envJsonObject(env, 'ALIYUN_PNVS_TEMPLATE_PARAM', {
|
|
code: '##code##',
|
|
min: String(Math.max(1, Math.ceil(Number(validTime) / 60))),
|
|
});
|
|
if (!Object.values(templateParam).some(value => String(value) === '##code##')) {
|
|
templateParam.code = '##code##';
|
|
}
|
|
|
|
return {
|
|
databaseUrl,
|
|
tenantId,
|
|
accessKeyId,
|
|
accessKeySecret,
|
|
signName,
|
|
templateCode,
|
|
displayName: envString(env, 'ALIYUN_PNVS_DISPLAY_NAME', '阿里云短信认证'),
|
|
endpoint: envString(env, 'ALIYUN_PNVS_ENDPOINT', 'https://dypnsapi.aliyuncs.com'),
|
|
regionId: envString(env, 'ALIYUN_PNVS_REGION_ID', 'cn-hangzhou'),
|
|
countryCode: envString(env, 'ALIYUN_PNVS_COUNTRY_CODE', '86'),
|
|
codeType: envNumberString(env, 'ALIYUN_PNVS_CODE_TYPE', '1'),
|
|
codeLength: envNumberString(env, 'ALIYUN_PNVS_CODE_LENGTH', '6'),
|
|
validTime,
|
|
interval,
|
|
duplicatePolicy: envNumberString(env, 'ALIYUN_PNVS_DUPLICATE_POLICY', '1'),
|
|
schemeName: envString(env, 'ALIYUN_PNVS_SCHEME_NAME'),
|
|
templateParam,
|
|
};
|
|
}
|
|
|
|
function publicConfig(config) {
|
|
return {
|
|
signName: config.signName,
|
|
templateCode: config.templateCode,
|
|
endpoint: config.endpoint,
|
|
regionId: config.regionId,
|
|
countryCode: config.countryCode,
|
|
templateParam: config.templateParam,
|
|
codeType: config.codeType,
|
|
codeLength: config.codeLength,
|
|
validTime: config.validTime,
|
|
interval: config.interval,
|
|
duplicatePolicy: config.duplicatePolicy,
|
|
...(config.schemeName ? { schemeName: config.schemeName } : {}),
|
|
secretRef: 'app_private.tenant_secrets:sms:aliyun-pnvs',
|
|
};
|
|
}
|
|
|
|
async function configureAliyunPnvsProvider(inputConfig, options = {}) {
|
|
const config = inputConfig?.databaseUrl ? inputConfig : buildConfig(options.env || process.env);
|
|
const configPublic = publicConfig(config);
|
|
if (options.dryRun) {
|
|
return {
|
|
tenantId: config.tenantId,
|
|
provider: 'aliyun-pnvs',
|
|
configPublic,
|
|
secretScope: 'sms',
|
|
secretKey: 'aliyun-pnvs',
|
|
};
|
|
}
|
|
|
|
const pool = new pg.Pool({ connectionString: config.databaseUrl, max: 1 });
|
|
try {
|
|
await pool.query('begin');
|
|
await pool.query(
|
|
`
|
|
insert into app_private.tenant_secrets (
|
|
tenant_id, secret_scope, secret_key, provider, secret_json, last_rotated_at
|
|
)
|
|
values ($1::uuid, 'sms', 'aliyun-pnvs', 'aliyun-pnvs', $2::jsonb, now())
|
|
on conflict (tenant_id, secret_scope, secret_key)
|
|
do update set provider = excluded.provider,
|
|
secret_json = excluded.secret_json,
|
|
last_rotated_at = now(),
|
|
updated_at = now()
|
|
`,
|
|
[
|
|
config.tenantId,
|
|
JSON.stringify({
|
|
accessKeyId: config.accessKeyId,
|
|
accessKeySecret: config.accessKeySecret,
|
|
}),
|
|
],
|
|
);
|
|
const providerResult = await pool.query(
|
|
`
|
|
insert into public.tenant_auth_providers (
|
|
tenant_id, provider, status, display_name, config_public
|
|
)
|
|
values ($1::uuid, 'aliyun-pnvs', 'active', $2, $3::jsonb)
|
|
on conflict (tenant_id, provider)
|
|
do update set status = excluded.status,
|
|
display_name = excluded.display_name,
|
|
config_public = excluded.config_public,
|
|
updated_at = now()
|
|
returning tenant_id as "tenantId", provider, status, config_public as "configPublic"
|
|
`,
|
|
[config.tenantId, config.displayName, JSON.stringify(configPublic)],
|
|
);
|
|
await pool.query('commit');
|
|
return providerResult.rows[0];
|
|
} catch (error) {
|
|
await pool.query('rollback').catch(() => undefined);
|
|
throw error;
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
const dryRun = process.argv.includes('--dry-run');
|
|
const result = await configureAliyunPnvsProvider(null, { dryRun });
|
|
console.log(JSON.stringify({
|
|
ok: true,
|
|
dryRun,
|
|
tenantId: result.tenantId,
|
|
provider: result.provider,
|
|
status: result.status || 'active',
|
|
secretRef: result.configPublic?.secretRef || 'app_private.tenant_secrets:sms:aliyun-pnvs',
|
|
}, null, 2));
|
|
} catch (error) {
|
|
console.error(error.message);
|
|
console.error(`
|
|
Required example:
|
|
DATABASE_URL=postgresql://tiku_app:***@127.0.0.1:54322/postgres
|
|
PNVS_TENANT_ID=00000000-0000-0000-0000-000000000001
|
|
ALIYUN_ACCESS_KEY_ID=<real-access-key-id>
|
|
ALIYUN_ACCESS_KEY_SECRET=<real-access-key-secret>
|
|
ALIYUN_PNVS_SIGN_NAME=<sms-sign-name>
|
|
ALIYUN_PNVS_TEMPLATE_CODE=<template-code>
|
|
|
|
Optional:
|
|
ALIYUN_PNVS_TEMPLATE_PARAM='{"code":"##code##","min":"5"}'
|
|
ALIYUN_PNVS_VALID_TIME=300
|
|
ALIYUN_PNVS_INTERVAL=60
|
|
`);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
const currentFile = fileURLToPath(import.meta.url);
|
|
if (process.argv[1] && fileURLToPath(pathToFileURL(process.argv[1])) === currentFile) {
|
|
await main();
|
|
}
|
|
|
|
export { buildConfig, configureAliyunPnvsProvider, publicConfig };
|