gongxiangp2/lib/env-validation.ts
chenpei 752b7bf999 fix: 修复批量上传和视频扩展名问题
主要修复:

- 修复批量上传闭包陷阱,防止文件丢失

- 添加 fileName 参数传递原始文件名,保留正确扩展名

- 添加取消检查回调,改进批量上传取消功能

其他改进:

- 优化搜索功能和管理后台

- 添加 FRP 故障诊断文档

- 更新环境验证和网络检测
2026-02-03 11:12:50 +08:00

264 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* R47修复:环境变量启动验证
*
* 目的:在应用启动时验证所有必需的环境变量,提供清晰的错误提示
* 时机:在 lib/prisma.ts 和其他关键模块中调用
*/
/**
* 环境变量验证错误
*/
export class EnvValidationError extends Error {
public missingVars: string[]
constructor(missingVars: string[]) {
const varList = missingVars.map(v => ` - ${v}`).join('\n')
super(
`缺少必需的环境变量:\n${varList}\n\n` +
`请在 .env 文件或 docker-compose.yml 中配置这些变量。\n` +
`详情请参考:.env.example 或项目文档。`
)
this.name = 'EnvValidationError'
this.missingVars = missingVars
}
}
/**
* 必需的环境变量列表
*/
const REQUIRED_ENV_VARS = [
// 数据库
'DATABASE_URL',
// NextAuth
'NEXTAUTH_SECRET',
// NEXTAUTH_URL - 改为可选,让 NextAuth 根据请求自动检测(支持内网/公网自适应)
// S3/MinIO(如果使用S3存储)
// 'S3_ENDPOINT',
// 'S3_BUCKET',
// 'S3_ACCESS_KEY_ID',
// 'S3_SECRET_ACCESS_KEY',
// Redis(如果使用Redis)
// 'REDIS_URL',
// 'USE_REDIS',
// 管理员API密钥 - 改为可选,不阻塞应用启动
// 'ADMIN_API_KEY',
]
/**
* 可选但建议配置的环境变量
*/
const OPTIONAL_ENV_VARS = [
// NextAuth URL(可选,不设置时 NextAuth 会自动检测)
'NEXTAUTH_URL',
// S3/MinIO
'S3_ENDPOINT',
'S3_BUCKET',
'S3_ACCESS_KEY_ID',
'S3_SECRET_ACCESS_KEY',
// Redis
'REDIS_URL',
'USE_REDIS',
'DINGTALK_APP_KEY',
'DINGTALK_APP_SECRET',
'DOWNLOAD_TOKEN_SECRET',
'DISABLE_CACHE',
'ADMIN_API_KEY', // 管理员API密钥(用于外部API调用验证)
]
/**
* 验证所有必需的环境变量
* @throws {EnvValidationError} 如果缺少必需的环境变量
*/
export function validateRequiredEnvVars(): void {
const missing: string[] = []
for (const varName of REQUIRED_ENV_VARS) {
if (!process.env[varName]) {
missing.push(varName)
}
}
if (missing.length > 0) {
throw new EnvValidationError(missing)
}
console.log('[Env] ✅ 所有必需的环境变量已配置')
}
/**
* 验证环境变量的格式
* @throws {Error} 如果环境变量格式不正确
*/
export function validateEnvFormats(): void {
const errors: string[] = []
// 验证 DATABASE_URL 格式
const databaseUrl = process.env.DATABASE_URL
if (databaseUrl) {
try {
const url = new URL(databaseUrl)
if (url.protocol !== 'postgresql:' && url.protocol !== 'postgres:') {
errors.push(`DATABASE_URL 格式错误:应该使用 postgresql:// 或 postgres:// 协议`)
}
} catch (e) {
errors.push(`DATABASE_URL 格式错误:${e}`)
}
}
// 验证 NEXTAUTH_URL 格式
const nextauthUrl = process.env.NEXTAUTH_URL
if (nextauthUrl) {
try {
new URL(nextauthUrl)
} catch (e) {
errors.push(`NEXTAUTH_URL 格式错误:${e}`)
}
}
// 验证 REDIS_URL 格式(如果配置了)
const redisUrl = process.env.REDIS_URL
if (redisUrl) {
try {
const url = new URL(redisUrl)
if (url.protocol !== 'redis:') {
errors.push(`REDIS_URL 格式错误:应该使用 redis:// 协议`)
}
} catch (e) {
errors.push(`REDIS_URL 格式错误:${e}`)
}
}
// 验证 S3_ENDPOINT 格式(如果配置了)
const s3Endpoint = process.env.S3_ENDPOINT
if (s3Endpoint) {
try {
const url = new URL(s3Endpoint)
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
errors.push(`S3_ENDPOINT 格式错误:应该使用 http:// 或 https:// 协议`)
}
} catch (e) {
errors.push(`S3_ENDPOINT 格式错误:${e}`)
}
}
if (errors.length > 0) {
const errorList = errors.map(e => ` - ${e}`).join('\n')
throw new Error(
`环境变量格式错误:\n${errorList}\n\n` +
`请检查 .env 文件或 docker-compose.yml 中的配置。`
)
}
console.log('[Env] ✅ 环境变量格式验证通过')
}
/**
* 检查可选环境变量并给出提示
*/
export function checkOptionalEnvVars(): void {
const configured: string[] = []
const notConfigured: string[] = []
for (const varName of OPTIONAL_ENV_VARS) {
if (process.env[varName]) {
configured.push(varName)
} else {
notConfigured.push(varName)
}
}
if (configured.length > 0) {
console.log(`[Env] ℹ️ 已配置的可选变量:${configured.join(', ')}`)
}
if (notConfigured.length > 0) {
console.log(`[Env] ⚠️ 未配置的可选变量:${notConfigured.join(', ')}`)
}
}
/**
* 检测是否在 Next.js 构建阶段
* 构建时不应该验证环境变量,因为构建环境没有运行时环境变量
*/
function isBuildPhase(): boolean {
// 方式1:检测 NEXT_PHASE 环境变量
if (process.env.NEXT_PHASE === 'phase-production-build') {
return true
}
// 方式2:检测 npm_lifecycle_event(构建脚本中会设置)
if (process.env.npm_lifecycle_event === 'build') {
return true
}
// 方式3:检测 __NEXT_PRIVATE_PREBUNDLED_REACT(Next.js 内部变量)
if (process.env.__NEXT_PRIVATE_PREBUNDLED_REACT) {
return true
}
return false
}
/**
* 完整的环境变量验证(在应用启动时调用)
* @throws {Error} 如果验证失败且在生产运行时环境
*/
export function validateEnvironment(): void {
// 构建阶段跳过验证
if (isBuildPhase()) {
return
}
console.log('[Env] 🔍 开始验证环境变量...')
try {
validateRequiredEnvVars()
validateEnvFormats()
checkOptionalEnvVars()
console.log('[Env] ✅ 环境变量验证完成')
} catch (error) {
console.error('[Env] ❌ 环境变量验证失败:', error)
// 只在生产运行时抛出错误,开发环境只记录警告
if (process.env.NODE_ENV === 'production') {
throw error
}
console.warn('[Env] ⚠️ 非 production 环境,跳过环境变量验证错误')
}
}
/**
* 安全获取环境变量(带默认值和类型转换)
*/
export function getEnvVar(key: string, defaultValue?: string): string | undefined {
const value = process.env[key]
if (value === undefined || value === '') {
return defaultValue
}
return value
}
/**
* 获取布尔型环境变量
*/
export function getBoolEnvVar(key: string, defaultValue: boolean = false): boolean {
const value = process.env[key]
if (value === undefined || value === '') {
return defaultValue
}
return value === 'true' || value === '1'
}
/**
* 获取数字型环境变量
*/
export function getNumberEnvVar(key: string, defaultValue: number = 0): number {
const value = process.env[key]
if (value === undefined || value === '') {
return defaultValue
}
const num = parseInt(value, 10)
return isNaN(num) ? defaultValue : num
}