// @ts-check import fs from 'node:fs' import path from 'node:path' import { PROJECT_ROOT } from '../../config/runtime.js' const NOTIFICATION_CONFIG_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'notification-config.json') const DEFAULT_BARK_SERVER_URL = 'https://api.day.app' export function getNotificationConfigFilePath() { return NOTIFICATION_CONFIG_FILE_PATH } export function getNotificationConfig() { return loadNotificationConfigFromFile() } export function saveNotificationConfig(rawValue) { const normalized = normalizeNotificationConfig(rawValue) fs.mkdirSync(path.dirname(NOTIFICATION_CONFIG_FILE_PATH), { recursive: true }) fs.writeFileSync(NOTIFICATION_CONFIG_FILE_PATH, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8') return normalized } export function listEnabledBarkRecipients(config = getNotificationConfig()) { if (config.enabled === false || config.channels?.bark?.enabled === false) { return [] } return (Array.isArray(config.channels?.bark?.recipients) ? config.channels.bark.recipients : []) .filter((item) => item.enabled !== false && String(item.deviceKey || '').trim()) } function loadNotificationConfigFromFile() { if (!fs.existsSync(NOTIFICATION_CONFIG_FILE_PATH)) { return createDefaultNotificationConfig() } try { const rawText = fs.readFileSync(NOTIFICATION_CONFIG_FILE_PATH, 'utf8') return normalizeNotificationConfig(JSON.parse(rawText)) } catch { return createDefaultNotificationConfig() } } function normalizeNotificationConfig(rawValue) { const source = isPlainObject(rawValue) ? rawValue : {} const bark = isPlainObject(source.channels?.bark) ? source.channels.bark : {} return { enabled: typeof source.enabled === 'boolean' ? source.enabled : true, channels: { bark: { enabled: typeof bark.enabled === 'boolean' ? bark.enabled : true, serverUrl: normalizeBarkServerUrl(bark.serverUrl), recipients: (Array.isArray(bark.recipients) ? bark.recipients : []) .map((item) => normalizeBarkRecipient(item)) .filter(Boolean), }, }, } } function normalizeBarkRecipient(rawValue) { if (!isPlainObject(rawValue)) { return null } const name = String(rawValue.name || '').trim() const deviceKey = String(rawValue.deviceKey || '').trim() const id = String(rawValue.id || deviceKey || name || '').trim() if (!name && !deviceKey) { return null } return { id, name, deviceKey, enabled: typeof rawValue.enabled === 'boolean' ? rawValue.enabled : true, } } function normalizeBarkServerUrl(value) { const normalized = String(value || DEFAULT_BARK_SERVER_URL).trim() || DEFAULT_BARK_SERVER_URL return normalized.replace(/\/+$/, '') } function createDefaultNotificationConfig() { return { enabled: true, channels: { bark: { enabled: true, serverUrl: DEFAULT_BARK_SERVER_URL, recipients: [], }, }, } } function isPlainObject(value) { return Object.prototype.toString.call(value) === '[object Object]' }