Files
order_site/apps/backend/src/services/notification/config-service.ts
T

148 lines
4.3 KiB
TypeScript

import path from 'node:path'
import { PROJECT_ROOT } from '../../config/runtime.js'
import { readJsonFile, writeJsonFile } from '../../utils/json-file-store.js'
const NOTIFICATION_CONFIG_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'notification-config.json')
const DEFAULT_BARK_SERVER_URL = 'https://api.day.app'
type JsonObject = Record<string, any>
type NotificationRecipient = {
id: string
name: string
deviceKey?: string
apiKey?: string
enabled: boolean
}
export function getNotificationConfigFilePath() {
return NOTIFICATION_CONFIG_FILE_PATH
}
export function getNotificationConfig() {
return loadNotificationConfigFromFile()
}
export function saveNotificationConfig(rawValue: unknown) {
return writeJsonFile(NOTIFICATION_CONFIG_FILE_PATH, rawValue, normalizeNotificationConfig)
}
export function listEnabledBarkRecipients(config: JsonObject = getNotificationConfig()) {
if (config.enabled === false || config.channels?.bark?.enabled === false) {
return []
}
return (Array.isArray(config.channels?.bark?.recipients) ? config.channels.bark.recipients : [])
.filter((item: JsonObject) => item.enabled !== false && String(item.deviceKey || '').trim())
}
export function listEnabledWpushRecipients(config: JsonObject = getNotificationConfig()) {
if (config.enabled === false || config.channels?.wpush?.enabled === false) {
return []
}
return (Array.isArray(config.channels?.wpush?.recipients) ? config.channels.wpush.recipients : [])
.filter((item: JsonObject) => item.enabled !== false && String(item.apiKey || item.apikey || '').trim())
}
function loadNotificationConfigFromFile() {
return readJsonFile(
NOTIFICATION_CONFIG_FILE_PATH,
createDefaultNotificationConfig,
normalizeNotificationConfig,
)
}
export function normalizeNotificationConfig(rawValue: unknown) {
const source = isPlainObject(rawValue) ? rawValue : {}
const bark = isPlainObject(source.channels?.bark) ? source.channels.bark : {}
const wpush = isPlainObject(source.channels?.wpush) ? source.channels.wpush : {}
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: unknown) => normalizeBarkRecipient(item))
.filter(Boolean),
},
wpush: {
enabled: typeof wpush.enabled === 'boolean' ? wpush.enabled : true,
recipients: (Array.isArray(wpush.recipients) ? wpush.recipients : [])
.map((item: unknown) => normalizeWpushRecipient(item))
.filter(Boolean),
},
},
}
}
function normalizeBarkRecipient(rawValue: unknown): NotificationRecipient | null {
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: unknown) {
const normalized = String(value || DEFAULT_BARK_SERVER_URL).trim() || DEFAULT_BARK_SERVER_URL
return normalized.replace(/\/+$/, '')
}
function normalizeWpushRecipient(rawValue: unknown): NotificationRecipient | null {
if (!isPlainObject(rawValue)) {
return null
}
const name = String(rawValue.name || '').trim()
const apiKey = String(rawValue.apiKey || rawValue.apikey || '').trim()
const id = String(rawValue.id || apiKey || name || '').trim()
if (!name && !apiKey) {
return null
}
return {
id,
name,
apiKey,
enabled: typeof rawValue.enabled === 'boolean' ? rawValue.enabled : true,
}
}
export function createDefaultNotificationConfig() {
return {
enabled: true,
channels: {
bark: {
enabled: true,
serverUrl: DEFAULT_BARK_SERVER_URL,
recipients: [] as NotificationRecipient[],
},
wpush: {
enabled: true,
recipients: [] as NotificationRecipient[],
},
},
}
}
function isPlainObject(value: unknown): value is JsonObject {
return Object.prototype.toString.call(value) === '[object Object]'
}