2026年7月19日 星期日

博客來買書一本 : Gemini 投資理財全攻略

今天在博客來看到這本旗標的書 66 折, 這種好康當然不錯過 :





OpenPoint 折 55 點, 實付 340 元 (約 56 折), 至興店取貨. 

2026 年第 27 周記事

過去一周主要在忙著把我之前寫的 serverless 專案用 agy 重構 (選用 Claude 模型), Claude 的 Coding 能力真的沒話說, 三兩下就搞定了. 周末這兩天我把新版程式佈署到 Mapleboard 時遇到站台設定衝突的干擾, 我已一年多未接觸 Mapleboard 網站設定, 印象有點模糊了, 好在透過 Gemini 協助全面修正設定後已解決, serverless 平台重構算是告一段落了. 這如果是像剛開始創建時手刻的話, 恐怕要花上個把月, 現在沒 AI 真是沒法過日子了. 

週五下午請假載爸去署立醫院回診, 先去小太陽掛號拿降血壓藥, 半路上 QRV 居然熄火, 最近有兩三次怠速熄火紀錄, 只要重啟引擎即可, 但卻回卻出現引擎異常標誌, 所幸休息一下還能發動. 從署醫拿藥出來順路開到附近的 Nissan 保養廠, 初步檢修是曲軸位置與角度感知器異常造成, 因已四點多需留廠, 想說乾脆就順便做 15.5 萬公里保養, 原本也是預約周六要去大中廠保養的. 廠長先載我跟爸回家. 週六保養的技師來電, 表示檢查發現剎車分泵漏油須換, 四條輪胎胎紋已淺, 且雨刷不良建議更換, 這一次維修總共花了 22000, 真是大手筆. 廠長週六下午把車開回來, 說以後就近在他們廠保養就好, 不用去大中廠,我也覺得這樣較方便. 但若八月買 RAV4 的話, QRV 變成備用車, 保養週期會變長, 我可能會改到八百屋去做例行保養.  

本周向福利會寄出諮詢單後, 國都的業務已打電話報價 124 萬, 似乎是可以接受的價格, 下周聯繫時再談. 週四去高都賞車時業務提到希望 8/15 前能決定, 因為目前有銷售競賽. 如果高都下周打電話來, 就用這價格跟她往下談, 可以的話在高都買交車較方便.  

下周五要帶阿中去岡山了, 所以這幾天要複習一下行程並整理行李. 

Google Antigravity 學習筆記 : 重構 serverless 函式執行平台 (五)

經過前面的測試已驗證了重構後的程式碼基本上達成了讓平台具備 API Key 功能的目標, 在提交到 GitHub 之前, 想要先佈署到 Mapleboard 上更新 serverless 到具有 API Key 功能的最新版. 關於 Mapleboard 筆記參考 :


本系列全部文章索引參考 :



7. 在 Mapleboard 佈署新版 serverless : 

先用 VNC Cloud 遠端連線到 Mapleboard 桌面, 開啟終端機, 切換到 serverless 專案目錄下, 用 zip 指令把整個專案除指定之敏感檔案外全部壓縮成 zip 檔 : 

tony1966@LX2438:~/flask_apps/serverless$ zip -r serverless_v4.zip . -x "serverless.db" "serverless_error.log" "*.pyc" "__pycache__/*" ".env"

參數 -r 表示要遞迴壓縮, 即連同所有子資料夾 (如 functions/) 一起打包. serverless_backup.zip 是壓縮結果的檔檔名. 後面的 . . 代表壓縮當前目錄下的所有東西, -x 參數後面接的是排除 (不壓縮) 名單, 這裡排除了本地資料庫 (serverless.db), 日誌檔 (serverless_error.log), Python 快取檔, 以及含有敏感密碼的 .env 檔. 

然後用 WinSCP 連線 Mapleboard, 注意, 之前安裝 fail2ban 時為了提升資安, 已修改 SSH 埠 (不再是預設的 22), 埠號可用下列指令查得 :

tony1966@LX2438:~/flask_apps/serverless$ sudo nano /etc/fail2ban/jail.local  

設定 WinSCP 連線時除了要輸入固定 IP 外, 還要更改 SSH 埠號, 如果用 22 埠是無法連線的. 連線成功後, 先將上面備份的 zip 檔傳送至本地保存. 完成後將本地的 serverless.py 主程式與 functions 資料夾上傳到 Mapleboard 的 serverless 專案目錄下覆蓋舊版程式檔. 

由於主程式 serverless.py 有更改, 所以須用下例指令重啟服務才會運行新版程式 :

tony1966@LX2438:~/flask_apps/serverless$ sudo systemctl restart serverless

用瀏覽器測試 hello.py 函式 : 





在有登入情況下, 對函式的請求無需攜帶 API Key 即可順利執行;  如果沒有登入就會收到 401 錯誤, 例如 :



但修改前一篇的 deploy.py 的 URL 想要進行本地佈署 hello.py 卻出現連線異常與 404 等錯誤, 經查原來我的 Mapleboard 之前在架站時 Nginx 與站台 (主要是 hello 站台與 flask.tony1966.cc) 設定出現埠的衝突, 重啟 Nginx 出現 2~3 個 warning. 經過與 Gemini 討論後, 修改站台設定後終於解決此問題, Nginx 設定檔就徹底理順了, 摘要如下 : 
  • reject_ip 盡職地守在大門口擋掉所有奇奇怪怪的 IP 掃描.
  • flask.tony1966.cc 安全地走 HTTPS 連線, 讓新版 Serverless 平台, 樹莓派爬蟲與電腦部署都能順暢通訊. 
  • hello 站台也成功轉型, 乾乾淨淨地留在 443 埠當作你的 is_alive 測試通道 (Port 8080/8081).
站台清單檢視指令 : ls -l /etc/nginx/sites-enabled/
重啟 Nginx 伺服器指令 : sudo nginx -t
重新載入 Nginx 指令  : sudo systemctl reload nginx

佈署程式 deploy.py 的 URL 要改成 https://flask.tony1966.cc, 下面是佈署市圖爬蟲伺服端程式 update_ksml_books.py 的範例 (伺服端程式都不必為 serverless 升版做任何改變) : 

SERVER_URL="https://flask.tony1966.cc"
API_TOKEN="your-api-key"

# 2. 定義要佈署的函式名稱與本地檔案路徑
TARGET_FUNCTION_NAME="update_ksml_books"
LOCAL_FILE_PATH="update_ksml_books.py"

再次執行 deploy.py 就順利將函式發佈到 serverless 平台上了 :

