mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-18 14:24:13 +00:00
- Add Payload email adapter for system emails (auth, password reset) - Add EmailLogs collection for tracking all sent emails - Extend Tenants collection with SMTP configuration fields - Implement tenant-specific email service with transporter caching - Add /api/send-email endpoint with: - Authentication required - Tenant access control (users can only send for their tenants) - Rate limiting (10 emails/minute per user) - Add form submission notification hook with email logging - Add cache invalidation hook for tenant email config changes Security: - SMTP passwords are never returned in API responses - Passwords are preserved when field is left empty on update - Only super admins can delete email logs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
// src/lib/envValidation.ts
|
|
|
|
/**
|
|
* Zentrale Validierung aller erforderlichen Environment-Variablen.
|
|
* Wird beim Server-Start aufgerufen und beendet den Prozess bei fehlenden Werten.
|
|
*/
|
|
|
|
interface RequiredEnvVars {
|
|
PAYLOAD_SECRET: string
|
|
DATABASE_URI: string
|
|
CONSENT_LOGGING_API_KEY: string
|
|
IP_ANONYMIZATION_PEPPER: string
|
|
}
|
|
|
|
// Optionale SMTP-Konfiguration (Fallback für Tenants ohne eigene SMTP-Config)
|
|
export interface SmtpEnvVars {
|
|
SMTP_HOST?: string
|
|
SMTP_PORT?: string
|
|
SMTP_SECURE?: string
|
|
SMTP_USER?: string
|
|
SMTP_PASS?: string
|
|
SMTP_FROM_ADDRESS?: string
|
|
SMTP_FROM_NAME?: string
|
|
}
|
|
|
|
export function getSmtpConfig(): SmtpEnvVars {
|
|
return {
|
|
SMTP_HOST: process.env.SMTP_HOST,
|
|
SMTP_PORT: process.env.SMTP_PORT,
|
|
SMTP_SECURE: process.env.SMTP_SECURE,
|
|
SMTP_USER: process.env.SMTP_USER,
|
|
SMTP_PASS: process.env.SMTP_PASS,
|
|
SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
|
|
SMTP_FROM_NAME: process.env.SMTP_FROM_NAME,
|
|
}
|
|
}
|
|
|
|
const FORBIDDEN_VALUES = [
|
|
'',
|
|
'default-pepper-change-me',
|
|
'change-me',
|
|
'your-secret-here',
|
|
'xxx',
|
|
]
|
|
|
|
function validateEnvVar(name: string, value: string | undefined): string {
|
|
if (!value || value.trim() === '') {
|
|
throw new Error(
|
|
`FATAL: Environment variable ${name} is required but not set. ` +
|
|
`Server cannot start without this value.`,
|
|
)
|
|
}
|
|
|
|
if (FORBIDDEN_VALUES.includes(value.trim().toLowerCase())) {
|
|
throw new Error(
|
|
`FATAL: Environment variable ${name} has an insecure default value. ` +
|
|
`Please set a secure random value.`,
|
|
)
|
|
}
|
|
|
|
return value.trim()
|
|
}
|
|
|
|
/**
|
|
* Validiert alle erforderlichen Environment-Variablen.
|
|
* Wirft einen Fehler und beendet den Server-Start, wenn Variablen fehlen.
|
|
*/
|
|
export function validateRequiredEnvVars(): RequiredEnvVars {
|
|
return {
|
|
PAYLOAD_SECRET: validateEnvVar('PAYLOAD_SECRET', process.env.PAYLOAD_SECRET),
|
|
DATABASE_URI: validateEnvVar('DATABASE_URI', process.env.DATABASE_URI),
|
|
CONSENT_LOGGING_API_KEY: validateEnvVar(
|
|
'CONSENT_LOGGING_API_KEY',
|
|
process.env.CONSENT_LOGGING_API_KEY,
|
|
),
|
|
IP_ANONYMIZATION_PEPPER: validateEnvVar(
|
|
'IP_ANONYMIZATION_PEPPER',
|
|
process.env.IP_ANONYMIZATION_PEPPER,
|
|
),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lazy-initialized Environment-Variablen.
|
|
* Wird erst beim ersten Zugriff validiert (vermeidet Build-Probleme).
|
|
*/
|
|
let _cachedEnv: RequiredEnvVars | null = null
|
|
|
|
export const env: RequiredEnvVars = new Proxy({} as RequiredEnvVars, {
|
|
get(_, prop: keyof RequiredEnvVars) {
|
|
if (!_cachedEnv) {
|
|
_cachedEnv = validateRequiredEnvVars()
|
|
}
|
|
return _cachedEnv[prop]
|
|
},
|
|
})
|