const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const webpush = require('web-push'); const ENV_PATH = path.join(__dirname, '..', '.env'); function loadEnv() { const env = {}; if (fs.existsSync(ENV_PATH)) { for (const line of fs.readFileSync(ENV_PATH, 'utf8').split('\n')) { const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/); if (m && !line.trim().startsWith('#')) env[m[1]] = m[2].replace(/^["']|["']$/g, ''); } } return env; } function saveEnv(env) { const lines = []; for (const [k, v] of Object.entries(env)) lines.push(`${k}=${v}`); fs.writeFileSync(ENV_PATH, lines.join('\n') + '\n', 'utf8'); } function ensure(key, generator) { if (process.env[key] || env[key]) return process.env[key] || env[key]; const value = generator(); env[key] = value; saveEnv(env); return value; } const env = loadEnv(); function ensureVapidPair() { if ((process.env.VAPID_PUBLIC_KEY || env.VAPID_PUBLIC_KEY) && (process.env.VAPID_PRIVATE_KEY || env.VAPID_PRIVATE_KEY)) { return { publicKey: process.env.VAPID_PUBLIC_KEY || env.VAPID_PUBLIC_KEY, privateKey: process.env.VAPID_PRIVATE_KEY || env.VAPID_PRIVATE_KEY, }; } const pair = webpush.generateVAPIDKeys(); env.VAPID_PUBLIC_KEY = pair.publicKey; env.VAPID_PRIVATE_KEY = pair.privateKey; saveEnv(env); return pair; } const vapid = ensureVapidPair(); const config = { port: Number(process.env.PORT || env.PORT || 3000), apiKey: process.env.DEEPSEEK_API_KEY || env.DEEPSEEK_API_KEY || '', openrouterApiKey: process.env.OPENROUTER_API_KEY || env.OPENROUTER_API_KEY || '', appPassword: process.env.APP_PASSWORD || env.APP_PASSWORD || '', model: process.env.MODEL || env.MODEL || 'deepseek-v4-flash', thinking: process.env.THINKING || env.THINKING || 'enabled', maxTokens: Number(process.env.MAX_TOKENS || env.MAX_TOKENS || 64000), baseUrl: process.env.DEEPSEEK_BASE_URL || env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com', loginMaxAttempts: Number(process.env.LOGIN_MAX_ATTEMPTS || env.LOGIN_MAX_ATTEMPTS || 5), loginLockMinutes: Number(process.env.LOGIN_LOCK_MINUTES || env.LOGIN_LOCK_MINUTES || 5), nominatimEmail: process.env.NOMINATIM_EMAIL || env.NOMINATIM_EMAIL || '', osrmUrl: process.env.OSRM_URL || env.OSRM_URL || 'https://router.project-osrm.org', sessionSecret: ensure('SESSION_SECRET', () => crypto.randomBytes(32).toString('hex')), vapidPublicKey: vapid.publicKey, vapidPrivateKey: vapid.privateKey, }; const envOr = (key, def) => process.env[key] || env[key] || def; // --------------------------------------------------------------------------- // Modell-Registry: DeepSeek direkt + OpenRouter-Modelle (z.B. GLM 5.3 Flash) // --------------------------------------------------------------------------- const MODELS = [ { id: 'deepseek-v4-flash', label: 'DeepSeek V4 Flash', provider: 'deepseek', vision: false, // Max. Output laut Doku: 384K – großzügiges Limit gegen abgeschnittene Dashboards maxTokens: 131072, reasoningEffort: () => envOr('DEEPSEEK_REASONING_EFFORT', 'max'), prices: { inputCacheHit: 0.014, inputCacheMiss: 0.44, output: 1.32 }, }, { id: 'z-ai/glm-5.3-flash', label: 'GLM 5.3 Flash (OpenRouter)', provider: 'openrouter', vision: true, // Max. Completion laut OpenRouter: 131072 maxTokens: 131072, reasoningEffort: () => envOr('GLM_REASONING_EFFORT', 'max'), prices: { inputCacheHit: 0.015, inputCacheMiss: 0.075, output: 0.25 }, costNote: 'Schätzung (OpenRouter, Rabatt bis Sep 2026)', }, ]; /** Registry inkl. benutzerdefiniertem Modell aus der Env (MODEL), falls nicht bekannt. */ function getModels() { if (MODELS.some((m) => m.id === config.model)) return MODELS; return [ ...MODELS, { id: config.model, label: config.model, provider: 'deepseek', vision: false, maxTokens: config.maxTokens, reasoningEffort: () => envOr('DEEPSEEK_REASONING_EFFORT', 'max'), prices: { inputCacheHit: 0.014, inputCacheMiss: 0.44, output: 1.32 }, }, ]; } /** Modell per ID auflösen (Fallback: Default-Modell aus Env). */ function getModel(id) { const models = getModels(); return models.find((m) => m.id === id) || models.find((m) => m.id === config.model) || models[0]; } module.exports = { config, MODELS, getModels, getModel };