const fs = require('fs'); const path = require('path'); const CONFIG_DIR = path.join(__dirname, '..', 'config'); const SETTINGS_FILE = path.join(CONFIG_DIR, 'admin-settings.json'); const DEFAULT_SETTINGS = { scheduleCron: '*/10 7-22 * * *', randomDelayMinSeconds: 10, randomDelayMaxSeconds: 120, initialDelayMinSeconds: 5, initialDelayMaxSeconds: 30, storeWatchCron: '*/30 * * * *', storeWatchInitialDelayMinSeconds: 10, storeWatchInitialDelayMaxSeconds: 60, storeWatchRequestDelayMs: 1000, storePickupCheckDelayMs: 400, ignoredSlots: [ { storeId: '51450', description: 'TVS' } ], notifications: { ntfy: { enabled: false, serverUrl: 'https://ntfy.sh', topicPrefix: '', username: '', password: '' }, telegram: { enabled: false, botToken: '' } } }; function ensureDir() { if (!fs.existsSync(CONFIG_DIR)) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } } function sanitizeNumber(value, fallback) { const num = Number(value); if (Number.isFinite(num) && num >= 0) { return num; } return fallback; } function sanitizeIgnoredSlots(slots = []) { if (!Array.isArray(slots)) { return DEFAULT_SETTINGS.ignoredSlots; } return slots .map((slot) => ({ storeId: slot?.storeId ? String(slot.storeId) : '', description: slot?.description ? String(slot.description) : '' })) .filter((slot) => slot.storeId); } function sanitizeString(value) { if (typeof value === 'string') { return value.trim(); } if (value === null || value === undefined) { return ''; } return String(value).trim(); } function sanitizeNotifications(input = {}) { const defaults = DEFAULT_SETTINGS.notifications; return { ntfy: { enabled: !!(input?.ntfy?.enabled), serverUrl: sanitizeString(input?.ntfy?.serverUrl || defaults.ntfy.serverUrl), topicPrefix: sanitizeString(input?.ntfy?.topicPrefix || defaults.ntfy.topicPrefix), username: sanitizeString(input?.ntfy?.username || defaults.ntfy.username), password: sanitizeString(input?.ntfy?.password || defaults.ntfy.password) }, telegram: { enabled: !!(input?.telegram?.enabled), botToken: sanitizeString(input?.telegram?.botToken || defaults.telegram.botToken) } }; } function readSettings() { ensureDir(); if (!fs.existsSync(SETTINGS_FILE)) { fs.writeFileSync(SETTINGS_FILE, JSON.stringify(DEFAULT_SETTINGS, null, 2)); return { ...DEFAULT_SETTINGS }; } try { const raw = fs.readFileSync(SETTINGS_FILE, 'utf8'); const parsed = JSON.parse(raw); return { scheduleCron: parsed.scheduleCron || DEFAULT_SETTINGS.scheduleCron, randomDelayMinSeconds: sanitizeNumber(parsed.randomDelayMinSeconds, DEFAULT_SETTINGS.randomDelayMinSeconds), randomDelayMaxSeconds: sanitizeNumber(parsed.randomDelayMaxSeconds, DEFAULT_SETTINGS.randomDelayMaxSeconds), initialDelayMinSeconds: sanitizeNumber(parsed.initialDelayMinSeconds, DEFAULT_SETTINGS.initialDelayMinSeconds), initialDelayMaxSeconds: sanitizeNumber(parsed.initialDelayMaxSeconds, DEFAULT_SETTINGS.initialDelayMaxSeconds), storeWatchCron: parsed.storeWatchCron || DEFAULT_SETTINGS.storeWatchCron, storeWatchInitialDelayMinSeconds: sanitizeNumber( parsed.storeWatchInitialDelayMinSeconds, DEFAULT_SETTINGS.storeWatchInitialDelayMinSeconds ), storeWatchInitialDelayMaxSeconds: sanitizeNumber( parsed.storeWatchInitialDelayMaxSeconds, DEFAULT_SETTINGS.storeWatchInitialDelayMaxSeconds ), storeWatchRequestDelayMs: sanitizeNumber( parsed.storeWatchRequestDelayMs, DEFAULT_SETTINGS.storeWatchRequestDelayMs ), storePickupCheckDelayMs: sanitizeNumber( parsed.storePickupCheckDelayMs, DEFAULT_SETTINGS.storePickupCheckDelayMs ), ignoredSlots: sanitizeIgnoredSlots(parsed.ignoredSlots), notifications: sanitizeNotifications(parsed.notifications) }; } catch (error) { console.error('Konnte Admin-Einstellungen nicht lesen:', error.message); return { ...DEFAULT_SETTINGS }; } } function writeSettings(patch = {}) { const current = readSettings(); const next = { scheduleCron: patch.scheduleCron || current.scheduleCron, randomDelayMinSeconds: sanitizeNumber(patch.randomDelayMinSeconds, current.randomDelayMinSeconds), randomDelayMaxSeconds: sanitizeNumber(patch.randomDelayMaxSeconds, current.randomDelayMaxSeconds), initialDelayMinSeconds: sanitizeNumber(patch.initialDelayMinSeconds, current.initialDelayMinSeconds), initialDelayMaxSeconds: sanitizeNumber(patch.initialDelayMaxSeconds, current.initialDelayMaxSeconds), storeWatchCron: patch.storeWatchCron || current.storeWatchCron, storeWatchInitialDelayMinSeconds: sanitizeNumber( patch.storeWatchInitialDelayMinSeconds, current.storeWatchInitialDelayMinSeconds ), storeWatchInitialDelayMaxSeconds: sanitizeNumber( patch.storeWatchInitialDelayMaxSeconds, current.storeWatchInitialDelayMaxSeconds ), storeWatchRequestDelayMs: sanitizeNumber( patch.storeWatchRequestDelayMs, current.storeWatchRequestDelayMs ), storePickupCheckDelayMs: sanitizeNumber( patch.storePickupCheckDelayMs, current.storePickupCheckDelayMs ), ignoredSlots: patch.ignoredSlots !== undefined ? sanitizeIgnoredSlots(patch.ignoredSlots) : current.ignoredSlots, notifications: patch.notifications !== undefined ? sanitizeNotifications(patch.notifications) : current.notifications }; ensureDir(); fs.writeFileSync(SETTINGS_FILE, JSON.stringify(next, null, 2)); return next; } module.exports = { DEFAULT_SETTINGS, readSettings, writeSettings };