2026年7月23日 星期四

岡山之旅 Day 0 (7/23) : 虎航自助報到

昨天接到虎航預辦登機通知 Email, 原來在出發前兩天就可以線上辦理自助報到, 上傳護照內頁圖檔驗證內容 (姓名, 生日, 護照號碼, 到期日等) 無誤後就可以下載電子登機證, 屆時到機場櫃台就可以直接託運行李, 不需要排隊報到了. 

按 Email 底下的 "預辦登機" 鈕 : 




填寫當初購票時取得之訂位代號 (Email 標題就有), 護照英文姓名 (不用輸入 -, 輸入也會被去除), 按 "開始報到" :






按去程航班右邊的 "可報到" 鈕 :




接下來是行李託運與安檢相關規定事項, 閱讀後勾選底下 "我已完整閱讀並同意以上事項" 後按 "下一步" :






接下來是要輸入護照資料, 如果可以用影印機掃描護照內頁最好, 辨識成功率較高 (但也要仔細檢查), 用手機拍照要橫拍, 光線也要注意才能順利辨識, 如果都失敗就用手動輸入 : 




檢視辨識資料是否正確 (錯誤資料可能影響登機) :




資料驗證過關後顯示已加購項目, 此處還可再加購, 若不需要就按左邊 "不加購到下一步" 鈕完成自助報到, 並顯示電子登機證下載超連結 : 




完成自助報到後, 虎航也會寄一封包含電子登機證 pdf 檔的 Email, 將此 pdf 存入手機, 有印表機最好也列印出來作為備份. 

iPhone 手機加入 eSIM 的方法

明天要跟老同事阿中前往日本岡山採水蜜桃, 主要是應其要求, 教他如何在日本自助旅行. 前天已購買中華電信行動漫遊方案 (暑假日本定量型 10 天 10GB 688 元), 今天上 KLOOK 為備用手機 iPhone SE2 買 eSIM 卡 (軟銀 6 天每日 1GB) :



付款完成後點右下角的 "帳戶" 點選 "我的訂單" 按此訂單進去就會出現 eSIM 卡的 QR 碼, 把它截圖印出來, 或傳送到同行家人手機 LINE 備用. 

到達目的地機場後, 開啟 iPhone 的 WiFi 功能連到家人或機場的免費 WiFi :




進入 "行動服務", 按底下的 "加入 eSIM" :




按 "使用行動條碼" :




拿出列印的 QR 碼或家人手機中的 QR 碼, 按 "掃描行動條碼" 掃描即可 :



掃描完成後開啟行動行動數據選擇此 eSIM 即可上網. 

iPhone 手機螢幕截圖方法

我的備用手機 iPhone SE2 有時需要螢幕截圖 (例如紀錄如何啟用 eSIM), 因為它有 HOME 鍵, 所以傳統截圖是同時按電源 + HOME 鍵即可, 但要同時按兩個鍵不方便, 也不好控制, 我今天問 Gemini , 原來可以用輕點背面 2 或 3 下方式截圖, 設定方法如下 : 

打開設定, 點選 "輔助使用" : 





點選身體動作項目下的 "觸控" :




開啟 "背面輕點" :




按點兩下或點三下, 勾選 "截圖" 即可 :




設定好後, 手指在手機背板輕敲兩下就可截圖了. 

2026年7月22日 星期三

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

前一篇在 Mapleboard 上成功地佈署了新版 serverless 平台, 驗證 API Key 功能正常, 整個專案重構來作業已近尾聲, 本篇要將專案提交上傳更新 GitHub 上的 repo (儲存庫). 

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



8. 提交新版 serverless 與上傳 GitHub : 

在提交之前, 我回顧了前面的測試過程, 發現剛開始時是從 https://github.com/tony1966/serverless.git 複製專案到 D:\antigravity_cli\projects 底下, 然後將其打上 tag 標記後建立一個新分支 api-token-auth, 再叫 agy 用 Claude 模型去重構此專案, 這就形成了 Nested Repository (巢狀儲存庫) : serverless 資料夾內自成一國 (裡面有自己的 .git), 而外層的 antigravity_cli 也有自己的 .git 現象. 

站在 D:\antigravity_cli 的角度看 projects/serverless 時, Git 預設只會把 serverless 當成一個被忽略的子目錄或是一個未追蹤的 gitlink, 兩邊的 Commit 完全是分開互不干擾的. 在 serverless 下提交並 push 時會上傳到 https://github.com/tony1966/serverless 上, 而非 Antigravity CLI 的 repo 庫https://github.com/tony1966/antigravity_cli. 雖然這種國中有國的情況並不少見, 但我想讓專案資料夾與儲存庫的關係單純點, 所以將此 serverless 專案剪下來貼到 D:\ 下, 與 D:\antigravity_cli\projects 分家. 

分家後的 serverless 也要有自己的 .gitignore, 所以我把 Antigravity CLI 大倉庫下的 .gitignore 複製一份來給它用 : 

# ==========================================
# Python 相關快取與編譯檔案
# ==========================================
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.poetry/
.venv/
venv/
ENV/
env/

# ==========================================
# 專案實務資料與測試產出
# ==========================================
*.json
*.db
*.log