D:\python\test>python deploy.py  
🚀 正在將 update_ksml_books.py 更新至 https://flask.tony1966.cc...
✅ 函式更新成功!
伺服器回應: {'func_name': 'update_ksml_books', 'message': '模組 update_ksml_books 已成功更新'}

deploy.py 的完整內容如下 :

# deploy.py
import os
import requests

# 1. 設定伺服器資訊與 API Token
# 地端測試可用 http://127.0.0.1:5000,上雲端後改成你的 Render 網址
SERVER_URL="https://flask.tony1966.cc"
API_TOKEN="your-api-key"

# 2. 定義要佈署的函式名稱與本地檔案路徑
TARGET_FUNCTION_NAME="update_ksml_books"
LOCAL_FILE_PATH="update_ksml_books.py"

def function_exists(func_name):
    """檢查遠端伺服器上的函式是否已存在(透過 GET 探測)"""
    url=f"{SERVER_URL}/function/{func_name}"
    headers={"X-API-Key": API_TOKEN}
    try:
        response=requests.get(url, headers=headers)
        # 404 表示不存在,其他狀態(200/400/500)表示檔案存在
        return response.status_code != 404
    except requests.exceptions.RequestException:
        return False  # 連線失敗時保守假設不存在

def deploy_function():
    # 檢查本地檔案是否存在
    if not os.path.exists(LOCAL_FILE_PATH):
        print(f"❌ 找不到本地檔案: {LOCAL_FILE_PATH}")
        return
    # 讀取本地最新的程式碼內容
    with open(LOCAL_FILE_PATH, "r", encoding="utf-8") as f:
        new_code=f.read()

    headers={
        "X-API-Key": API_TOKEN,
        "Content-Type": "application/json"
    }
    payload={
        "func_name": TARGET_FUNCTION_NAME,
        "code": new_code
    }

    # 3. 自動判斷:函式已存在 → update,不存在 → save(新增)
    if function_exists(TARGET_FUNCTION_NAME):
        url=f"{SERVER_URL}/function/update_function"
        action="更新"
    else:
        url=f"{SERVER_URL}/function/save_function"
        action="新增"

    print(f"🚀 正在將 {LOCAL_FILE_PATH} {action}至 {SERVER_URL}...")
    try:
        response=requests.post(url, json=payload, headers=headers)
        # 新增成功是 201,更新成功是 200
        if response.status_code in (200, 201):
            print(f"✅ 函式{action}成功!")
            print("伺服器回應:", response.json())
        else:
            print(f"❌ {action}失敗 (狀態碼: {response.status_code})")
            try:
                print("錯誤原因:", response.json())
            except ValueError:
                print("非 JSON 回應內容:", response.text)
    except requests.exceptions.RequestException as e:
        print(f"💥 連線發生異常: {e}")

if __name__ == "__main__":
    deploy_function()

市圖爬蟲程式本機版則需要配合 serverless 添加 API Key 功能而升版, 否則請求都會被 401 拒絕, 這部分記在另一篇. 

2026年7月18日 星期六

Google Antigravity 學習筆記 : 重構 serverless 函式執行平台 (四)

前面的測試已驗證了重構後的程式碼基本上達成了讓平台具備 API Key 功能的目標, 本篇要來測試本地自動化佈署功能, 在此過程中發現了原系統的一些缺陷, 都利用 agy 請 Claude Sonnet 修正, 除了主程式 serverless.py 外, 還包括 functions 下的 update_function.py 與 save_function.py. 

本系列全部文章索引參考 :



6. 本地自動化佈署 : 

第一版的 serverless 系統在新增或修改函式時都需登入平台, 透過手動操作網頁來完成, 在本次重構加上 API Key 機制後, 系統安全性增強, 可以在本地撰寫一個自動化佈署程式, 攜帶 API Key 經過驗證完成函式的佈署. 只要寫一個 Python 函式讀取要佈署的函式檔, 然後攜帶 API key 呼叫 update_function 或 save_function 即可完成函式的佈署. 


(1). 與 Claude 討論 :

> 我執行下列佈署程式 :
  # deploy.py
  import os
  import requests

  # 1. 設定伺服器資訊與 API Token
  # 地端測試可用 http://127.0.0.1:5000,上雲端後改成你的 Render 網址
  SERVER_URL="http://127.0.0.1:5000"
  API_TOKEN="new-test-token"

  # 2. 定義要更新的函式名稱與本地檔案路徑
  TARGET_FUNCTION_NAME="hello"
  LOCAL_FILE_PATH="hello.py"

  def deploy_function():
      # 檢查本地檔案是否存在
      if not os.path.exists(LOCAL_FILE_PATH):
          print(f"❌ 找不到本地檔案: {LOCAL_FILE_PATH}")
          return
      # 讀取本地最新的程式碼內容
      with open(LOCAL_FILE_PATH, "r", encoding="utf-8") as f:
          new_code=f.read()
      # 3. 準備發送給 update_function 的端點與資料
      # 路由設計是動態載入的 /function/update_function
      url=f"{SERVER_URL}/function/update_function"
      headers={
          "X-API-Key": API_TOKEN,
          "Content-Type": "application/json"
          }
      payload={
          "func_name": TARGET_FUNCTION_NAME,
          "code": new_code
          }

      print(f"🚀 正在將 {LOCAL_FILE_PATH} 部署至 {SERVER_URL}...")
      try:
          # 發送 POST 請求更新遠端函式
          response=requests.post(url, json=payload, headers=headers)
          # 檢查伺服器回應狀態
          if response.status_code == 200:
              print("✅ 函式更新成功!")
              print("伺服器回應:", response.json())
          else:
              print(f"❌ 更新失敗 (狀態碼: {response.status_code})")
              try:
                  print("錯誤原因:", response.json())
              except ValueError:
                  print("非 JSON 回應內容:", response.text)
      except requests.exceptions.RequestException as e:
          print(f"💥 連線發生異常: {e}")

      deploy_function()
  ===
  出現如下錯誤 :
  D:\python\test>python deploy.py
  🚀 正在將 hello.py 部署至 http://127.0.0.1:5000...
  ❌ 更新失敗 (狀態碼: 401)
  錯誤原因: {'error': 'Authentication required', 'login_url': '/login'}
  ===
  在提供有效 API Key 情況下應該要能通過不受 login 阻攔, 請修改

▸ Thought for 29s
  The user is experiencing a 401 error with the message "Authentication required" ...
  問題很清楚。看一下目前的驗證順序:

