feat: add Sora admin page and integrate DB/Redis Prometheus metrics
Some checks failed
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled

- Create SoraAdminView with overview, user stats, and generations tabs
- Add /admin/sora route for Sora management
- Add i18n support (zh/en) for Sora admin page
- Extract Prometheus metrics to prommetrics package to avoid import cycles
- Integrate SetDBConnections/SetRedisConnections in OpsMetricsCollector
This commit is contained in:
User
2026-04-16 12:01:12 +08:00
parent 7fa795e6a4
commit c4007afe6b
7 changed files with 691 additions and 135 deletions

View File

@@ -2,12 +2,11 @@ package routes
import (
"net/http"
"strconv"
"sync"
"time"
"github.com/Wei-Shaw/sub2api/internal/prommetrics"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
@@ -20,125 +19,8 @@ type HealthChecker interface {
var (
healthChecker HealthChecker
healthCheckerOnce sync.Once
// Prometheus metrics
prometheusRegistry = prometheus.NewRegistry()
// Custom metrics
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "sub2api_http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "path", "status"},
)
httpRequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "sub2api_http_request_duration_seconds",
Help: "HTTP request duration in seconds",
Buckets: prometheus.DefBuckets,
},
[]string{"method", "path"},
)
dbConnectionsActive = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_db_connections_active",
Help: "Number of active database connections",
},
)
dbConnectionsIdle = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_db_connections_idle",
Help: "Number of idle database connections",
},
)
redisConnectionsTotal = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_redis_connections_total",
Help: "Total number of Redis connections",
},
)
redisConnectionsIdle = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_redis_connections_idle",
Help: "Number of idle Redis connections",
},
)
accountActiveTotal = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_accounts_active_total",
Help: "Total number of active accounts",
},
)
requestQueueDepth = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_request_queue_depth",
Help: "Current request queue depth",
},
)
opsHealthScore = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_ops_health_score",
Help: "Overall system health score (0-100)",
},
)
errorRate = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_error_rate",
Help: "Current error rate",
},
)
successRate = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_success_rate",
Help: "Current success rate",
},
)
qps = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_qps",
Help: "Queries per second",
},
)
tps = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "sub2api_tps",
Help: "Tokens per second",
},
)
)
func init() {
// Register custom metrics
prometheusRegistry.MustRegister(
httpRequestsTotal,
httpRequestDuration,
dbConnectionsActive,
dbConnectionsIdle,
redisConnectionsTotal,
redisConnectionsIdle,
accountActiveTotal,
requestQueueDepth,
opsHealthScore,
errorRate,
successRate,
qps,
tps,
)
}
// SetHealthChecker sets the health checker instance (called during app initialization)
func SetHealthChecker(checker HealthChecker) {
healthCheckerOnce.Do(func() {
@@ -157,8 +39,8 @@ func RegisterCommonRoutes(r *gin.Engine) {
// Liveness check - for Kubernetes liveness probe
r.GET("/live", livenessHandler)
// Prometheus metrics endpoint
r.GET("/metrics", gin.WrapH(promhttp.HandlerFor(prometheusRegistry, promhttp.HandlerOpts{})))
// Prometheus metrics endpoint - use the shared registry from prommetrics package
r.GET("/metrics", gin.WrapH(promhttp.HandlerFor(prommetrics.Registry, promhttp.HandlerOpts{})))
// Claude Code telemetry logs (ignore, return 200 directly)
r.POST("/api/event_logging/batch", func(c *gin.Context) {
@@ -275,57 +157,54 @@ func livenessHandler(c *gin.Context) {
})
}
// Prometheus metric update functions
// Prometheus metric update functions - delegate to prommetrics package
// RecordHTTPRequest records an HTTP request for Prometheus metrics
func RecordHTTPRequest(method, path string, statusCode int, duration time.Duration) {
httpRequestsTotal.WithLabelValues(method, path, strconv.Itoa(statusCode)).Inc()
httpRequestDuration.WithLabelValues(method, path).Observe(duration.Seconds())
prommetrics.RecordHTTPRequest(method, path, statusCode, duration)
}
// SetDBConnections sets database connection metrics
func SetDBConnections(active, idle int) {
dbConnectionsActive.Set(float64(active))
dbConnectionsIdle.Set(float64(idle))
prommetrics.SetDBConnections(active, idle)
}
// SetRedisConnections sets Redis connection metrics
func SetRedisConnections(total, idle int) {
redisConnectionsTotal.Set(float64(total))
redisConnectionsIdle.Set(float64(idle))
prommetrics.SetRedisConnections(total, idle)
}
// SetActiveAccounts sets the active accounts count
func SetActiveAccounts(count int) {
accountActiveTotal.Set(float64(count))
prommetrics.SetActiveAccounts(count)
}
// SetRequestQueueDepth sets the request queue depth
func SetRequestQueueDepth(depth int) {
requestQueueDepth.Set(float64(depth))
prommetrics.SetRequestQueueDepth(depth)
}
// SetOpsHealthScore sets the overall health score
func SetOpsHealthScore(score int) {
opsHealthScore.Set(float64(score))
prommetrics.SetOpsHealthScore(score)
}
// SetErrorRate sets the error rate
func SetErrorRate(rate float64) {
errorRate.Set(rate)
prommetrics.SetErrorRate(rate)
}
// SetSuccessRate sets the success rate
func SetSuccessRate(rate float64) {
successRate.Set(rate)
prommetrics.SetSuccessRate(rate)
}
// SetQPS sets queries per second
func SetQPS(value float64) {
qps.Set(value)
prommetrics.SetQPS(value)
}
// SetTPS sets tokens per second
func SetTPS(value float64) {
tps.Set(value)
prommetrics.SetTPS(value)
}