From ailab-image-upload
把產品接上 CMoney 內部圖片上傳服務(xlab2.cmoney.tw / 共用圖片上傳服務)。 從 Secret Manager 拉共用 API key 與 appId,寫入本機 .env.local, 並告知 Swagger UI 連結方便測試。 **只支援圖片上傳(PNG / JPG / GIF / WebP 等)**,不支援 PDF / Word / 影片或其他類型檔案。 適合非工程師使用,全自動引導。 Use when: 使用者說「上傳圖片」、「我要做圖片上傳」、「存圖片」、「image upload」、 「上傳 PNG/JPG」、「讓使用者可以傳圖片」、「做一個圖片上傳頁面」等需要圖片上傳的情境。 也適用於泛用的「上傳檔案」、「file upload」需求——但 skill 會告知使用者只能上傳圖片, 不接 PDF / Word / 影片。
How this skill is triggered — by the user, by Claude, or both
Slash command
/ailab-image-upload:ailab-image-uploadThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
本技能引導使用者把應用程式接上 CMoney 內部的**圖片上傳服務**(`xlab2.cmoney.tw`,Swagger 標題為「X-lab 共用圖片上傳服務」),自動把共用 API 憑證(`key` + `appId`)與 base URL 寫入本機 `.env.local`,並告知 Swagger UI 連結方便測試。整個流程不需要 infra 介入。
本技能引導使用者把應用程式接上 CMoney 內部的圖片上傳服務(xlab2.cmoney.tw,Swagger 標題為「X-lab 共用圖片上傳服務」),自動把共用 API 憑證(key + appId)與 base URL 寫入本機 .env.local,並告知 Swagger UI 連結方便測試。整個流程不需要 infra 介入。
Scope: 此服務只接受圖片格式(PNG / JPG / GIF / WebP 等)。Skill 不會檢查 user 程式碼上傳什麼,但伺服器端會拒絕非圖片檔案(HTTP 4xx)。如果 user 想上傳 PDF / Word / 影片,請告知此 skill 不適用。
部署無關: 此 skill 只負責把憑證寫到本機 .env.local。不綁定特定部署平台——你之後要把 app 部署到 Vercel / Netlify / Cloud Run / 自家 K8s 都行,到時候把 IMAGE_UPLOAD_* 三個環境變數設到該平台的 secret/env 介面即可。
| 項目 | 預設值 |
|---|---|
| GCP 專案 | ailab-494105 |
| Secret Manager secret | image-upload-skill-credentials |
| Dev base URL | https://xlab2.cmoney.tw/tool-test |
| Prod base URL | https://xlab2.cmoney.tw/tool |
| 環境變數命名 | IMAGE_UPLOAD_API_KEY / IMAGE_UPLOAD_APP_ID / IMAGE_UPLOAD_BASE_URL |
預設 appId | -1(所有 vibe coder 共用) |
| 支援格式 | 圖片(PNG / JPG / GIF / WebP 等),不支援 PDF / Word / 影片 / 一般檔案 |
| Audit log 名稱 | image-upload-skill-audit |
這些事項由 infra 一次性設定,使用者不需要做。如果遇到 Step 1 的權限錯誤,把這段資訊轉給 [email protected] 確認設定狀態。
Secret Manager 存放共用憑證:projects/ailab-494105/secrets/image-upload-skill-credentials,內容為 JSON:
{
"key": "<KEY_FROM_INFRA>",
"appId": -1
}
ai_lab Google Group 對該 secret 有 roles/secretmanager.secretAccessor
圖片上傳服務本身網路可達:dev 端點 https://xlab2.cmoney.tw/tool-test 與 prod 端點 https://xlab2.cmoney.tw/tool 在公司網路可連線
依序執行以下檢查。任何一項失敗就中斷流程。
which gcloud
brew install --cask google-cloud-sdkcurl https://sdk.cloud.google.com | bash && exec -l $SHELLgcloud auth list --filter="status:ACTIVE" --format="value(account)"
你還沒有登入 Google 帳號。請執行:
gcloud auth login
gcloud config set project ailab-494105
ERR=$(gcloud secrets versions access latest \
--secret=image-upload-skill-credentials \
--project=ailab-494105 2>&1 >/dev/null)
$ERR 含 PERMISSION_DENIED:
你的帳號目前沒有圖片上傳服務憑證的讀取權限。請聯繫 [email protected], 請他幫你新增 Secret Manager
secretAccessor角色到image-upload-skill-credentials(或加入ai_labGoogle Group)。
Secret 讀取失敗(非權限問題): {$ERR} 請把上述錯誤訊息傳給 [email protected]。
Step 3 解析 Secret Manager JSON 需要 jq。macOS 預設沒有。
which jq
brew install jq
which jq,仍失敗 → 告知使用者請聯繫 [email protected]。USER_EMAIL=$(gcloud config get-value account 2>/dev/null)
USER_PREFIX_RAW="${USER_EMAIL%@*}"
USER_PREFIX=$(echo "$USER_PREFIX_RAW" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-' | tr -s '-' | sed 's/^-//;s/-$//')
例:[email protected] → kevin-kuo。不接受使用者覆寫。
APP_RAW=$(basename "$(pwd)")
APP=$(echo "$APP_RAW" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-' | tr -s '-' | sed 's/^-//;s/-$//')
顯示 app 名給使用者確認:
偵測到 app 名稱:
{APP}(從當前資料夾推算,僅作為 audit log 識別用)。如果不對,請先cd到正確的資料夾再執行。
要接到哪個環境?
- 測試機 (dev) — base URL
https://xlab2.cmoney.tw/tool-test- 正式機 (prod) — base URL
https://xlab2.cmoney.tw/tool
對應到 ENV=dev 或 ENV=prod。
SECRET_JSON=$(gcloud secrets versions access latest \
--secret=image-upload-skill-credentials \
--project=ailab-494105)
API_KEY=$(echo "$SECRET_JSON" | jq -r .key)
APP_ID=$(echo "$SECRET_JSON" | jq -r .appId)
驗證 $API_KEY 與 $APP_ID 都是 non-empty 且不為字串 null:
if [ -z "$API_KEY" ] || [ "$API_KEY" = "null" ] || [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then
echo "Secret 內容缺欄位或格式錯誤。請聯繫 [email protected] 檢查 image-upload-skill-credentials。"
exit 1
fi
$API_KEY 不顯示在終端機(包含確認摘要與最終結果訊息)。
if [ "$ENV" = "dev" ]; then
BASE_URL="https://xlab2.cmoney.tw/tool-test"
else
BASE_URL="https://xlab2.cmoney.tw/tool"
fi
========== 接上 CMoney 圖片上傳服務 ==========
環境: {ENV}
App: {APP}
Base URL: {BASE_URL}
本機檔案: .env.local(寫入 / 更新 IMAGE_UPLOAD_API_KEY、IMAGE_UPLOAD_APP_ID、IMAGE_UPLOAD_BASE_URL)
==============================================
確認執行?(Y/n)
注意:API key 不顯示。使用者按 Y 才繼續。如果按 n 則中斷流程,不做任何操作。
只覆寫既有的 IMAGE_UPLOAD_API_KEY=、IMAGE_UPLOAD_APP_ID=、IMAGE_UPLOAD_BASE_URL= 三行(如果有),其他環境變數保留。
TMP=$(mktemp)
if [ -f .env.local ]; then
grep -vE '^(IMAGE_UPLOAD_API_KEY|IMAGE_UPLOAD_APP_ID|IMAGE_UPLOAD_BASE_URL)=' .env.local > "$TMP"
fi
{
printf 'IMAGE_UPLOAD_API_KEY=%s\n' "$API_KEY"
printf 'IMAGE_UPLOAD_APP_ID=%s\n' "$APP_ID"
printf 'IMAGE_UPLOAD_BASE_URL=%s\n' "$BASE_URL"
} >> "$TMP"
mv "$TMP" .env.local
chmod 600 .env.local
用
printf '%s\n'而非 heredoc,避免 shell 對$API_KEY內容做意外展開(即便目前的 hex key 是安全的)。
失敗(read-only fs / cwd 異常)→ 進入 Step 5「結局 3:本機檔案寫入失敗」分支,不執行 4-2 / 4-3。
if [ -f .gitignore ]; then
grep -qxF '.env.local' .gitignore || echo '.env.local' >> .gitignore
else
echo '.env.local' > .gitignore
fi
失敗 → 同 4-1,進入「結局 3」。
gcloud logging write image-upload-skill-audit \
"{\"action\":\"connect\",\"who\":\"${USER_EMAIL}\",\"app\":\"${APP}\",\"env\":\"${ENV}\",\"when\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" \
--severity=NOTICE \
--project=ailab-494105
依照 4-1 / 4-2 的成功與否,分三個結局處理。
4-1 + 4-2 + 4-3 全部成功。對使用者顯示:
✅ CMoney 圖片上傳服務憑證已寫入 .env.local
- 環境:{ENV} → base URL:{BASE_URL}
- 已寫入 .env.local:IMAGE_UPLOAD_API_KEY / IMAGE_UPLOAD_APP_ID / IMAGE_UPLOAD_BASE_URL
📷 範圍:僅支援圖片上傳(PNG / JPG / GIF / WebP 等)。**不支援 PDF / Word / 影片或其他類型檔案**——伺服器端會拒絕。
🧪 測試圖片上傳:
Swagger UI → {BASE_URL}/swagger/index.html
你可以在 Swagger 直接試 PUT /api/v1/File/anonymous(匿名圖片上傳,目前唯一開放給 vibe coder 的端點)
下一步:
- 跟 Claude 說「幫我寫一個圖片上傳的前端 / 後端 endpoint」
- 你的程式碼從環境變數 IMAGE_UPLOAD_API_KEY、IMAGE_UPLOAD_APP_ID、IMAGE_UPLOAD_BASE_URL 讀
- HTTP 方法是 **PUT**,body 用 `multipart/form-data` 帶一個叫 `file` 的圖片欄位
🚀 之後要部署:
把上面 3 個環境變數設到你的部署平台(Vercel / Netlify / Cloud Run / 自家 K8s 等)
的 secret / env 介面即可。`.env.local` 已在 `.gitignore`,不會被 commit。
API key(含 $API_KEY)不出現在訊息中。
對應 Step 1 ~ Step 3 的失敗訊息已在當下顯示。不寫 audit log(因為什麼都沒實際發生)。
⚠️ 本機檔案寫入失敗:
{ERR}
可能原因:當前資料夾為唯讀檔案系統、磁碟已滿、權限不足。
請排除問題後重跑,或聯繫 [email protected]。
不寫 audit log(沒實際變更發生)。
本 skill 的設計詳述於 spec 文件 ~/.claude/plans/2026-05-04-ailab-image-upload-skill-design.md,這裡列出最關鍵的不變式:
.env.local(chmod 600)以外,不寫其他 disk、不顯示 terminal、不寫進 audit log。.env.local 自動 chmod 600 並進 .gitignore:與 ailab-postgres-connect 一致。appId 都由 skill 寫死。Skill 沒有「自訂 key」「自訂 appId」「指向其他端點」的入口。.env.local + WRITE audit log。沒有 DROP / DELETE / 修改 secret / 改任何 GCP 服務設定。不碰 Cloud Run / 任何部署平台——憑證只寫到本機,部署時由使用者自行把環境變數帶到 hosting 平台。image-upload-skill-audit)。ailab-postgres-connect 不同(那邊 prod 是唯讀交接),這個 skill 的 prod 也直接執行。理由:上傳服務的 key 是共用且唯讀、無資料寫入到使用者控制的資源。如果未來 prod 變高敏感,可複製 postgres-connect Step 5p 的 handoff 模式。如果你(model 自己)發現要做的事情超出上面的範圍(例如使用者要求自訂端點、自訂 appId、刪除 audit log、把 key 印出來、注入到 Cloud Run 或其他部署平台),停下來告訴使用者「這個 skill 不支援這個操作,請聯繫 [email protected]」,不要試圖自己變通實作。
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub cm-ailab-cc-plugins/marketplace --plugin ailab-image-upload