Gateway: - remote_runtime.go: P3-C-08 从请求上下文透传 X-Request-Id 到 platform-token-runtime Supply-api: - 新建 internal/metrics/metrics.go: HTTP请求计数/latency/token发布/worker queue指标 (Prometheus-text) - 新建 internal/metrics/metrics_test.go: 6个测试覆盖 - bootstrap.go: 注册 /metrics (P3-C-01/04)、/health、/healthz 别名 (P3-C-05) Platform-token-runtime: - bootstrap.go: 添加 /health 和 /livez 别名 (P3-C-05) 三服务 /metrics 统一为 text/plain; version=0.0.4 三服务 /health 端点统一别名 Gateway → platform-token-runtime 透传 trace ID
129 lines
3.5 KiB
Go
129 lines
3.5 KiB
Go
package app
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/jackc/pgx/v5/pgxpool"
|
||
|
||
"lijiaoqiao/platform-token-runtime/internal/auth/service"
|
||
"lijiaoqiao/platform-token-runtime/internal/httpapi"
|
||
met "lijiaoqiao/platform-token-runtime/internal/metrics"
|
||
)
|
||
|
||
type Config struct {
|
||
Addr string
|
||
Env string
|
||
RuntimeStore service.RuntimeStore
|
||
AuditStore service.AuditStore
|
||
Now func() time.Time
|
||
}
|
||
|
||
var newPostgresStoreBundle = func(ctx context.Context, databaseURL string) (service.RuntimeStore, service.AuditStore, func(), error) {
|
||
if ctx == nil {
|
||
ctx = context.Background()
|
||
}
|
||
pool, err := pgxpool.New(ctx, strings.TrimSpace(databaseURL))
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
if err := pool.Ping(ctx); err != nil {
|
||
pool.Close()
|
||
return nil, nil, nil, err
|
||
}
|
||
return service.NewPostgresRuntimeStore(pool), service.NewPostgresAuditStore(pool), pool.Close, nil
|
||
}
|
||
|
||
func BuildPostgresStores(ctx context.Context, databaseURL string) (service.RuntimeStore, service.AuditStore, func(), error) {
|
||
if strings.TrimSpace(databaseURL) == "" {
|
||
return nil, nil, nil, fmt.Errorf("token runtime database url is required")
|
||
}
|
||
return newPostgresStoreBundle(ctx, databaseURL)
|
||
}
|
||
|
||
func BuildRuntime(cfg Config) (*service.InMemoryTokenRuntime, service.AuditStore, error) {
|
||
now := cfg.Now
|
||
if now == nil {
|
||
now = time.Now
|
||
}
|
||
|
||
env := strings.ToLower(strings.TrimSpace(cfg.Env))
|
||
runtimeStore := cfg.RuntimeStore
|
||
auditStore := cfg.AuditStore
|
||
|
||
switch env {
|
||
case "", "dev":
|
||
if runtimeStore == nil {
|
||
runtimeStore = service.NewInMemoryRuntimeStore()
|
||
}
|
||
if auditStore == nil {
|
||
auditStore = service.NewMemoryAuditStore()
|
||
}
|
||
case "prod", "staging":
|
||
if runtimeStore == nil {
|
||
return nil, nil, fmt.Errorf("runtime store is required in %s", env)
|
||
}
|
||
if auditStore == nil {
|
||
return nil, nil, fmt.Errorf("audit store is required in %s", env)
|
||
}
|
||
default:
|
||
return nil, nil, fmt.Errorf("unsupported TOKEN_RUNTIME_ENV %q", cfg.Env)
|
||
}
|
||
|
||
return service.NewInMemoryTokenRuntimeWithStore(now, runtimeStore), auditStore, nil
|
||
}
|
||
|
||
func BuildServer(cfg Config) (*http.Server, error) {
|
||
now := cfg.Now
|
||
if now == nil {
|
||
now = time.Now
|
||
}
|
||
|
||
addr := strings.TrimSpace(cfg.Addr)
|
||
if addr == "" {
|
||
addr = ":18081"
|
||
}
|
||
|
||
runtime, auditor, err := BuildRuntime(cfg)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
api := httpapi.NewTokenAPI(runtime, auditor, now)
|
||
mux := http.NewServeMux()
|
||
mux.HandleFunc("/actuator/health", func(w http.ResponseWriter, _ *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusOK)
|
||
_, _ = w.Write([]byte(`{"status":"UP"}`))
|
||
})
|
||
// P3-C-05: /health 和 /livez 别名(统一路径,对齐 gateway/supply-api)
|
||
mux.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusOK)
|
||
_, _ = w.Write([]byte(`{"status":"UP"}`))
|
||
})
|
||
mux.HandleFunc("/livez", func(w http.ResponseWriter, _ *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusOK)
|
||
_, _ = w.Write([]byte(`{"status":"UP"}`))
|
||
})
|
||
// P3-B: /metrics 端点(Prometheus-text 格式)
|
||
mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
|
||
_, _ = w.Write([]byte(met.Export()))
|
||
})
|
||
api.Register(mux)
|
||
|
||
return &http.Server{
|
||
Addr: addr,
|
||
Handler: mux,
|
||
ReadHeaderTimeout: 5 * time.Second,
|
||
ReadTimeout: 10 * time.Second,
|
||
WriteTimeout: 15 * time.Second,
|
||
IdleTimeout: 30 * time.Second,
|
||
}, nil
|
||
}
|