fix: normalize PNVS SMS provider aliases

This commit is contained in:
Codex
2026-07-04 00:27:56 +08:00
parent a6ffb6b962
commit 3a10daf847
6 changed files with 38 additions and 7 deletions

View File

@@ -55,10 +55,8 @@ const DEFAULT_MAX_IMPORT_JSON_BODY_BYTES = 10 * 1024 * 1024;
const HARD_MAX_JSON_BODY_BYTES = 50 * 1024 * 1024;
const PRODUCTION_SMS_PROVIDERS = new Set([
'aliyun-pnvs',
'aliyun_pnvs',
'aliyun-pnvs-sms',
'aliyun-sms-auth',
'aliyun_sms_auth',
]);
const PRODUCTION_STORAGE_PROVIDERS = new Set(['aliyun_oss', 'tencent_cos', 'supabase_storage']);
@@ -107,7 +105,7 @@ function validateProductionConfig(nextConfig: ApiConfig) {
const failures: string[] = [];
if (nextConfig.corsOrigins.includes('*')) failures.push('CORS_ORIGIN must not include * in production');
if (!PRODUCTION_SMS_PROVIDERS.has(nextConfig.authSmsProvider.trim().toLowerCase())) {
if (!PRODUCTION_SMS_PROVIDERS.has(nextConfig.authSmsProvider.trim().toLowerCase().replace(/[_\s]/g, '-'))) {
failures.push('AUTH_SMS_PROVIDER must be aliyun-pnvs in production');
}
if (isUnsafeSecret(nextConfig.authCodePepper, DEFAULT_AUTH_CODE_PEPPER)) {

View File

@@ -208,7 +208,7 @@ async function consumeSmsCode(
}
function normalizeSmsProviderName(value: string) {
const normalized = value.toLowerCase().replace(/_/g, '-');
const normalized = value.toLowerCase().replace(/[_\s]/g, '-');
if (normalized === 'aliyun-pnvs' || normalized === 'aliyun-pnvs-sms' || normalized === 'aliyun-sms-auth') return 'aliyun-pnvs';
if (normalized === 'aliyun' || normalized === 'aliyun-sms') return 'aliyun';
if (normalized === 'tencent' || normalized === 'tencent-sms') return 'tencent';

View File

@@ -19,6 +19,7 @@ assert.match(providerSource, /CheckSmsVerifyCode/, 'PNVS provider should call Ch
assert.match(providerSource, /VerifyResult.*PASS/s, 'PNVS verify should require provider PASS result');
assert.match(providerSource, /##code##/, 'PNVS provider should preserve Aliyun-generated code placeholder');
assert.match(routeSource, /aliyun-pnvs/, 'auth routes should recognize aliyun-pnvs aliases');
assert.match(routeSource, /replace\(\s*\/\[_\\s\]\/g,\s*'-'\s*\)/, 'auth routes should normalize underscore PNVS provider aliases before runtime selection');
assert.match(routeSource, /verification.*provider/s, 'auth routes should store provider verification mode');
assert.match(routeSource, /provider\.verify/, 'auth routes should delegate PNVS verification to provider');
assert.match(routeSource, /export async function bindPhoneRoute[\s\S]*activeSmsProvider[\s\S]*consumeSmsCode/, 'phone binding should reuse active SMS provider verification');

View File

@@ -120,6 +120,12 @@ assert.match(
const safeApi = runImport(apiConfigUrl, safeApiEnv);
assert.equal(safeApi.status, 0, `safe production API config should load: ${safeApi.output}`);
const safeApiUnderscorePnvsAlias = runImport(apiConfigUrl, {
...safeApiEnv,
AUTH_SMS_PROVIDER: 'aliyun_pnvs',
});
assert.equal(safeApiUnderscorePnvsAlias.status, 0, `underscore PNVS alias should load in production API config: ${safeApiUnderscorePnvsAlias.output}`);
const unsafeWorker = runImport(workerConfigUrl, {
...safeBaseEnv,
STORAGE_DEFAULT_PROVIDER: 'local_dev',

View File

@@ -196,6 +196,34 @@ WORKER_PUBLIC_BANK_SYNC_BATCH_SIZE=5
assert.equal(safeAliyunPnvs.status, 0, `aliyun-pnvs readiness should pass without blockers: ${safeAliyunPnvs.stdout} ${safeAliyunPnvs.stderr}`);
assert.equal(safeAliyunPnvs.payload.summary?.blocker, 0, 'aliyun-pnvs readiness should have no blockers');
const safeAliyunPnvsUnderscoreAlias = runReadiness(`
NODE_ENV=production
DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku
CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com
AUTH_SMS_PROVIDER=aliyun_pnvs
AUTH_CODE_PEPPER=${strongSecretA}
AUTH_SESSION_SECRET=${strongSecretB}
AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json
AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1
ALLOW_LEGACY_AUTH_HEADERS=false
ALLOW_PLATFORM_ADMIN_KEY=false
PLATFORM_ADMIN_API_KEY=${strongSecretC}
STORAGE_DEFAULT_PROVIDER=aliyun_oss
STORAGE_DEFAULT_BUCKET=tiku-assets
STORAGE_REQUIRE_TENANT_PREFIX=true
ALIYUN_OSS_REGION=cn-hangzhou
ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY
ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder
WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http
WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan
WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx
WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false
`);
assert.equal(safeAliyunPnvsUnderscoreAlias.status, 0, `aliyun_pnvs readiness should pass without blockers: ${safeAliyunPnvsUnderscoreAlias.stdout} ${safeAliyunPnvsUnderscoreAlias.stderr}`);
assert.equal(safeAliyunPnvsUnderscoreAlias.payload.summary?.blocker, 0, 'aliyun_pnvs readiness should have no blockers');
const pnvsTemplateParamWarning = runReadiness(
`
NODE_ENV=production

View File

@@ -12,10 +12,8 @@ const DEFAULT_PLATFORM_ADMIN_API_KEY = 'local-platform-admin-key';
const HARD_MAX_JSON_BODY_BYTES = 50 * 1024 * 1024;
const PRODUCTION_SMS_PROVIDERS = new Set([
'aliyun-pnvs',
'aliyun_pnvs',
'aliyun-pnvs-sms',
'aliyun-sms-auth',
'aliyun_sms_auth',
]);
const PRODUCTION_STORAGE_PROVIDERS = new Set(['aliyun_oss', 'tencent_cos', 'supabase_storage']);
const AUTH_PROVIDER_ALIASES = {
@@ -567,7 +565,7 @@ function validateEnv() {
}
}
const authSmsProvider = env('AUTH_SMS_PROVIDER', 'mock').trim().toLowerCase();
const authSmsProvider = normalizeProvider(env('AUTH_SMS_PROVIDER', 'mock')).replace(/[_\s]/g, '-');
if (!PRODUCTION_SMS_PROVIDERS.has(authSmsProvider)) {
block('env.auth_sms_provider', 'AUTH_SMS_PROVIDER must be aliyun-pnvs in production', {
provider: authSmsProvider || '(empty)',