iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
Build on AWS

從一個網站的誕生,看懂 AWS 架構與自動化的全流程!系列 第 23

Day 23 管理員公告系統:API Gateway x SNS / SES 訊息發布

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20251007/20172743sLPmP7gzJv.png

一、前言

在一個完整的會員系統中,除了會員通知外,還需要讓管理員具備 一對多的公告能力,例如:系統維護公告、活動通知、緊急訊息。若沒有集中化的公告管道,管理員可能需要透過多種工具分別通知會員,既容易出錯,也難以統一管理。

透過 API Gateway x SNS / SES,可以實現由管理員後台統一發布公告,並自動推送到 Email 或其他通道。

此 Lab 的定位是 後台訊息發布模組,解決以下痛點:

(1) 管理員公告缺乏集中化,造成通知延遲與不一致。
(2) 難以跨管道(Email / SMS / Push)同時發布訊息。
(3) 缺乏彈性與稽核機制,難以追蹤誰發布了公告。

在整體 Serverless 架構中,它屬於 系統後台模組,負責公告訊息的統一入口,並整合至既有會員通知通道(SES / SNS)。

二、需要使用到的服務

  1. Amazon SNS:集中式訊息中介,負責將公告廣播給不同的訂閱者(Email / Lambda / SQS)。
  2. Amazon SES:寄送 Email 公告。
  3. Amazon API Gateway:提供管理員後台 API,讓公告能透過 Portal 發布。
  4. AWS Lambda:接收公告內容,轉換成 Email 模板並呼叫 SES。
  5. Amazon DynamoDB(可選):紀錄公告發送紀錄,提供稽核與追蹤。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743b9A1bJ5Pva.png

三、架構/概念圖

https://ithelp.ithome.com.tw/upload/images/20251007/20172743hncDwALNt7.png

四、技術重點

  1. 權限控管:只有具備 Admin 角色的使用者能呼叫公告 API。
  2. 公告稽核:將公告紀錄存入 DynamoDB,包含 createdBycreatedAt,方便後續追蹤。
  3. 多管道整合:除 Email 外,可額外新增 SMS 或 Mobile Push,擴展公告影響力。
  4. 防止濫用:在 API Gateway 設定 Rate Limit,避免誤觸或惡意濫發。
  5. 標準化模板:使用 SES Template 建立公告格式,確保不同公告具有一致的視覺風格。

五、Lab流程

1️⃣ 前置作業

  1. 在 SES 中驗證寄件網域與信箱(若尚未完成,參考 Day 10)。

2️⃣ 主要配置

1. 創建SNS Topic

  1. 進入「SNS」頁面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743xJLtz2D5xK.png

  2. 點選「建立主題 (Create topic)」。
    https://ithelp.ithome.com.tw/upload/images/20251007/2017274345LRn4Vi4L.png

  3. 選擇「標準」類型,並創建主題。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727437PJ2i3chcX.png
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743O4XKSFvyCP.png

  4. 建立完成。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743FmXlRwIyAu.png

2. SNS E-mail 訂閱與確認

  1. 選擇剛建立的 SNS 主題(Topic)。

  2. 點擊 「建立訂閱 (Create subscription)」
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743jCDgKOlzfz.png

  3. 新增訂閱者的電子信箱。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743XGvpdPmoDs.png

  4. 創建完成。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727437oFE2161LE.png

  5. 確認:該收件信箱將收到一封確認信,請務必點擊信中的連結來啟用訂閱。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743Igx1KH1JOo.png

  6. 點擊確認連結後的畫面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743nhtY9MoRbi.png

  7. 完成驗證。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743NGagQ7llXK.png

3. 創建Lambda函數 - API觸發

  1. 進入「Lambda」頁面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743gF0fBhft5X.png

  2. 創建一個新的函數。
    https://ithelp.ithome.com.tw/upload/images/20251007/2017274385HDkWkqDg.png

  3. 輸入函數名稱,並選擇編撰語言。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743SZ1wsnl9Md.png

  4. 跳過建議畫面。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727430Rv9ru0Mq4.png

  5. 寫入程式碼,並部署。

    • 程式碼範例

      // index.mjs (使用 Node.js SDK v3)
      import { SNSClient, PublishCommand } from "@aws-sdk/client-sns";
      
      const snsClient = new SNSClient({ region: process.env.AWS_REGION });
      const TOPIC_ARN = process.env.SNS_TOPIC_ARN;
      
      if (!TOPIC_ARN) {
          // 確保環境變數已設定
          throw new Error('Environment variable SNS_TOPIC_ARN must be set.');
      }
      
      export const handler = async (event) => {
          try {
              // 1. 解析 API Gateway 傳入的 JSON Body
              const requestBody = JSON.parse(event.body); 
      
              // 2. 構造訊息 Payload
              const messagePayload = {
                  subject: requestBody.subject || '系統通知',
                  content: requestBody.message || '沒有內容的公告。',
                  // 嘗試從 API Gateway Context 獲取管理員資訊
                  createdBy: event.requestContext?.authorizer?.claims?.['cognito:username'] || 'Admin Portal', 
                  createdAt: new Date().toISOString()
              };
      
              // 3. 構造 SNS Publish 參數
              const params = {
                  TopicArn: TOPIC_ARN,
                  Subject: `系統公告: ${messagePayload.subject}`,
                  MessageStructure: "json",
                  Message: JSON.stringify({
                      default: messagePayload.content,   // 給 SMS、SQS 等用
                      email: messagePayload.content,     // Email 訂閱者只收到這段
                      sms: messagePayload.content        // 簡訊可用
                  }),
              };
      
              // 4. 發布訊息
              const command = new PublishCommand(params);
              await snsClient.send(command);
      
              console.log("✅ Announcement successfully published to SNS.");
      
              return {
                  statusCode: 200,
                  body: JSON.stringify({ status: "success", message: "Announcement published" }),
              };
          } catch (error) {
              console.error("❌ Failed to process or publish announcement:", error);
              return {
                  statusCode: 500,
                  body: JSON.stringify({ status: "error", message: error.message || "Internal server error." }),
              };
          }
      };
      

    https://ithelp.ithome.com.tw/upload/images/20251007/20172743CubeUw9z0A.png

  6. 進入「組態」分頁,設定環境變數。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743zTGlXYejjv.png

  7. 變數key「SNS_TOPIC_ARN」,Value為你在 SNS 的ARN。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743hnIVte0kdZ.png

    • SNS的ARN在哪?
      https://ithelp.ithome.com.tw/upload/images/20251007/201727436fjOX6uGEs.png

