jim800121chen 22f0837ba8 feat(visionA-backend): Phase 0 → 0.7 雲端後端(雙 binary + OIDC BFF + stage 部署)
從 edge-ai-platform POC 轉為正式產品的雲端後端,含以下整合階段:

- Phase 0:雛形骨架 — `cmd/api-server` (REST :3721) + `cmd/remote-proxy`
  (tunnel :3800 / internal :3801) 雙 binary 共用 internal/,沿用 POC 的
  WebSocket+yamux tunnel 協定但解耦 relay 與 API
- Phase 0.6:OIDC BFF 接 Innovedus Member Center
  - internal/oidc package(coreos/go-oidc + PKCE S256 + state + nonce)
  - internal/usersession package(HMAC-SHA256 cookie + RotateSessionID
    防 session fixation, OWASP ASVS V3.2.1)
  - 4 個 OIDC handler(/api/auth/login|callback|me|logout)+ AuthMiddleware
  - 完全拔除 StaticAuthProvider,OIDC 是唯一認證路徑
  - 9 個 ADR(含 ADR-010 BFF / ADR-011 取代 static auth /
    ADR-012 pending session shared cookie / ADR-013 PKCE-only public client)
- Phase 0.7:A1 改造 + security audit 修復
  - OIDC ClientSecret 變選填,支援 stage MC 的 public PKCE-only client
    (AuthStyleInParams 強制 token endpoint 不送 client_secret)
  - 預留 ServiceClient* 欄位給未來 client_credentials grant
  - 移除 13+ 處 resolveUserID(uc, StaticUserID) fallback 改 strict mode
    (Audit C1:multi-tenant 隔離破口)
  - Pairing exchange MarkUsed 失敗 abort + revoke session token(Audit M3)
  - 新增 all_endpoints_require_auth_test 整合測試(51 endpoint × 401)

驗證:go test -race -count=3 ./... 17 packages 全綠 / go vet 0 warning

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 11:21:20 +08:00

96 lines
3.8 KiB
Go
Raw Permalink 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 storage 定義物件儲存介面與 LocalFS 實作。
//
// 對齊 storage.md §1 與 PRD interface-contracts.md §8.4
// - 雛形使用 LocalFSStore檔案系統+ 假 presigned URLHMAC 簽名)
// - Phase 1 新增 S3Store同 interface業務邏輯不用動
//
// Key 命名規範見 storage.md §2`models/{user_id}/{model_id}.nef`)。
package storage
import (
"context"
"errors"
"io"
"time"
)
// ==========================================================================
// Errors
// ==========================================================================
var (
// ErrNotFound 表示指定 key 的 object 不存在。
ErrNotFound = errors.New("storage: object not found")
// ErrAlreadyExists 表示 object 已存在且 Put 被要求不覆蓋Phase 1
ErrAlreadyExists = errors.New("storage: object already exists")
// ErrInvalidKey 表示 key 含非法字元(例:包含 "..", 嘗試 path traversal
ErrInvalidKey = errors.New("storage: invalid key")
// ErrInvalidSignature 表示 presigned URL 簽名錯誤或過期。
ErrInvalidSignature = errors.New("storage: invalid or expired signature")
)
// ==========================================================================
// Types
// ==========================================================================
// Object 是儲存物件的描述metadata不含實際內容
//
// 對齊 storage.md §1 的 ObjectInfo。
type Object struct {
Key string `json:"key"`
Size int64 `json:"size"`
ContentType string `json:"contentType"`
LastModified time.Time `json:"lastModified"`
ETag string `json:"etag,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// ==========================================================================
// Store interface
// ==========================================================================
// Store 是物件儲存的抽象。
//
// 實作必須:
// - 並發安全(多 goroutine 同時 Put / Get 不 panic
// - Key 驗證(防止 path traversal
// - 語意對齊Exists 回 (false, nil) 表不存在,其他錯誤走 err
type Store interface {
// Put 上傳一個 object若已存在則覆蓋。
// size 為預期大小bytes實作可用於早期檢查或配額控制。
// meta 可為 nil無額外 metadata
Put(ctx context.Context, key string, r io.Reader, size int64, meta map[string]string) error
// Get 下載一個 objectcaller 必須 Close() reader。
// 不存在回 ErrNotFound。
Get(ctx context.Context, key string) (io.ReadCloser, *Object, error)
// Stat 取得 object 的 metadata不下載內容
// 不存在回 ErrNotFound。
Stat(ctx context.Context, key string) (*Object, error)
// Exists 判斷 key 是否存在Minor-4 / PRD §8.4 要求)。
// 語意true = 存在可用false = 不存在(非 error
// 其他錯誤(權限 / IO回 (false, err)。
Exists(ctx context.Context, key string) (bool, error)
// Delete 刪除 object不存在為 no-op不回 error。
Delete(ctx context.Context, key string) error
// List 列出指定 prefix 下的所有 object。
List(ctx context.Context, prefix string) ([]*Object, error)
// PresignedGetURL 產生一個限時下載 URL。
// 對 LocalFS回傳 baseURL + /key?expires=&signature=(由 api-server 驗證)
// 對 S3回傳原生 AWS presigned URL
PresignedGetURL(ctx context.Context, key string, ttl time.Duration) (string, error)
// PresignedPutURL 產生一個限時上傳 URL。
// 對 LocalFS回傳 baseURL + /key?expires=&signature=&mode=put
// 對 S3回傳原生 AWS presigned PUT URL
PresignedPutURL(ctx context.Context, key string, ttl time.Duration) (string, error)
}