# ==========================================
# 開發工具與編輯器暫存檔
# ==========================================
.vscode/
.idea/
*.swp
*.bak
.DS_Store
Thumbs.db

# ==========================================
# 敏感資料與環境變數 (絕對不能上傳)
# ==========================================
.env
.env.local
.env.*.local
*.env

分家後的 serverless 專案結構如下 :

PS D:\> tree serverless /f   
列出磁碟區 新增磁碟區 的資料夾 PATH
磁碟區序號為 0000027B 1258:16B8
D:\SERVERLESS
│  .env
│  .gitignore
│  Procfile
│  requirements.txt
│  serverless.db
│  serverless.py
│  serverless_error.log
└─functions
    │  add_function.py
    │  add_table.py
    │  clear_stats.py
    │  delete_function.py
    │  delete_record.py
    │  drop_table.py
    │  edit_function.py
    │  execute_sql.py
    │  export_table.py
    │  hello.py
    │  list_functions.py
    │  list_tables.py
    │  save_function.py
    │  show_schema.py
    │  show_stats.py
    │  update_function.py
    │  view_table.py
    │  __init__.py
    │
    └─apps_backup
          add.py
          hello.py
          linebot_gemini.py
          linebot_gpt.py
          send_books_messages.py
          send_ksml_books_messages.py
          update_ksml_books.py

這樣便可進行 Git 提交作業了, 首先切換到專案目錄下 :

S D:\> cd serverless   

然後用 git add . 指令將所有變更加入暫存區, 把修改過的程式碼與新增的 .gitignore 檔加入追蹤 (.gitignore 內所舉的檔案除外) : 

PS D:\serverless> git add .   
warning: in the working copy of 'functions/save_function.py', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'functions/update_function.py', LF will be replaced by CRLF the next time Git touches it
PS D:\serverless>

報出的 warning 可以忽略. 

然後建立 Commit 紀錄, 將變更封裝並附上明確的功能說明 : 

PS D:\serverless> git commit -m "feat: implement serverless API core with API Key auth"   
[feature/api-token-auth df845ce] feat: implement serverless API core with API Key auth
 5 files changed, 172 insertions(+), 37 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 functions/hello.py

接下來是推送到 GitHub 遠端倉庫的 feature/api-token-auth 分支 (-u 參數會自動建立本地與遠端分支的追蹤關係) :

PS D:\serverless> git push -u origin feature/api-token-auth   
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 16 threads
Compressing objects: 100% (18/18), done.
Writing objects: 100% (18/18), 8.71 KiB | 1.24 MiB/s, done.
Total 18 (delta 7), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (7/7), completed with 4 local objects.
remote:
remote: Create a pull request for 'feature/api-token-auth' on GitHub by visiting:
remote:      https://github.com/tony1966/serverless/pull/new/feature/api-token-auth
remote:
To https://github.com/tony1966/serverless.git
 * [new branch]      feature/api-token-auth -> feature/api-token-auth
branch 'feature/api-token-auth' set up to track 'origin/feature/api-token-auth'.

可見 18 個檔案物件已成功打包上傳 (其中包含新的 .gitignore 與 code), 且在 GitHub 成功建立獨立的 feature/api-token-auth 分支與追蹤關係. 這時到 GitHub 去察看此 repo :




可見 GitHub 遠端倉庫 tony1966/serverless 已經順利接收到了最新的變更, 而下方的是 main 分支 (正式生產環境) 的檔案列表, 顯示它依然維持 6 個月前的穩定版本, 只要此 feature/api-token-auth 分支尚未合併到 main 分支就不會影響目前依賴此 repo 的線上服務 (例如 render.com). 

目前暫時先做到這裡, 後續若準備好要把 API Key 驗證功能部署到 Render.com 時, 只要按右上角的 "Compare & pull request" 鈕, 檢查變更內容沒問題後按 "Create pull request" 鈕, 再按 "Merge pull request" 與 "Confirm merge" 鈕即可合併的 main 分支, 完成升版的最後一塊拼圖. 

如果要在終端機下指令合併, 順序如下 : 

# 切換到 main 分支
git checkout main

# 拉取遠端最新 main(確保同步)
git pull origin main

# 將 feature 分支合併進 main
git merge feature/api-token-auth

# 推送回 GitHub main 分支(會觸發 Render 自動部署)
git push origin main

註冊第三個 render.com 帳戶

上週完成 serverless 改版後本想先佈署到 Mapleboard 上, 做實際驗證後再提交上傳 GitHub, 但由於 Mapleboard 站台設定引發疑似光世代數據機的雞肋防火牆阻擋問題, 導致本地的圖書館爬蟲要更新 Mapleboard 的 serverless 資料庫時偶而被阻擋, 這讓我對自架網站可靠性動搖. 反觀我架在 render.com 上的 serverless 只要定時 trigger 卻很穩定在運行, 可見雲端還是比較可靠, 所以今天又註冊了一個 render.com 帳戶來做測試. 

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


首先到 render.com 官網按右上角的 Sign Up 鈕進入註冊頁面 :


這次使用 Google 帳戶註冊, 過程比用 email 簡單 :




選擇 Google 帳戶後按 "繼續" 即可 : 





註冊成功直接進入 Dashboard 頁面 :



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 已被建立, 再次執行網頁測試結果相同.