4. 設定Lambda的IAM user權限

  1. 進入「IAM 」頁面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743ox4l4uMKHn.png

  2. 進入IAM role的頁面,點選該Lambda自動創建的IAM role。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743WssYK2549u.png

  3. 新增「許可政策」。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727437fjVarcRV5.png

  4. 增加SNS的「Publish」權限。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743187rPgLS3F.png

  5. 授權範圍為指定的SNS。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743vmiQUc4rDk.png

    • SNS的ARN在哪裡?
      https://ithelp.ithome.com.tw/upload/images/20251007/20172743M266cwzNpU.png
  6. 點選下一步。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727436J8fdBjlOS.png

  7. 設定「許可政策」名稱。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743lKpXAr24dy.png

  8. 完成畫面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743jX77mIMNEv.png

5. 設定API Gateway 的路徑授權方

  1. 進入「API Gateway」頁面。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727437rWjLJFVRO.png

  2. 進入之前創建的HTTP API Gateway中。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743sAMjkQIIEc.png

  3. 建立新的路由路徑。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727434qgVGs6W0c.png

  4. 設定動作為「POST」,路徑可以自定義。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743UTqvZOWlUd.png

6. 創建API Gateway,配置路徑觸發Lambda

  1. 設定整合的配置。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743NDRgOcgKFo.png

  2. 選擇剛剛創建的Lambda函數。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743OPVtK7nf5G.png

  3. 完成畫面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743t6zuzKoDZt.png

3️⃣ 測試驗證

1. 使用Terminal取得 Access Token

  1. 透過終端機(terminal)向Cognito取得 session

    • 第一段:

      aws cognito-idp initiate-auth \
        --auth-flow USER_PASSWORD_AUTH \
        --client-id <Cognito_APP_CLIENT_ID> \
        --auth-parameters USERNAME=<username>,PASSWORD=<password>
      

    https://ithelp.ithome.com.tw/upload/images/20251007/20172743TC8IiZxzWG.png

  2. 透過終端機(terminal),用 session向Cognito拿到對應的 IdTokenAccessToken

    • 第二段

      aws cognito-idp respond-to-auth-challenge \
        --client-id <Cognito_APP_CLIENT_ID> \
        --challenge-name SOFTWARE_TOKEN_MFA \
        --session <上一步回傳的Session> \
        --challenge-responses USERNAME=<username>,SOFTWARE_TOKEN_MFA_CODE=<六位數驗證碼>
      

    https://ithelp.ithome.com.tw/upload/images/20251007/201727434KqGINKMaO.png

2. 使用Terminal發布測試公告

  • 範例程式碼

    # 替換 YOUR_API_ENDPOINT 和 YOUR_TOKEN
    API_URL="https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.com/prod/announce"
    ADMIN_TOKEN="YOUR_ACTUAL_JWT_TOKEN"
    
    curl -X POST \
      -H "Authorization: Bearer $ADMIN_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"subject": "系統緊急維護", "message": "服務將於 15 分鐘後停機,請提前儲存您的資料。"}' \
      "$API_URL"
    

https://ithelp.ithome.com.tw/upload/images/20251007/20172743LhlqK7Ga09.png
https://ithelp.ithome.com.tw/upload/images/20251007/20172743uTC0iZrvUF.png

3. 檢查結果

  1. API 回應:您應收到 HTTP 200 和 JSON Body:{"status": "success", "message": "Announcement published"}
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743PeSXZYLTqF.png

  2. Lambda Logs:檢查 CloudWatch Logs for PublishAnnouncement 函數,確認 Log 顯示 ✅ Announcement successfully published to SNS.

    (1) 進到Lambda對應的CloudWatch頁面。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743JmIw1Gz6HC.png

    (2) 進到最近一筆的Log紀錄內。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743I6ls4AsAtp.png

    (3) 有看到已經成功寄出。
    https://ithelp.ithome.com.tw/upload/images/20251007/20172743EwfrLfNwUH.png

  3. Email 驗證:檢查信件,主旨為:系統公告: 系統緊急維護,內容為一個 JSON 格式的訊息。
    https://ithelp.ithome.com.tw/upload/images/20251007/201727433cEbDHLr3H.png

六、結語

今天的 Lab 建立了一個 集中化的管理員公告系統,讓管理員能透過 Portal 發布公告,並由 SNS 自動分發至 Email(SES)或其他管道。這樣的架構解決了公告散亂、無法追蹤的問題,同時具備可擴充性,未來可支援多種通知通道,形成一個可靠的 系統訊息廣播中樞


上一篇
Day 22 活動通知自動化:SES 打造客製化 Email 通知
下一篇
Day 24 多管道通知整合:SNS x Lambda 打造訊息廣播系統(LINE)
系列文
從一個網站的誕生,看懂 AWS 架構與自動化的全流程!24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言