Files
lijiaoqiao/supply-api/internal/httpapi/alert_api.go
Your Name ad776e4079 fix: P0/P1 security fixes across gateway, token-runtime, and supply-api
P0 fixes:
- platform-token-runtime: Add store.Save() after Refresh token update (P0-3)
- platform-token-runtime: Add sync.RWMutex to InMemoryRuntimeStore (P0-4)
- platform-token-runtime: Add bearer token auth to /audit-events endpoint (P0-5)
- gateway: Fail startup in production if PASSWORD_ENCRYPTION_KEY uses default (P0-1)
- gateway: Require explicit CORS_ALLOW_ORIGINS in production (P0-2)

P1 fixes:
- gateway: Add TrustedProxies config field + env var GATEWAY_TRUSTED_PROXIES (P1-5)
- gateway: Sanitize X-Request-ID header to prevent log injection (P1-6)
- gateway: Strip internal error details from error responses to clients (P1-7)
- supply-api: Upgrade deriveDEK from trivial byte-rotation to HKDF-SHA256 (P1-1)
- supply-api: Reject HS256/HS384/HS512 in production, require RSA (P1-2)

Code quality fixes:
- supply-api: Add BruteForceMaxAttempts + BruteForceLockoutDuration to AuthConfig (MED-12)
- supply-api: Add TrustedProxies to token_auth_middleware (IP spoofing protection)
- supply-api: Use shared pathutil.SplitPath instead of duplicate splitPath
- supply-api: Fix query_key_reject_middleware call sites with trustedProxies param
- gateway: Wire TrustedProxies into AuthMiddlewareConfig and extractClientIP
- gateway: Add CORSAllowOrigins to AuthConfig, wire into CORSMiddleware
- gateway: Fix CompletionsHandle to have context and RecordResult like ChatCompletions
- gateway: Add sanitizeRequestID helper for X-Request-ID log injection prevention
- gateway: Add os import for PASSWORD_ENCRYPTION_KEY check
- gateway: Add strings import to handler.go for sanitizeRequestID

Environment issues documented in TEST_ENVIRONMENT_ISSUES.md
2026-04-17 14:36:02 +08:00

107 lines
2.7 KiB
Go

package httpapi
import (
"errors"
"net/http"
"net/url"
"lijiaoqiao/supply-api/internal/audit/handler"
"lijiaoqiao/supply-api/internal/audit/service"
"lijiaoqiao/supply-api/internal/pkg/pathutil"
)
// AlertAPI 告警API处理器
type AlertAPI struct {
alertHandler *handler.AlertHandler
}
// NewAlertAPI 创建告警API处理器
func NewAlertAPI(alertSvc *service.AlertService) (*AlertAPI, error) {
if alertSvc == nil {
return nil, errors.New("alert service is required")
}
alertHandler := handler.NewAlertHandler(alertSvc)
return &AlertAPI{
alertHandler: alertHandler,
}, nil
}
// Register 注册告警路由
func (a *AlertAPI) Register(mux *http.ServeMux) {
// Alert CRUD
mux.HandleFunc("/api/v1/audit/alerts", a.handleAlert)
mux.HandleFunc("/api/v1/audit/alerts/", a.handleAlertByID)
}
// handleAlert 处理 /api/v1/audit/alerts 的路由分发
func (a *AlertAPI) handleAlert(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
a.alertHandler.CreateAlert(w, r)
case http.MethodGet:
a.alertHandler.ListAlerts(w, r)
default:
writeError(w, http.StatusMethodNotAllowed, CodeMethodNotAllowed, "method not allowed")
}
}
// handleAlertByID 处理 /api/v1/audit/alerts/{alert_id} 的路由分发
func (a *AlertAPI) handleAlertByID(w http.ResponseWriter, r *http.Request) {
// 提取路径最后部分判断操作
path := r.URL.Path
if len(path) > 0 && path[len(path)-1] == '/' {
path = path[:len(path)-1]
}
parts := pathutil.SplitPath(path)
if len(parts) < 5 {
writeError(w, http.StatusBadRequest, CodeInvalidPath, "invalid path")
return
}
alertID := parts[4]
// 检查是否是特殊操作
if len(parts) > 5 && parts[5] == "resolve" {
if r.Method == http.MethodPost {
a.alertHandler.ResolveAlert(w, r)
} else {
writeError(w, http.StatusMethodNotAllowed, CodeMethodNotAllowed, "method not allowed")
}
return
}
// 常规CRUD操作
switch r.Method {
case http.MethodGet:
// 安全设置alert_id到查询参数
query := make(url.Values)
for k, v := range r.URL.Query() {
query[k] = v
}
query.Set("alert_id", alertID)
r.URL.RawQuery = query.Encode()
a.alertHandler.GetAlert(w, r)
case http.MethodPut:
query := make(url.Values)
for k, v := range r.URL.Query() {
query[k] = v
}
query.Set("alert_id", alertID)
r.URL.RawQuery = query.Encode()
a.alertHandler.UpdateAlert(w, r)
case http.MethodDelete:
query := make(url.Values)
for k, v := range r.URL.Query() {
query[k] = v
}
query.Set("alert_id", alertID)
r.URL.RawQuery = query.Encode()
a.alertHandler.DeleteAlert(w, r)
default:
writeError(w, http.StatusMethodNotAllowed, CodeMethodNotAllowed, "method not allowed")
}
}