Files
CloudSearch/source_clean/src/cloud/notification.service.ts

224 lines
7.3 KiB
TypeScript

import { getSystemConfig } from '../admin/system-config.service';
import { getAllNotifiers, getNotifier, getAllNotifierParams, notifyWith } from './notifiers';
import { findPushUserForConfig, getPushUserByAccount } from './push-user.service';
import { getDb } from '../database/database';
type NotifyLevel = 'info' | 'warn' | 'error';
interface NotifyChannel {
name: string;
params: Record<string, any>;
}
// Re-export for API routes
export { getAllNotifiers, getNotifier, getAllNotifierParams };
// ======================== Global channel management ========================
let _globalChannelsCache: NotifyChannel[] | null = null;
let _configHash = '';
/** Returns the full global_notify_config JSON */
export function getGlobalNotifyConfig(): Record<string, any> {
const raw = getSystemConfig('global_notify_config') || '{}';
try { return JSON.parse(raw); } catch { return {}; }
}
export function getGlobalNotifyConfigs(): NotifyChannel[] {
const raw = getSystemConfig('global_notify_config') || '{}';
let globalConfig: any = {};
try { globalConfig = JSON.parse(raw); } catch {}
const channels: NotifyChannel[] = [];
if (globalConfig.channels) {
for (const [name, params] of Object.entries(globalConfig.channels)) {
if (params && typeof params === 'object') {
channels.push({ name, params: { ...(params as any), title: 'CloudSearch' } });
}
}
}
return channels;
}
function checkEventEnabled(eventName: string): boolean {
try {
const raw = getSystemConfig('global_notify_config') || '{}';
let globalConfig: any = {};
try { globalConfig = JSON.parse(raw); } catch {}
if (globalConfig.events && globalConfig.events[`on_${eventName}`] !== undefined) {
return globalConfig.events[`on_${eventName}`] !== false;
}
const val = getSystemConfig(`notify_on_${eventName}`);
return val !== 'false' && val !== '0';
} catch {
return true;
}
}
// ======================== Core send ========================
export async function sendToChannels(channels: NotifyChannel[], title: string, content: string, level: NotifyLevel): Promise<void> {
for (const ch of channels) {
try {
await notifyWith(ch.name, {
...ch.params,
title,
content,
level,
});
} catch (err: any) {
console.error(`[Notify] ${ch.name} error:`, err.message);
}
}
}
// ======================== Export API ========================
export function notify(title: string, content: string, level: NotifyLevel = 'info'): void {
const channels = getGlobalNotifyConfigs();
if (channels.length === 0) return;
sendToChannels(channels, title, content, level).catch(() => {});
}
export function notifyError(title: string, detail: string): void {
notify(title, detail, 'error');
}
export function notifyWarn(title: string, detail: string): void {
notify(title, detail, 'warn');
}
export function notifyInfo(title: string, detail: string): void {
notify(title, detail, 'info');
}
function applyTemplate(template: string, vars: Record<string, string>): string {
return template.replace(/\{([^}]+)\}/g, (_, key) => vars[key] || '{' + key + '}');
}
function getEventTemplates(): Record<string, { title?: string; content: string }> {
try {
const raw = getSystemConfig('global_notify_config') || '{}';
const cfg = JSON.parse(raw);
return cfg.eventTemplates || {};
} catch { return {}; }
}
export function notifyEvent(eventName: string, title: string, content: string, level: NotifyLevel = 'info', templateVars?: Record<string, string>): void {
if (!checkEventEnabled(eventName)) return;
const eventKey = 'on_' + eventName;
const templates = getEventTemplates();
const tmpl = templates[eventKey];
if (tmpl && tmpl.content) {
const vars = { ...(templateVars || {}) };
if (!vars.content && content) vars.content = content;
if (!vars.title && title) vars.title = title;
title = applyTemplate(tmpl.title || title, vars);
content = applyTemplate(tmpl.content, vars);
}
notify(title, content, level);
}
export function notifyConfigEvent(
configId: number,
eventName: string,
title: string,
content: string,
level: NotifyLevel = 'info',
templateVars?: Record<string, string>
): void {
// Apply global template if available
const eventKey = 'on_' + eventName;
const templates = getEventTemplates();
const tmpl = templates[eventKey];
if (tmpl && tmpl.content) {
const vars = { ...(templateVars || {}) };
if (!vars.content && content) vars.content = content;
if (!vars.title && title) vars.title = title;
title = applyTemplate(tmpl.title || title, vars);
content = applyTemplate(tmpl.content, vars);
}
// Find matching push user by cloud_configs.promotion_account
const pushUser = findPushUserForConfig(configId);
if (!pushUser) {
notifyEvent(eventName, title, content, level, templateVars);
return;
}
let notifyConfig: any = {};
try { notifyConfig = JSON.parse(pushUser.notify_config); } catch {}
if (notifyConfig.events && notifyConfig.events[eventKey] === false) return;
const userChannels: NotifyChannel[] = [];
if (notifyConfig.channels) {
for (const [name, params] of Object.entries(notifyConfig.channels)) {
userChannels.push({ name, params: { ...(params as any), title } });
}
}
if (userChannels.length > 0) {
sendToChannels(userChannels, title, content, level).catch(() => {});
} else {
notifyEvent(eventName, title, content, level, templateVars);
}
}
export async function testChannel(
channelName: string,
account?: string,
directParams?: Record<string, any>
): Promise<{ success: boolean; message: string }> {
let params: Record<string, any> = {};
if (directParams) {
params = directParams;
} else if (account) {
const user = getPushUserByAccount(account);
if (user) {
let notifyConfig: any = {};
try { notifyConfig = JSON.parse(user.notify_config); } catch {}
const chParams = notifyConfig.channels?.[channelName];
if (!chParams) return { success: false, message: 'User has not configured this channel' };
params = chParams;
} else {
return { success: false, message: 'Push user not found' };
}
} else {
const channels = getGlobalNotifyConfigs();
const ch = channels.find(c => c.name === channelName);
if (!ch) return { success: false, message: 'Channel not configured globally' };
params = ch.params;
}
const result = await notifyWith(channelName, {
...params,
title: '\u{1F514} CloudSearch Test',
content: `Test message\\nChannel: ${channelName}\\nTime: ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`,
level: 'info',
});
return result;
}
export function saveConfigNotifySettings(configId: number, notify: any): void {
try {
const db = getDb();
db.prepare('UPDATE cloud_configs SET notify_config = ? WHERE id = ?').run(JSON.stringify(notify), configId);
} catch (err: any) {
console.error('[Notify] Failed to save config notify settings:', err.message);
}
}
function getConfigNotifySettings(configId: number): any {
try {
const db = getDb();
const row = db.prepare('SELECT notify_config FROM cloud_configs WHERE id = ?').get(configId) as any;
if (row && row.notify_config) return JSON.parse(row.notify_config);
} catch {}
return {};
}
export function getConfigNotifySettingsJSON(configId: number): any {
return getConfigNotifySettings(configId);
}