● Read(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  根本原因找到了:

  步驟 1(L165)先攔截, update_function  在  PROTECTED_FUNCTIONS  裡,API Client 沒有 Session →
  直接 401,根本輪不到步驟 2 的 Token 驗證。

  解法:將兩層驗證合併,一次判斷 Session 或 Token 擇一通過,同時可以移除  TOKEN_PROTECTED_FUNCTIONS
  (已不需要):

● Read(D:/antigravity_cli/projects/serverless/serverless.py)
  if __name__ == "__main__":
● Edit(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  完成。來驗證最終結果:

● Read(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  還有一個小問題:L184 的步驟編號  # 3.  與 L182 的  # 3.  重複了,順手修正:
● Edit(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  完成。以下是這次修改的完整說明:
  ──────
  ## 問題根源

    原本流程(有 Bug):
      步驟 1:update_function 在 PROTECTED_FUNCTIONS? → 是
               check_auth() = False(API Client 無 Session)
               → 直接 401 ❌ (Token 驗證根本輪不到)

  ## 修正後的流程
    新流程(handle_function 前三步):

    步驟 1:一次性完成雙重驗證
            is_session_auth = check_auth()
            if 無 Session → 嘗試讀 X-API-Key → 查資料庫 → is_token_auth
            is_authorized = is_session_auth OR is_token_auth

    步驟 2:管理模組 → 需要 is_authorized,否則 401
    步驟 3:一般函式 → 需要 is_authorized,否則 401

  ## 各情境結果

   呼叫者                      | Session              | API Key              | 結果
  -----------------------------|----------------------|----------------------|----------------------
   瀏覽器管理者(update/save) | ✅                   | ❌                   | ✅ 放行
    deploy.py  API Client      | ❌                   | ✅ 有效              | ✅ 放行
   未認證請求                  | ❌                   | ❌                   | ❌ 401
   無效 Token                  | ❌                   | ❌ 無效              | ❌ 401
────────────────────────────────────────────────────────────
> 成功了, 但出現連線異常:
  D:\python\test>python deploy.py
  🚀 正在將 hello.py 部署至 http://127.0.0.1:5000...
  ✅ 函式更新成功!
  💥 連線發生異常: Expecting value: line 1 column 1 (char 0)
  這要修改 deploy.py 還是系統的 update_function.py 較好?
▸ Thought for 27s
  The user is seeing a successful update (✅) but then a connection error. Let me...
  先看清楚錯誤發生在哪一行:

   deploy.py  收到 200 → 印出 ✅ → 再執行  response.json()  時爆炸,代表  update_function.py
  回傳的是 HTML 而非 JSON。

  讓我確認  update_function.py  的完整內容:

● Read(D:/antigravity_cli/projects/serverless/functions/update_function.py) (ctrl+o to expand)

  問題找到了,而且有兩個 bug 同時存在:

   # | 問題         | 說明
  ---|--------------|-------------------------------------------------------------------------------
   1 | 欄位名稱不符 |  deploy.py  送  func_name ,但  update_function.py  讀  module_name  →
     |              | 讀到空字串
   2 | 輸入格式不符 |  deploy.py  送 JSON body,但  update_function.py  只讀  request.form  →
     |              | 永遠讀不到資料
   3 | 回應格式不符 | 成功時回傳 HTML, deploy.py  呼叫  response.json()  爆炸
  ──────
  ## 修哪個?

  修  update_function.py ,理由:

  • 這是 server 端的問題(不認識 JSON 輸入、不回傳 JSON)
  • 瀏覽器用表單呼叫、API Client 用 JSON 呼叫,server 應該兩者都支援
  •  deploy.py  的設計是正確的,不需要改
● Edit(D:/antigravity_cli/projects/serverless/functions/update_function.py)
● Create(D:/antigravity_cli/projects/serverless/functions/update_function.py) (ctrl+o to expand)

  完成。以下是修改摘要:
  ──────
  ## 修改的檔案

  ### update_function.py

  修正了三個問題:

   # | 問題                                 | 修正方式
  ---|--------------------------------------|-------------------------------------------------------
   1 | 只讀  request.form ,不懂 JSON       | 用  request.is_json  判斷,JSON 時改讀
     |                                      | request.get_json()
   2 | 只認  module_name ,不認  func_name  | JSON 輸入時同時嘗試讀取  func_name  與  module_name
   3 | 成功時只回傳 HTML                    | JSON 請求時回傳  {"message": "...", "func_name":
     |                                      | "..."}
  ──────
  ## 預期執行結果
    D:\python\test>python deploy.py
    🚀 正在將 hello.py 部署至 http://127.0.0.1:5000...
    ✅ 函式更新成功!
    伺服器回應: {'func_name': 'hello', 'message': '模組 hello 已成功更新'}


────────────────────────────────────────────────────────────
> update_function 是更新已有的函式, 如果要新增函式則是使用 add_function 與 save_function,
  如果要佈署新函式, deploy.py 要怎麼改? 同時請分析 add_function.py 與 save_function.py 在執行 deploy.p
  佈署方面是否有需要配合修改之處?
▸ Thought for 1s
  The user wants to understand the deployment flow for new functions. Let me first...
  先讀取三個相關檔案:

● Read(D:/antigravity_cli/projects/serverless/functions/add_function.py)
● Read(D:/antigravity_cli/projects/serverless/functions/save_function.py) (ctrl+o to expand)
  分析完畢。以下是完整報告:
  ──────
  ## 架構釐清

    add_function  → 純 UI 頁面,回傳 HTML 表單,action 指向 save_function
    save_function → 實際寫檔(新增),只接受 form POST
    update_function → 實際寫檔(覆蓋),已修改支援 JSON

  deploy.py 要新增函式,只需呼叫  save_function ,不需碰  add_function (那是給瀏覽器用的 UI)。
  ──────
  ## save_function.py 的問題(與 update_function 一樣)

   #                           | 問題                                 | 影響
  -----------------------------|--------------------------------------|-----------------------------
   1                           | 只讀  request.form ,不懂 JSON       | API Client 送來的資料讀不到
   2                           | 只認  module_name ,不認  func_name  | deploy.py 欄位名稱不符
   3                           | 成功/失敗都回傳 HTML                 |  response.json()  會爆炸
  ──────
  ## 修改計劃

  修改  save_function.py :與  update_function.py  相同邏輯,支援 JSON/form 雙輸入。

  修改  deploy.py :加入「新增 or 更新」判斷邏輯,自動選擇呼叫  save_function  或  update_function
  。

  開始修改:
● Create(D:/antigravity_cli/projects/serverless/functions/save_function.py) (ctrl+o to expand)

  現在修改  deploy.py ,加入自動判斷新增或更新:
● Create(D:/python/test/deploy.py) (ctrl+o to expand)

  完成。以下是完整摘要:
  ──────
  ## 修改了哪些檔案

   檔案                           | 修改內容
  --------------------------------|-----------------------------------------------------------------
   save_function.py               | 支援 JSON/form 雙輸入、相容  func_name / module_name 、回傳
                                  | JSON(201)或 HTML
   deploy.py                      | 新增  function_exists()  探測,自動選  save_function  或
                                  | update_function
    functions/update_function.py  | 上一輪已修改,本次不動
    add_function.py               | 不需修改,它只是瀏覽器 UI 頁面
  ──────
  ## deploy.py 新流程

    執行 deploy.py
        ↓
    function_exists():GET /function/{func_name}
        ├─ 404 → 函式不存在 → POST /function/save_function  → 回 201 ✅
        └─ 其他 → 函式已存在 → POST /function/update_function → 回 200 ✅
    ──────
  ## 預期執行結果

    # 第一次佈署(函式不存在)
    🚀 正在將 hello.py 新增至 http://127.0.0.1:5000...
    ✅ 函式新增成功!
    伺服器回應: {'func_name': 'hello', 'message': '模組 hello 已成功建立'}

    # 第二次佈署(函式已存在)
    🚀 正在將 hello.py 更新至 http://127.0.0.1:5000...
    ✅ 函式更新成功!
    伺服器回應: {'func_name': 'hello', 'message': '模組 hello 已成功更新'}


(2). 本地佈署測試 :

上面程序中 Claude 已經修改 serverless.py, update_function.py, 以及 save_function.py, 且修正了原先使用的佈署程式 deploy.py. 接下來要實測看看是否可在本機用佈署程式 deploy.py 將 hello.py 發布到後端平台上. 

先複習一下 serverless 平台的函式檔基本架構 :

# func_module.py
def main(request, **kwargs):
    config=kwargs.get('config', {})  # 預設為空 dict
    result='do something'
    return result

注意, main() 的參數一定要傳入 request 與關鍵字參數 **kwargs : 
  • request :
    用來接收 HTTP 請求的相關資訊 (例如前端傳進來的 Payload, Query Parameters, Headers等).
  • **kwargs 與 config :
    這是一個極具擴充性的設計, 透過 kwargs.get('config', {}) 可以在伺服器端 (Flask 主程式) 執行該函式時動態注入系統級的配置或環境變數 (例如資料庫連線字串, 加密金鑰等), 而不需要把這些敏感資訊寫死在 hello.py 裡面. 
為了展示此架構中 main() 的兩個參數用途, 先開啟 .env 檔, 添加一個環境變數 DB_TIMEOUT :

DB_TIMEOUT=30

然後按照上面的函式架構寫了一個新的 hello.py 函式檔 (其實只是加上讀取環境變數 DB_TIMEOUT 而已) :

# hello.py
def main(request, **kwargs):
    # 讀取系統注入的設定(如果有的話)
    config=kwargs.get('config', {})    
    # 這裡可以安全地取得環境變數
    db_timeout=config.get('DB_TIMEOUT', 30)
    # 先從請求字串擷取 name 參數 (?name=)
    name=request.args.get('name')
    if not name:   # 請求字串沒有 name 參數
        # 嘗試從 RESTful 子路徑 (例如 /function/hello/Tony) 中擷取 name 參數 
        subpath=request.view_args.get('subpath', '')   # 取得 Flask 傳入的 subpath 參數
        parts=subpath.strip('/').split('/')   # 拆解成串列例如 ['Tony']
        if parts:
            name=parts[0]   # 取第一段作為 name (例如 'Tony')
    # 從 request 也沒有找到
    if not name:
        name='World'    
    result=f"Hello {name}! Database timeout is set to {db_timeout}s."
    return result

然後用下列 Claude 修正過的 deploy.py 程式來佈署此新的 hello.py (注意 API_TOKEN 必須填入有效的 API Key) :

# deploy.py
import os
import requests

# 1. 設定伺服器資訊與 API Token
# 地端測試可用 http://127.0.0.1:5000,上雲端後改成你的 Render 網址
SERVER_URL="http://127.0.0.1:5000"
API_TOKEN="new-test-token"

# 2. 定義要佈署的函式名稱與本地檔案路徑
TARGET_FUNCTION_NAME="hello"
LOCAL_FILE_PATH="hello.py"

def function_exists(func_name):
    """檢查遠端伺服器上的函式是否已存在(透過 GET 探測)"""
    url=f"{SERVER_URL}/function/{func_name}"
    headers={"X-API-Key": API_TOKEN}
    try:
        response=requests.get(url, headers=headers)
        # 404 表示不存在,其他狀態(200/400/500)表示檔案存在
        return response.status_code != 404
    except requests.exceptions.RequestException:
        return False  # 連線失敗時保守假設不存在

def deploy_function():
    # 檢查本地檔案是否存在
    if not os.path.exists(LOCAL_FILE_PATH):
        print(f"❌ 找不到本地檔案: {LOCAL_FILE_PATH}")
        return
    # 讀取本地最新的程式碼內容
    with open(LOCAL_FILE_PATH, "r", encoding="utf-8") as f:
        new_code=f.read()

    headers={
        "X-API-Key": API_TOKEN,
        "Content-Type": "application/json"
    }
    payload={
        "func_name": TARGET_FUNCTION_NAME,
        "code": new_code
    }

    # 3. 自動判斷:函式已存在 → update,不存在 → save(新增)
    if function_exists(TARGET_FUNCTION_NAME):
        url=f"{SERVER_URL}/function/update_function"
        action="更新"
    else:
        url=f"{SERVER_URL}/function/save_function"
        action="新增"

    print(f"🚀 正在將 {LOCAL_FILE_PATH} {action}至 {SERVER_URL}...")
    try:
        response=requests.post(url, json=payload, headers=headers)
        # 新增成功是 201,更新成功是 200
        if response.status_code in (200, 201):
            print(f"✅ 函式{action}成功!")
            print("伺服器回應:", response.json())
        else:
            print(f"❌ {action}失敗 (狀態碼: {response.status_code})")
            try:
                print("錯誤原因:", response.json())
            except ValueError:
                print("非 JSON 回應內容:", response.text)
    except requests.exceptions.RequestException as e:
        print(f"💥 連線發生異常: {e}")

if __name__ == "__main__":
    deploy_function()

可見 Claude 修改後的 deploy.py 是透過讀取 functions 資料夾下有無該函式來判斷是要新增 (無) 還是更新 (有), 目前 functions 下有 hello.py, 所以執行 

執行結果 : 

D:\python\test>python deploy.py   
🚀 正在將 hello.py 部署至 http://127.0.0.1:5000...
✅ 函式更新成功!
伺服器回應: {'func_name': 'hello', 'message': '模組 hello 已成功更新'}

接下來驗證此新佈署的 hello.py 函式功能是否正確 :






可見不同的參數傳遞方式功能都正確, 且 .env 中的環境變數 DB_TIMEOUT 也正確讀到了. 接下來測試 save_function 是否能順利佈署新函式, 先把 functions 底下的 hello.py 刪除, 然後再執行一次 deploy.py : 

D:\python\test>python deploy.py   
🚀 正在將 hello.py 新增至 http://127.0.0.1:5000...
✅ 函式新增成功!
伺服器回應: {'func_name': 'hello', 'message': '模組 hello 已成功建立'}

檢視 functions 下果然 hello.py 已被建立, 再次執行網頁測試結果相同. 

Google Antigravity 學習筆記 : 重構 serverless 函式執行平台 (三)

在前一篇測試中利用 Antigravity CLI (agy) 完成 serverless 函式執行平台的重構, 為其加上 API Key 認證機制, 所有請求必須在標頭中攜帶有效的 X-API-Key 屬性值, 否則請求將被拒絕, 使用 curl.exe 驗證新版 serverless 功能確實符合要求, 本篇則要改用 requests 套件來測試, 這是在實際應用中最常見用法. 

本系列全部文章索引參考 :



5. 使用 requests 提出請求 : 

使用 requests 套件發送請求時, 要將 API Key 放進 headers 參數中的 X-API-Key 欄位傳送給伺服器, 請求的 HTTP 方法可以使用 GET 或 POST, 分別呼叫 requests.get() 與 requests.post() 函式. 

注意, 在前一篇測試中我們已將 API Key 更新如下 :




以下測試將使用  new-test-token 這個 API Key.


(1). 使用 GET 方法 : 

GET 方法不帶 Body, 只需要單純將 API Key 放入 headers 中的 X-API-Key 欄位, 然後在呼叫 requests.get() 時傳入 headers 參數即可, 例如 : 

# serverless_api_key_get_ok.py
import requests

url="http://localhost:5000/function/hello"
headers={"X-API-Key": "new-test-token"}
response=requests.get(url, headers=headers)
if response.status_code == 200:
    print("回應:", response.text)
else:
    print(f"錯誤 {response.status_code}:", response.text)

此處因為 hello.py 傳回值是純文字, 所以用 resposne.text 而非 response.json() 取得回應值, 執行結果如下, 因為傳送了有效的 API Key, 所以伺服器回應 200 OK :

>>> %Run serverless_api_key_get_ok.py   
回應: Hello World!

如果沒有傳送有效的 API Key, 請求會被拒絕而得到 401 回應, 例如 :

# serverless_api_key_get_ng.py
import requests

url="http://localhost:5000/function/hello"
response=requests.get(url)
if response.status_code == 200:
    print("回應:", response.text)
else:
    print(f"錯誤 {response.status_code}:", response.text)

執行結果如下 :

>>> %Run serverless_api_key_get_ng.py  
錯誤 401: {
  "error": "Missing API token",
  "hint": "Provide X-API-Key header"
}


(2). 使用 POST 方法 : 

呼叫 requests.post() 時傳入有效的 API Key 就可順利取得 200 OK 回應, 例如 :

# serverless_api_key_post_ok.py
import requests

url="http://localhost:5000/function/hello"
headers={"X-API-Key": "new-test-token"}
response=requests.post(url, headers=headers)
if response.status_code == 200:
    try:
        # 先嘗試用 JSON 解析
        print("回應 (JSON):", response.json())
    except requests.exceptions.JSONDecodeError:
        # 如果不是 JSON 就印出純文字
        print("回應 (純文字):", response.text)
else:
    print(f"錯誤 {response.status_code}:", response.text)

此例使用 try catch 來判斷回應物件型態是 JSON 格式字串還是純文字, 前者呼叫 response.json() 取得回應內容, 後者則用 response.text, 執行結果如下 :

>>> %Run serverless_api_key_post_ok.py  
回應 (純文字): Hello World!

如果呼叫 requests.post() 時沒有傳入有效的 API Key 就會收到 401 錯誤, 例如 :

# serverless_api_key_post_ng.py
import requests

url="http://localhost:5000/function/hello"
response=requests.post(url)
if response.status_code == 200:
    try:
        # 先嘗試用 JSON 解析
        print("回應 (JSON):", response.json())
    except requests.exceptions.JSONDecodeError:
        # 如果不是 JSON 就印出純文字
        print("回應 (純文字):", response.text)
else:
    print(f"錯誤 {response.status_code}:", response.text)

執行結果如下 :

>> %Run serverless_api_key_post_ng.py   
錯誤 401: {
  "error": "Missing API token",
  "hint": "Provide X-API-Key header"
}

以上測試說明重構後的 serverless 平台確實已改為 API Key 版本, 任何請求必須攜帶 API Key 才可能取得 200 OK 回應, 否則請求都會被拒絕, 收到 401 錯誤. 


6. 主程式原始碼異動摘要 : 

經過上面測試, 我們確認了此重購專案已達成了預期目標, 以下做個 Code Review, 將主程式 serverless.py 的異動摘要如下 :

首先是在 init_db() 函式中添加了一個儲存 API Key 的認證資料表 api_tokens :

    # API Token 認證表
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS api_tokens (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            token      TEXT    NOT NULL UNIQUE,
            owner      TEXT    NOT NULL,
            is_active  INTEGER NOT NULL DEFAULT 1,
            created_at TEXT    NOT NULL DEFAULT (datetime('now'))
        )
    """)

其次是添加了一個 TOKEN_PROTECTED_FUNCTIONS 清單 : 

# 需要 API Token 驗證的函式列表(儲存/更新 function 及所有一般函式執行)
TOKEN_PROTECTED_FUNCTIONS=['save_function', 'update_function']

這兩個系統函式 save_function 與 update_function 用來將函式內容寫進檔案中, 屬於最高權限的敏感操作, 必須通過 API Key 驗證. 

其三是新增了一個 require_api_token() 函式來檢查請求標頭中是否有攜帶有效之 API Key, 有就傳回所呼叫的函式, 否則傳回 401 錯誤 : 

def require_api_token(f):  # 裝飾器:驗證請求 Header 中的 X-API-Key
    @wraps(f)
    def decorated(*args, **kwargs):
        token=request.headers.get('X-API-Key')
        if not token:  # Header 遺失
            return jsonify({'error': 'Missing API token', 'hint': 'Provide X-API-Key header'}), 401
        conn=sqlite3.connect(DB_PATH)
        cursor=conn.cursor()
        cursor.execute(
            'SELECT id FROM api_tokens WHERE token=? AND is_active=1',
            (token,)
        )
        row=cursor.fetchone()
        conn.close()
        if not row:  # Token 不存在或已停用
            return jsonify({'error': 'Invalid or inactive API token'}), 401
        return f(*args, **kwargs)
    return decorated

最後是在 handle_function() 函式中添加雙軌互補驗證 (Session 或 Token 擇一通過) 機制 :

    # 2. API Token 驗證:已登入管理者直接放行;否則需提供有效 Token
    #    觸發範圍:TOKEN_PROTECTED_FUNCTIONS(save/update)及所有一般函式
    if func_name in TOKEN_PROTECTED_FUNCTIONS or func_name not in PROTECTED_FUNCTIONS:
        if not check_auth():  # 管理者已登入 → 信任,跳過 Token 驗證
            token=request.headers.get('X-API-Key')
            if not token:
                return jsonify({'error': 'Missing API token', 'hint': 'Provide X-API-Key header'}), 401
            conn=sqlite3.connect(DB_PATH)
            cursor=conn.cursor()
            cursor.execute(
                'SELECT id FROM api_tokens WHERE token=? AND is_active=1',
                (token,)
            )
            row=cursor.fetchone()
            conn.close()
            if not row:
                return jsonify({'error': 'Invalid or inactive API token'}), 401

這段程式碼採用了巢狀條件設計, 提供了兩道過濾網 :
  • 第一層 (判定範圍) :只要呼叫的函式是敏感的系統功能 (save/update_function) 或任何使用者自訂函式 (不在白名單內) 就會觸發此區塊.
  • 第二層 (身份切換) :
    • 情境 A (管理員) :
      瀏覽器帶著 Cookie 請求, check_auth() 回傳 True. 此時 if not check_auth() 判定不成立, 直接跳過整個 Token 檢查與資料庫查詢, 此安排讓管理員登入後線上操作函式更新與存檔不因為沒帶 API Key 而被 401 擋掉. 
    • 情境 B (外部 API 呼叫/排程腳本) :
      本機 Python 腳本或 curl 請求沒有瀏覽器 Session, 呼叫 check_auth() 會回傳 False, 程式進入 if 內部強制執行後續的 X-API-Key 擷取與 SQLite 比對. 
if not check_auth() 是檢查使用者是否有登入, 沒有登入就要接受 API Key 查驗, 檢查 HTTP 請求的 Header 有沒有帶 X-API-Key 欄位, 沒有帶就直接拒絕請求, 回傳 401 Unauthorized 狀態碼. 如果已登入, 則程式會直接跳過整個 if not check_auth(): 內部的所有程式碼, 直接放行去執行該函式. 

注意此處使用了 SQLite 的參數化查詢 token=? 並把變數包在元組 (token,) 裡傳進去, 這樣可以徹底杜絕駭客在 Header 亂寫 SQL 語法, 利用 SQL Injection 來破解驗證. 如果資料庫回傳 None, 表示 API Key 不存在, 或存在但被停用 (is_active 不是 1), 那就回傳 401 錯誤拒絕請求; 反之就是通過了驗證, 准許呼叫此函式, 放行讓後續的業務邏輯繼續執行. 

Google Antigravity 學習筆記 : 重構 serverless 函式執行平台 (二)

在前一篇測試中, 我已將 serverless 第一版原始碼從 GitHub 上複製到本機 Antigravity CLI 的專案目錄下, 並做好前置處理, 包含將原專案打上標籤, 建立新分支, 建立隔離資料夾並移動應用 App, 最後提交 Git 紀錄. 本篇要用 agy 來進行重構, 為 serverless 平台加上 API Token 認證機制. 

本系列全部文章索引參考 :



2. 用 agy 重構專案 : 

切換到專案目錄下後輸入 agy 啟動 Antigravity CLI 介面, 這次仍然使用 Claude Sonnet 模型來完成此重構計劃. 輸入下列提示詞 :

我們正在 feature/api-token-auth 分支上,要為這個 serverless 專案加上 API Token 認證機制。請協助修改 serverless.py,完成以下任務:

1. 初始化資料庫:
   在系統啟動時(或初始化 DB 的地方),自動建立 `api_tokens` 資料表(欄位包含:id, token, owner, is_active, created_at)。

2. 實作驗證裝飾器(Decorator):
   撰寫一個 `@require_api_token` 裝飾器,從請求的 Header 中讀取 `X-API-Key`。
   - 若 Header 遺失或 Token 不存在於資料庫中(且 is_active 必須為 1),回傳 401 Unauthorized。

3. 保護 API 路由:
   將這個裝飾器套用到「儲存/更新 function」以及「執行 function」的 Flask Route 上。

請先分析原始專案的架構, 提出你的重構計劃, 讓我看過後再修改程式碼。




Claude 閱讀原專案後規劃結果如下 :

> 我們正在 feature/api-token-auth 分支上,要為這個 serverless 專案加上 API Token 認證機制。
  請協助修改 serverless.py,完成以下任務:

  1. 初始化資料庫:
     在系統啟動時(或初始化 DB 的地方),自動建立 `api_tokens` 資料表(欄位包含:id, token, owner,
  is_active, created_at)。

  2. 實作驗證裝飾器(Decorator):
     撰寫一個 `@require_api_token` 裝飾器,從請求的 Header 中讀取 `X-API-Key`。
     - 若 Header 遺失或 Token 不存在於資料庫中(且 is_active 必須為 1),回傳 401 Unauthorized。

  3. 保護 API 路由:
     將這個裝飾器套用到「儲存/更新 function」以及「執行 function」的 Flask Route 上。

  請先分析原始專案的架構, 提出你的重構計劃, 讓我看過後再修改程式碼。
▸ Thought for 2s
  Let me first explore the project structure to understand the codebase before mak...

● ListDir(D:/antigravity_cli/projects/serverless)
● Read(D:/antigravity_cli/projects/serverless/serverless.py)
● ListDir(D:/antigravity_cli/projects/serverless/functions)
● Read(D:/antigravity_cli/projects/serverless/requirements.txt)
● Read(D:/antigravity_cli/projects/serverless/functions/save_function.py)
● Read(D:/antigravity_cli/projects/serverless/functions/update_function.py) (ctrl+o to expand)

  已完整閱讀專案架構,以下是我的分析與重構計劃。
  ──────

● Create(C:/Users/tony1/.gemini/antigravity-...-a43e-cfa6fbc204e5/refactor_plan.md) (ctrl+o to expand)

  以下是重構計劃的摘要:

  ## 架構分析結論

  現有系統有兩層認證:

  1. Session Cookie — 管理者透過  /login  密碼登入,保護  PROTECTED_FUNCTIONS  清單
  2. 一般函式執行 — 完全沒有保護,任何人只要知道 URL 就能呼叫
  ──────
  ## 三個關鍵設計決策

  ### 1.  init_db()  修正

  移除  if not os.path.exists(DB_PATH)  判斷,改用  CREATE TABLE IF NOT EXISTS
  ,確保每次啟動都檢查表是否存在,不會因 DB 已建立但缺表而漏掉建立  api_tokens 。

  ### 2.  @require_api_token  裝飾器

  讀取  X-API-Key  Header → 查詢  api_tokens  表( is_active=1 )→ 不符合一律回 401 JSON。

  ### 3. 保護路由的方案

  因為  save_function 、 update_function  與一般函式執行共用同一個  handle_function()
  路由,無法直接套裝飾器。

  採用方案 A:在路由內部邏輯中呼叫驗證輔助函式,改動最小且與現有 Session 機制並存。
  ──────
  計劃文件已整理完畢,請確認後按「Proceed」或告訴我需要調整的地方,即可開始撰寫程式碼。

此計劃看來很周到, 輸入 Proceed 按 Enter 看它表演即可 :

> Proceed

▸ Thought for 1s
  The user wants to proceed with the refactoring plan. Let me implement the change...
  好的,開始實作!

● Read(D:/antigravity_cli/projects/serverless/serverless.py)
● Edit(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  修改成功,來驗證最終結果:

● Read(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  內容正確。最後修正一個步驟編號的小問題(step 2 重複出現):
● Edit(D:/antigravity_cli/projects/serverless/serverless.py) (ctrl+o to expand)

  所有修改完成,以下是完整摘要:
  ──────
  ## ✅ 修改完成摘要
  檔案:serverless.py

  ### 變更一覽

   #             | 位置          | 修改內容
  ---------------|---------------|------------------------------------------------------------------
   1             | L6            | 新增  from functools import wraps
   2             | L16–37        |  init_db()  改為冪等執行,新增  api_tokens  資料表
   3             | L56–73        | 新增  require_api_token(f)  裝飾器(備用,可掛在未來獨立路由上)
   4             | L105–106      | 新增  TOKEN_PROTECTED_FUNCTIONS  常數
   5             | L167–181      |  handle_function()  步驟 2 加入 API Token 驗證邏輯
   6             | L207–208      | 移除  __main__  區塊重複的  init_db()  呼叫

  ### 認證邏輯流程(修改後)

    /function/<func_name>
        │
        ├─ func_name 在 PROTECTED_FUNCTIONS?
        │   └─ 未登入 Session → 401 (Authentication required)
        │
        ├─ func_name 在 TOKEN_PROTECTED_FUNCTIONS(save/update)
        │  OR func_name 不在 PROTECTED_FUNCTIONS(一般函式)?
        │   ├─ 無 X-API-Key Header → 401 (Missing API token)
        │   └─ Token 無效或 is_active=0 → 401 (Invalid or inactive API token)
        │
        └─ 驗證通過 → 動態載入並執行函式

  ### 快速測試方式

    # 插入測試 Token
    sqlite3 serverless.db "INSERT INTO api_tokens (token, owner) VALUES ('test-token-123',
  'developer');"

    # ✅ 正確呼叫
    curl http://localhost:5000/function/hello -H "X-API-Key: test-token-123"

    # ❌ 無 Token → 401
    curl http://localhost:5000/function/hello

不到三分鐘就完成重構了, 這若是照以前人工來改程式碼, 恐怕得花上一周或更久. 我叫 Claude 說明它改了哪些地方 :

> 解釋你修改了哪些檔案? 哪個部分(函式)?  

▸ Thought Process
  The user wants a clear explanation of which files and which functions were modif...
  這次只修改了 1 個檔案,共改動 4 個區塊:
  ──────
  ## 修改的檔案
  ### serverless.py
  ──────
  ### 1. Import 區塊(L1–8)

    + from functools import wraps

  新增  wraps ,讓裝飾器能保留被包裝函式的原始名稱與文件字串。
  ──────
  ### 2.  init_db()  函式(L16–37)
  改動前:用  if not os.path.exists(DB_PATH)  判斷,只有 DB 不存在時才建表,且只建  call_stats
  一張表。

  改動後:
  • 移除  os.path.exists  條件判斷,改為每次啟動都執行( CREATE TABLE IF NOT EXISTS
  本身就是冪等的,不會重複建表)
  • 新增  api_tokens  資料表,欄位為  id, token, owner, is_active, created_at
  ──────
  ### 3. 新增  require_api_token()  裝飾器(L56–73)

  全新函式,位於  record_call()  之後、 app = Flask(...)  之前。

  功能:從 Request Header 讀取  X-API-Key ,查詢資料庫確認 Token 存在且  is_active=1 ,否則回傳  401
  。

  │ 此裝飾器目前定義為備用,供未來掛在獨立路由上使用。
  ──────
  ### 4.  handle_function()  路由函式(L163–181)

  在原有的 Session 驗證之後,插入第二層 API Token 驗證邏輯(步驟 2):

    觸發條件(滿足其一即驗證):
      A. func_name 在 TOKEN_PROTECTED_FUNCTIONS → save_function、update_function
      B. func_name 不在 PROTECTED_FUNCTIONS → 所有一般使用者自訂函式

  兩種情況都會檢查  X-API-Key  Header,並查詢  api_tokens  表,不符合就回傳  401 。
  ──────
  ### 未修改的部分

   未動到的檔案/函式                               | 原因
  -------------------------------------------------|------------------------------------------------
    functions/  目錄下所有模組                     | 驗證邏輯集中在  serverless.py ,子模組不需變動
    check_auth()                                   | Session 登入機制保持原樣
    record_call()                                  | 呼叫統計邏輯不變
    /login 、 /logout  路由                        | 管理者登入流程不變

這個說明後續可以開啟主程式 serverless.py 來檢視. 


3. 功能測試 : 

上面 agy 已完成重構, 接下來實地測試 Claud 實作的 API Token 是否能正常運作. 在進行測試之前, 須在專案目錄下建立一個純文字檔 .env, 紀錄管理者的 token 與 key, 前者是自己容易記的密碼, 後者是用工具函式產生的一長串金鑰, 可以用 Python 內建的 secrets 模組來產生, 參考 :


在專案目錄下用記事本編輯一個 .env 檔, 輸入如下內容 (範例) :

SECRET_TOKEN=admin123
SECRET_KEY=fKFQPound41lv8FrWzU-lmFywxAT47uMwfvmBbiuLTE

其中的 SECRET_KEY 可在 Thonny 的 Python 互動環境匯入 secrets 模組後呼叫 secrets.token_urlsafe(32) 函式產生, 例如 :

>>> import secrets  
>>> secrets.token_urlsafe(32)  
'fKFQPound41lv8FrWzU-lmFywxAT47uMwfvmBbiuLTE'

由於我主要的測試環境主要是使用 Thonny 自帶的 Python, 所有 requirements.txt 中的套件在此開發環境都有安裝 (此專案主要是用到 Flask), 所以按 Thonny 的 "工具/開啟系統終端機" 開啟命令提示字元視窗, 切換到專案目錄下, 用下列指令啟動 Flask 開發伺服器 :

D:\antigravity_cli\projects\serverless>python serverless.py  
 * Serving Flask app 'serverless'
 * Debug mode: on

伺服器運行起來後, serverless.py 就會建立一個 SQlite 資料庫 serverless.db, 執行下列指令在此系統資料庫裡的 api_tokens 資料表填入測試用的權杖 test-token-123 : 

PS D:\antigravity_cli\projects\serverless> python -c "import sqlite3; conn = sqlite3.connect('serverless.db'); conn.execute('INSERT INTO api_tokens (token, owner, is_active) VALUES (?, ?, ?)', ('test-token-123', 'developer', 1)); conn.commit(); conn.close(); print('🎉 測試 Token 寫入成功!')"
🎉 測試 Token 寫入成功!

然後在瀏覽器網址列輸入 Flask 開發伺服器預設網址 http://localhost:5000/ 會看到 serverless 平台網站已順利運行, 按 "登入系統" :



輸入 .env 檔案中設定的 SECRET_TOKEN 密碼, 按 "登入" 鈕 :



登入成功後按 "函式列表" 進入管理頁面首頁 :



按 "資料表" :




可見多了一個 api_tokens 的資料表, 這就是 Claude 重構此平台時所添加, 用來儲存 API Key (token) 的資料表, 按 "檢視" :



顯示前面用 Python 指令寫入 api_tokens 資料表的測試用權杖 :




接下來用 curl.exe 測試沒帶 token 的情況, 這樣的請求會被拒絕 : 

PS D:\antigravity_cli\projects\serverless> curl.exe -i http://localhost:5000/function/hello   
HTTP/1.1 401 UNAUTHORIZED
Server: Werkzeug/2.3.7 Python/3.10.11
Date: Fri, 17 Jul 2026 11:36:12 GMT
Content-Type: application/json
Content-Length: 73
Connection: close

{
  "error": "Missing API token",
  "hint": "Provide X-API-Key header"
}

可見未攜帶 token 的請求會回應 "401 UNAUTHORIZED". 主體 (body) 內容為 JSON 訊息, error 鍵之值表示沒有收到 API token; 而 hit 鍵之值則表示必須在標頭攜帶 X-API-Key, 可在 curl.exe 指令中用 -H 參數指定此 token, 例如 : 

PS D:\antigravity_cli\projects\serverless> curl.exe -i http://localhost:5000/function/hello -H "X-API-Key: test-token-123"
HTTP/1.1 200 OK
Server: Werkzeug/2.3.7 Python/3.10.11
Date: Fri, 17 Jul 2026 15:41:03 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 12
Connection: close

Hello World!

可見若有攜帶正確的 X-API-Key 就會執行該函式, 回應 200 OK. 


4. 線上管理 API Key : 

在第一版的 serverless 函式執行平台就已實作了線上管理 serverless.db 的功能, 除了提供超連結可刪除資料表與紀錄外, 還可以執行 SQL 指令以新增, 更新, 或刪除紀錄. 

按資料表列表頁面下方的 "執行 SQL" :




輸入 SQL 指令後按 "執行" 即可, 例如要修改 owner=developer 使用中的 token 的 SQL 指令 :

UPDATE api_tokens 
SET token = 'new-test-token' WHERE owner='developer' AND is_active=1;




執行結果為成功 :




返回資料表內容檢視頁面可知 API Key (token 欄位) 已經更新了 : 




新增 API key 的 SQL 例如 : 

INSERT INTO api_tokens (token, owner, is_active) 
VALUES ('fKFQPound41lv8FrWzU-lmFywxAT47uMwfvmBbiuLTE', 'proj-a', 1);






只要以管理員身分登入, 就可以線上管理 API Key 了. 

以上測試顯示 agy 的重構確實達成了預期的目標, 讓 serverless 平台升級為需要 API Key 才能使用的網路服務, 避免伺服器被濫用或遭到攻擊. 

2026年7月17日 星期五

購買 Hahow 課程 : AI Coding 無痛上手

昨天在 Hahow 購買了 Kai 老師的 "AI Coding 無痛上手" 課程募資 : 





雖然我平常就在用 Vibe coding, 但看到課程介紹就很想去了解人家的作法, 不同的老師有不同的方法論, 值得花點學費去了解別人的 AI 工作術. 但我最近似乎買太多課程了, 月底從岡山回來後, 八月份起要來好好上課了. 

購買 MasterTalk 課程 (補記)

今天查看 LINE 紀錄才想起上周三 (7/8) 請假陪水某與菁菁去大甲鎮瀾宮拜拜, 在自強號上滑手機時看到 MasterTalk 平台的新課程通知 :





有點數折換打 9 折, 實付 3492 元. 

2026年7月15日 星期三

2026年7月14日 星期二

參加嘖嘖 ORIVA 隨行烘乾機募資預購

最近在 FB 看到嘖嘖的 ORIVA 隨行烘乾機募資廣告, 覺得對出國旅行衣物烘乾很方便, 就參加預購了, 但要八月才開始出貨, 趕不上月底跟阿中的岡山之旅 :