Add Email Alert architecture analysis

This commit is contained in:
accusys
2026-03-29 12:07:12 +08:00
parent 4cdd01202d
commit 850bc8166f
+305
View File
@@ -0,0 +1,305 @@
# Email Alert 架構分析
## 概述
RAIDGuard X 支援透過 SMTP 發送郵件通知功能,包括:
- 手動發送測試郵件
- 事件觸發自動郵件通知
## 架構流程
```
┌─────────────────────────────────────────────────────────────────────┐
│ Email Alert 架構 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ GUI │ │ Controller │ │ SMTP Server │ │
│ │ frmEmail │◄──────►│ (Port 8922)│ │ │ │
│ └──────┬───────┘ └──────────────┘ └──────┬───────┘ │
│ │ │ │
│ │ In-Band API (OpCode 0x01) │ │
│ │ - SMTP 伺服器 │ │
│ │ - SMTP 使用者名稱/密碼 │ │
│ │ - 寄件人/收件人 │ │
│ │ - 啟用事件通知 │ │
│ └────────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ JavaMail API (javax.mail) │ │
│ │ - Session.getInstance() │ │
│ │ - MimeMessage │ │
│ │ - Transport.send() │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
## 設定介面 (frmEmail)
```
┌─────────────────────────────────────────────────────────────────┐
│ Email Settings │
├─────────────────────────────────────────────────────────────────┤
│ Mailing List: [Send Test Email] │
│ ┌─────────────────────────────────────────┐ │
│ │ user1@example.com │ [Remove] │
│ │ user2@example.com │ │
│ │ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ SMTP Settings: │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Mail Server (SMTP): [smtp.example.com ] │ │
│ │ From Email: [sender@example.com ] │ │
│ │ ☐ SMTP Auth Username: [__________] │ │
│ │ Password: [__________] │ │
│ │ ☐ Post Event to Email (Enable auto-alert) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ [OK] [Cancel] │
└─────────────────────────────────────────────────────────────────┘
```
## 儲存的設定
透過 In-Band API 儲存到控制器:
| OpCode | 說明 |
|--------|------|
| 0x07 | 更新 SMTP 伺服器名稱 |
| 0x09 | 更新 SMTP 使用者名稱 |
| 0x0A | 更新 SMTP 密碼 |
| 0x0B | 取得 SMTP 使用者名稱 |
| 0x0C | 取得 SMTP 密碼 |
| 0x05 | 取得 SMTP 伺服器名稱 |
| 0x0E | 儲存 Post Event 設定 |
| 0x19 | 取得 Post Event 設定 |
## 程式碼分析
### 1. 郵件發送 (frmEmail.java)
```java
// 使用 JavaMail API
import javax.mail.*;
import javax.mail.internet.*;
public void jbSendMail_actionPerformed(ActionEvent e) {
// 建立 Session
Properties props = new Properties();
props.put("mail.smtp.host", this.jtServerIp.getText());
if (jcSMTPCheck.isSelected()) {
// SMTP 認證
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.user", sUsername);
props.put("mail.smtp.password", sPassword);
session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(sUsername, sPassword);
}
});
} else {
// 無認證
props.put("mail.smtp.auth", "false");
session = Session.getInstance(props);
}
// 建立郵件
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(sNameMailFrom));
msg.setSubject(sMailTitle);
msg.setText(sMailBody);
msg.setSentDate(new Date());
// 發送到所有收件人
for (int j = 0; j < dmMailList.getRowCount(); j++) {
sNameMailTo = dmMailList.getValueAt(j, 0).toString().trim();
if (!sNameMailTo.equals("")) {
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(sNameMailTo, false));
Transport.send(msg); // 發送郵件
}
}
}
```
### 2. SMTP 認證設定
```java
// 有認證
if (jcSMTPCheck.isSelected()) {
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true"); // SSL
session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
// 無認證
else {
props.put("mail.smtp.host", smtpServer);
props.put("mail.smtp.auth", "false");
session = Session.getInstance(props);
}
```
### 3. 事件通知開關
```java
// 儲存 Post Event 設定到控制器
bCmds[2] = 0x0E; // OpCode
if (jcPostEvent.isSelected()) {
bCmds[4] = 1; // Enable
} else {
bCmds[4] = 0; // Disable
}
// 透過 CmdQueue 發送到控制器
CmdQueue.NewCommand(ip, sn, 1, 1, 0x01);
```
## 依賴庫
```xml
<!-- pom.xml -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
```
## Polling 與事件觸發
```
┌─────────────────────────────────────────────────────────────────┐
│ Polling Timer (定時輪詢) │
│ - wkPolling1: 每 12 秒 │
│ - wkPolling2: 每 30 秒 │
│ - wkEventPolling: 每 18 秒 │
│ - wkEventPollingForSNMP: 每 22 秒 │
└──────────────────────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 檢測事件 │
│ 1. 取得控制器事件 (Page 0x24) │
│ 2. 比對新事件 │
│ 3. 觸發通知 │
└──────────────────────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 通知方式 │
│ - SNMP Trap (frmMain.EventPollingForSNMP) │
│ - Email Alert (如果 jcPostEvent 啟用) │
│ - UI Event List 更新 │
└─────────────────────────────────────────────────────────────────┘
```
## 關鍵類
| 類別 | 檔案 | 功能 |
|------|------|------|
| `frmEmail` | frmEmail.java | 郵件設定介面 |
| `dlgServer` | dlgServer.java | Server 設定 (包含 SMTP) |
| `PageDataDB` | PageDataDB.java | 郵件設定儲存 |
| `SNMPSender` | SNMP/SNMPSender.java | SNMP Trap 發送 |
## Code Flow
### 1. 設定 SMTP
```
frmEmail (開啟)
→ 輸入 SMTP 伺服器
→ 輸入寄件人/收件人
→ (可選) 啟用 SMTP 認證
→ (可選) 啟用 Post Event
→ OK → 儲存到控制器 (OpCode 0x07/0x09/0x0A/0x0E)
```
### 2. 發送測試郵件
```
點擊 [Send Test Email]
→ frmEmail.jbSendMail_actionPerformed()
→ 建立 Session (JavaMail)
→ 發送到所有收件人
→ 顯示成功/失敗訊息
```
### 3. 事件自動通知
```
Polling 偵測到新事件
→ 檢查 jcPostEvent 是否啟用
→ 組建郵件內容 (事件描述)
→ 發送到所有收件人
```
## 郵件格式
```
Subject: RAIDGuard X Alert - {Controller Name}
Body:
Controller: {SN}
Event: {Event Description}
Time: {Timestamp}
Severity: {Info/Warning/Error}
```
## 安全考量
1. **密碼儲存**: SMTP 密碼儲存在控制器端,可能有安全風險
2. **傳輸加密**: 支援 SSL/TLS (jcSMTPCheck)
3. **認證**: 支援 SMTP AUTH (PLAIN/LOGIN)
## Rust 實作建議
```rust
// 使用 lettre crate
use lettre::{SmtpClient, Transport, Message, SendableEmail};
use lettre::smtp::authentication::Credentials;
pub fn send_email(
smtp_server: &str,
username: &str,
password: &str,
from: &str,
to: &str,
subject: &str,
body: &str,
use_auth: bool,
use_tls: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let email = Message::builder()
.from(from.parse()?)
.to(to.parse()?)
.subject(subject)
.body(body.to_string())?;
let mut smtp = SmtpClient::new_localhost()?;
if use_tls {
smtp = SmtpClient::new_new_ssl(smtp_server)?;
} else {
smtp = SmtpClient::new_new(smtp_server)?;
}
if use_auth {
let creds = Credentials::new(username.to_string(), password.to_string());
smtp = smtp.credentials(creds);
}
let mut transport = smtp.transport();
transport.send(email)?;
Ok(())
}
```