This commit is contained in:
Baobhan Sith
2025-04-26 19:39:03 +08:00
parent dbd2ce7f0f
commit 62e53c3cdc
13 changed files with 846 additions and 144 deletions
@@ -1,21 +1,30 @@
import { Request, Response } from 'express';
import { NotificationService } from '../services/notification.service';
import { NotificationSetting } from '../types/notification.types';
import { AuditLogService } from '../services/audit.service';
import { NotificationSettingsRepository } from '../repositories/notification.repository'; // Use repository
import { NotificationSetting, NotificationChannelType, NotificationChannelConfig, WebhookConfig, EmailConfig, TelegramConfig, NotificationEvent } from '../types/notification.types';
import { AuditLogService } from '../services/audit.service'; // Keep for now if other parts use it
import { AppEventType, default as eventService } from '../services/event.service'; // Import event service
const auditLogService = new AuditLogService();
// Remove sender imports as they are no longer called directly for testing
// import telegramSenderService from '../services/senders/telegram.sender.service';
// import emailSenderService from '../services/senders/email.sender.service';
// import webhookSenderService from '../services/senders/webhook.sender.service';
// import { ProcessedNotification } from '../services/notification.processor.service'; // Not needed here
// Removed escapeTelegramMarkdownV2 helper function
const auditLogService = new AuditLogService(); // Keep for now if other parts use it, but prefer eventService
export class NotificationController {
private notificationService: NotificationService;
private repository: NotificationSettingsRepository; // Use repository
constructor() {
this.notificationService = new NotificationService();
this.repository = new NotificationSettingsRepository(); // Instantiate repository
}
// GET /api/v1/notifications
getAll = async (req: Request, res: Response): Promise<void> => {
try {
const settings = await this.notificationService.getAllSettings();
const settings = await this.repository.getAll(); // Use repository
res.status(200).json(settings);
} catch (error: any) {
res.status(500).json({ message: '获取通知设置失败', error: error.message });
@@ -32,11 +41,14 @@ export class NotificationController {
}
try {
const newSettingId = await this.notificationService.createSetting(settingData);
const newSetting = await this.notificationService.getSettingById(newSettingId);
// 记录审计日志
const newSettingId = await this.repository.create(settingData); // Use repository
const newSetting = await this.repository.getById(newSettingId);
// 记录审计日志 (Use event service)
if (newSetting) {
auditLogService.logAction('NOTIFICATION_SETTING_CREATED', { settingId: newSetting.id, name: newSetting.name, type: newSetting.channel_type });
eventService.emitEvent(AppEventType.NotificationSettingCreated, {
userId: (req.session as any).userId, // Assuming userId is in session
details: { settingId: newSetting.id, name: newSetting.name, type: newSetting.channel_type }
});
}
res.status(201).json(newSetting);
} catch (error: any) {
@@ -59,11 +71,14 @@ export class NotificationController {
}
try {
const success = await this.notificationService.updateSetting(id, settingData);
const success = await this.repository.update(id, settingData); // Use repository
if (success) {
const updatedSetting = await this.notificationService.getSettingById(id);
// 记录审计日志
auditLogService.logAction('NOTIFICATION_SETTING_UPDATED', { settingId: id, updatedFields: Object.keys(settingData) });
const updatedSetting = await this.repository.getById(id);
// 记录审计日志 (Use event service)
eventService.emitEvent(AppEventType.NotificationSettingUpdated, {
userId: (req.session as any).userId,
details: { settingId: id, updatedFields: Object.keys(settingData) }
});
res.status(200).json(updatedSetting);
} else {
res.status(404).json({ message: `未找到 ID 为 ${id} 的通知设置` });
@@ -83,81 +98,100 @@ export class NotificationController {
}
try {
const success = await this.notificationService.deleteSetting(id);
const settingToDelete = await this.repository.getById(id); // Get details before deleting for audit log
if (!settingToDelete) {
res.status(404).json({ message: `未找到 ID 为 ${id} 的通知设置` });
return;
}
const success = await this.repository.delete(id); // Use repository
if (success) {
// 记录审计日志
auditLogService.logAction('NOTIFICATION_SETTING_DELETED', { settingId: id });
// 记录审计日志 (Use event service)
eventService.emitEvent(AppEventType.NotificationSettingDeleted, {
userId: (req.session as any).userId,
details: { settingId: id, name: settingToDelete.name, type: settingToDelete.channel_type } // Include name/type in audit
});
res.status(204).send(); // No Content
} else {
res.status(404).json({ message: `未找到 ID 为 ${id} 的通知设置` });
// Should not happen if getById succeeded, but handle defensively
res.status(404).json({ message: `删除 ID 为 ${id} 的通知设置失败,可能已被删除` });
}
} catch (error: any) {
res.status(500).json({ message: '删除通知设置失败', error: error.message });
}
};
// --- Refactored Test Endpoints ---
// Removed executeTestSend method as testing now goes through the event system
// POST /api/v1/notifications/:id/test
// Tests an existing, saved setting configuration by triggering a test event
testSetting = async (req: Request, res: Response): Promise<void> => {
const id = parseInt(req.params.id, 10);
const { config } = req.body;
if (isNaN(id)) {
res.status(400).json({ message: '无效的通知设置 ID' });
return;
}
if (!config) {
res.status(400).json({ message: '缺少用于测试的配置信息' });
return;
}
try {
const originalSetting = await this.notificationService.getSettingById(id);
if (!originalSetting) {
const settingToTest = await this.repository.getById(id);
if (!settingToTest) {
res.status(404).json({ message: `未找到 ID 为 ${id} 的通知设置` });
return;
return;
}
const result = await this.notificationService.testSetting(originalSetting.channel_type, config);
// Trigger the standard test event, passing the config to be used by the processor
eventService.emitEvent(AppEventType.TestNotification, {
userId: (req.session as any).userId, // Optional: associate test with user
details: {
message: `为设置 ID ${id} (${settingToTest.name}) 触发的测试`,
testTargetConfig: settingToTest.config, // Pass the config to use
testTargetChannelType: settingToTest.channel_type // Pass the channel type
}
});
// Respond immediately confirming the event was triggered
res.status(200).json({ message: '测试通知事件已触发。请检查对应渠道的接收情况。' });
if (result.success) {
// 记录审计日志 (可选,根据需要决定是否记录测试操作)
res.status(200).json({ message: result.message });
} else {
// 记录审计日志 (可选)
res.status(500).json({ message: result.message });
}
} catch (error: any) {
res.status(500).json({ message: '测试通知设置时发生内部错误', error: error.message });
console.error(`[NotificationController] Error triggering test for setting ${id}:`, error);
res.status(500).json({ message: '触发测试通知时发生内部错误', error: error.message });
}
};
// POST /api/v1/notifications/test-unsaved
// Tests configuration data provided in the request body by triggering a test event
testUnsavedSetting = async (req: Request, res: Response): Promise<void> => {
const { channel_type, config } = req.body;
const { channel_type, config } = req.body as { channel_type: NotificationChannelType, config: NotificationChannelConfig };
if (!channel_type || !config) {
res.status(400).json({ message: '缺少必要的测试信息 (channel_type, config)' });
return;
}
// Basic validation for channel type
if (!['webhook', 'email', 'telegram'].includes(channel_type)) {
res.status(400).json({ message: '无效的渠道类型' });
return;
}
try {
const result = await this.notificationService.testSetting(channel_type, config);
// Trigger the standard test event, passing the unsaved config to be used by the processor
eventService.emitEvent(AppEventType.TestNotification, {
userId: (req.session as any).userId,
details: {
message: `为未保存的 ${channel_type} 配置触发的测试`,
testTargetConfig: config, // Pass the unsaved config to use
testTargetChannelType: channel_type // Pass the channel type
}
});
// Respond immediately confirming the event was triggered
res.status(200).json({ message: '测试通知事件已触发。请检查对应渠道的接收情况。' });
if (result.success) {
res.status(200).json({ message: result.message });
} else {
res.status(500).json({ message: result.message });
}
} catch (error: any) {
res.status(500).json({ message: '测试通知设置时发生内部错误', error: error.message });
console.error(`[NotificationController] Error triggering test for unsaved ${channel_type}:`, error);
res.status(500).json({ message: '触发测试通知时发生内部错误', error: error.message });
}
};
}
} // End of class NotificationController