Files
Emailsorter/server/config/index.mjs
ANDJ ecae89a79d fix(dev): Vite-API-Proxy, Auth, Stripe-Mails und Backend-Erweiterungen
- Client: API-Basis-URL (joinApiUrl, /v1-Falle), Vite strictPort + Proxy 127.0.0.1, Nicht-JSON-Fehler

- Server: /api-404 ohne Wildcard-Bug, SPA-Fallback, Auth-Middleware, Cron, Mailer, Crypto

- Routen: OAuth-State, Email/Stripe/Analytics; client/.env.example

Made-with: Cursor
2026-04-03 00:23:01 +02:00

171 lines
4.4 KiB
JavaScript

/**
* Application Configuration
* Centralized configuration management
*/
import { log } from '../middleware/logger.mjs'
/**
* Environment configuration
*/
export const config = {
// Server
port: parseInt(process.env.PORT || '3000', 10),
nodeEnv: process.env.NODE_ENV || 'development',
isDev: process.env.NODE_ENV !== 'production',
isProd: process.env.NODE_ENV === 'production',
// URLs
baseUrl: process.env.BASE_URL || 'http://localhost:3000',
frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173',
// Appwrite
appwrite: {
endpoint: process.env.APPWRITE_ENDPOINT,
projectId: process.env.APPWRITE_PROJECT_ID,
apiKey: process.env.APPWRITE_API_KEY,
databaseId: process.env.APPWRITE_DATABASE_ID,
},
// Stripe
stripe: {
secretKey: process.env.STRIPE_SECRET_KEY,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
prices: {
basic: process.env.STRIPE_PRICE_BASIC || 'price_basic_monthly',
pro: process.env.STRIPE_PRICE_PRO || 'price_pro_monthly',
business: process.env.STRIPE_PRICE_BUSINESS || 'price_business_monthly',
},
},
// Google OAuth
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
redirectUri: process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3000/api/oauth/gmail/callback',
},
// Microsoft OAuth
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
redirectUri: process.env.MICROSOFT_REDIRECT_URI || 'http://localhost:3000/api/oauth/outlook/callback',
},
// Mistral AI
mistral: {
apiKey: process.env.MISTRAL_API_KEY,
},
// Rate Limiting
rateLimit: {
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '60000', 10),
max: parseInt(process.env.RATE_LIMIT_MAX || '100', 10),
},
// CORS
cors: {
origin: process.env.CORS_ORIGIN || process.env.FRONTEND_URL || 'http://localhost:5173',
credentials: true,
},
// Free Tier Limits
freeTier: {
emailsPerMonth: parseInt(process.env.FREE_TIER_EMAILS_PER_MONTH || '500', 10),
emailAccounts: 1,
autoSchedule: false, // manual only
},
/** Highest product tier (admin comped plan, PLANS key in stripe.mjs). Optional env: TOP_SUBSCRIPTION_PLAN */
topSubscriptionPlan: (process.env.TOP_SUBSCRIPTION_PLAN || 'business').trim().toLowerCase(),
// Admin: comma-separated list of emails with admin rights (e.g. support)
adminEmails: (process.env.ADMIN_EMAILS || '')
.split(',')
.map((e) => e.trim().toLowerCase())
.filter(Boolean),
// Gitea Webhook (Deployment)
gitea: {
webhookSecret: process.env.GITEA_WEBHOOK_SECRET || '',
webhookAuthToken: process.env.GITEA_WEBHOOK_AUTH_TOKEN || process.env.GITEA_WEBHOOK_SECRET || '',
},
/** HMAC secret for Gmail/Outlook OAuth state (recommended in production) */
oauthStateSecret: process.env.OAUTH_STATE_SECRET || '',
}
/**
* Required environment variables
*/
const requiredVars = [
'APPWRITE_ENDPOINT',
'APPWRITE_PROJECT_ID',
'APPWRITE_API_KEY',
'APPWRITE_DATABASE_ID',
'STRIPE_SECRET_KEY',
'STRIPE_WEBHOOK_SECRET',
]
/**
* Optional but recommended variables
*/
const recommendedVars = [
'MISTRAL_API_KEY',
'GOOGLE_CLIENT_ID',
'MICROSOFT_CLIENT_ID',
]
/**
* Validate configuration
*/
export function validateConfig() {
const missing = []
const warnings = []
// Check required variables
for (const varName of requiredVars) {
if (!process.env[varName]) {
missing.push(varName)
}
}
if (missing.length > 0) {
log.error(`Fehlende Umgebungsvariablen: ${missing.join(', ')}`)
process.exit(1)
}
// Check recommended variables
for (const varName of recommendedVars) {
if (!process.env[varName]) {
warnings.push(varName)
}
}
if (warnings.length > 0) {
log.warn(`Optionale Variablen fehlen: ${warnings.join(', ')}`)
}
log.success('Konfiguration validiert')
return true
}
/**
* Feature flags based on available config
*/
export const features = {
gmail: () => Boolean(config.google.clientId && config.google.clientSecret),
outlook: () => Boolean(config.microsoft.clientId && config.microsoft.clientSecret),
ai: () => Boolean(config.mistral.apiKey),
}
/**
* Check if an email has admin rights (support, etc.)
*/
export function isAdmin(email) {
if (!email || typeof email !== 'string') return false
return config.adminEmails.includes(email.trim().toLowerCase())
}
export default config