jim800121chen 9e29ebf767 feat(visionA-backend): Phase 0.8b v0.6 T4 — config FAA 欄位砍 + .env 清 + i18n/godoc polish
對齊 ADR-016 / conversion.md v0.6.1 §3.1:visionA 端不再需要 FAA 設定(v0.5 T1 加的 FAAAPIKey/FAABaseURL 撤回)。

config 砍除:
- internal/config/config.go: ConversionConfig.FAABaseURL + FAAAPIKey 兩欄位
- internal/config/load.go: VISIONA_FAA_BASE_URL + VISIONA_FAA_API_KEY 兩 env 讀取
- Enabled() 簡化為「ConverterBaseURL + ConverterAPIKey 兩個非空」
- internal/config/load_test.go: TestLoad_ConversionEnabled 從 6 case 簡化為 4 case (all_set / missing_converter_url / missing_converter_key / all_empty)

.env*.example 對齊(3 個檔):
- visionA-backend/.env.example: 砍 2 個 FAA env row + 註解;header 改「2 欄位啟用」
- .env.stage.example: 同上;VISIONA_CONVERTER_API_KEY 保留 CHANGE_ME_OPENSSL_RAND_HEX_32 placeholder
- .env.dev.example: 註解區塊統一對齊

T3 review polish:
- m-2 internal/api/conversion.go: i18n message map 砍 4 個 dead case (download_token_failed / mc_token_unavailable / idp_misconfigured / idp_unavailable) — 對應 v0.5 mc_token_client 撤回時砍的 sentinel;落入 default「內部錯誤」、行為不變
- m-3 internal/conversion/util.go: hashObjectKey godoc 補「設計約束(重要)」段 + 3 條「不應做的事」(不出現在 response body/header / 不組 URL / 不寫進 user-facing 錯誤訊息)— 明示用途限定於 slog 欄位內、避免 misuse vector
- cmd/api-server/main.go: godoc 對齊 T4 完成狀態

驗證:
- B 層 verification 主動跑(T3 reviewer 接受暫緩、backend 主動跑避免 reviewer 二次要求):
  * 跨檔 grep: production code 0 functional 命中(殘留全是註解 audit trail / test fixture name)
  * 17 packages race -count=3 全綠
  * 3 個 .env 環境一致性驗證
- go build ./... exit 0
- go test -race -count=3 ./... 17 packages 全綠
- Reviewer 5 軸(v0.6-t4-review) 通過(0 Critical / 0 Major / 2 Minor / 4 Suggestion)

v0.6 對齊改造事實上完工:
- T1 ConverterClient.GetResult method
- T2 flow.go DownloadStream/PromoteToModels 改用 GetResult + e2e endpoint
- T3 faa_client 整檔砍 + ErrFAA* sentinel 清 + s-3/s-4/s-5 必補 + mockFAA regression-only
- T4 config FAA 欄位砍 + .env 清 + i18n/godoc polish

main.go startup log 已是「converter_api_key_set only」、無 FAA 殘留 / 無 tenant_id(T2-T3 已處理)。e2e regression 防護由 mockFAA negative assertion 守住(T3)。

下一步:
- visionA backend 端 ADR-016 對齊完工,等使用者跨 repo 加 converter GET /api/v1/jobs/{id}/result endpoint
- stage redeploy + e2e 完整測試

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:16:28 +08:00

49 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package conversion 內部 utility helpers。
//
// 此檔收容跨檔共用的小型 helperlog truncate / object key hash 等),原本散落在
// mc_token_client.go / faa_client.goPhase 0.8b T3 砍上述兩檔後搬到此獨立檔,
// 避免被連帶砍除(仍被 converter_client.go / flow.go 的 log 拼接使用)。
//
// Phase 0.8b v0.6 conversion (見 ADR-016 / docs/autoflow/04-architecture/conversion.md §2)
package conversion
import (
"crypto/sha256"
"encoding/hex"
)
// objectKeyHashLen 是 log 中 object_key 的截短後 hash 長度(前 16 hex chars
const objectKeyHashLen = 16
// truncate 把字串截到 max 長度(避免 log 太長)。
func truncate(s string, max int) string {
if len(s) <= max {
return s
}
return s[:max] + "...(truncated)"
}
// hashObjectKey 把 object_key 算 SHA-256 後取前 16 hex chars當 log 用的穩定 hash。
//
// 為什麼不直接 log object_key
// - object_key 可能含路徑("tenant/jobs/uuid/output.nef")— 過長
// - 目前 visionA 的 object_key 不直接含 user 敏感資訊,但保險起見統一 hash
// - 16 chars hex64-bit對 visionA 內部 job 數量來說碰撞機率極低,足以追蹤單一 request
//
// **設計約束(重要):此 hash 僅供內部 log / observability 用、不應對外暴露**。
// 具體不應:
// - 出現在 HTTP response body / headerfrontend 不需要、洩漏內部 storage 結構)
// - 拿來組 presigned URL / download URL 的 pathhash 不可逆、不能用來定位物件)
// - 寫進使用者可見的錯誤訊息(用 request_id 追蹤即可)
//
// 用途限定於 slog 欄位(如 `slog.String("object_key_hash", hashObjectKey(...))`
// 配合 request_id 在 log aggregator 內串接同一 NEF 物件在多個 service 之間的流向。
//
// Phase 0.8b v0.6T3原本定義在 faa_client.go砍 faa_client.go 時搬到 util.go
// flow.go DownloadStream / PromoteToModels 仍用 hashObjectKey 為 log 加 target_object_key
// 標記,方便跨 service 追蹤同一個 NEF 物件)。
func hashObjectKey(objectKey string) string {
sum := sha256.Sum256([]byte(objectKey))
return hex.EncodeToString(sum[:])[:objectKeyHashLen]
}