mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
Add admin endpoints to list and revoke child keys under a master. Standardize OpenAPI responses to use ResponseEnvelope with MapData for error payloads, and regenerate swagger specs accordingly.
144 lines
4.1 KiB
Go
144 lines
4.1 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/ez-api/ez-api/internal/model"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type APIKeyStatsSummaryResponse struct {
|
|
TotalRequests int64 `json:"total_requests"`
|
|
SuccessRequests int64 `json:"success_requests"`
|
|
FailureRequests int64 `json:"failure_requests"`
|
|
SuccessRate float64 `json:"success_rate"`
|
|
FailureRate float64 `json:"failure_rate"`
|
|
}
|
|
|
|
// GetAPIKeyStatsSummary godoc
|
|
// @Summary APIKey stats summary (admin)
|
|
// @Description Aggregate APIKey success/failure stats across all provider groups
|
|
// @Tags admin
|
|
// @Produce json
|
|
// @Security AdminAuth
|
|
// @Param since query int false "Start time (unix seconds)"
|
|
// @Param until query int false "End time (unix seconds)"
|
|
// @Success 200 {object} ResponseEnvelope{data=APIKeyStatsSummaryResponse}
|
|
// @Failure 500 {object} ResponseEnvelope{data=MapData}
|
|
// @Router /admin/apikey-stats/summary [get]
|
|
func (h *AdminHandler) GetAPIKeyStatsSummary(c *gin.Context) {
|
|
if h == nil || h.db == nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
|
|
return
|
|
}
|
|
|
|
// Parse optional time range parameters
|
|
var sinceTime, untilTime *time.Time
|
|
if sinceStr := c.Query("since"); sinceStr != "" {
|
|
if ts, err := strconv.ParseInt(sinceStr, 10, 64); err == nil {
|
|
t := time.Unix(ts, 0).UTC()
|
|
sinceTime = &t
|
|
}
|
|
}
|
|
if untilStr := c.Query("until"); untilStr != "" {
|
|
if ts, err := strconv.ParseInt(untilStr, 10, 64); err == nil {
|
|
t := time.Unix(ts, 0).UTC()
|
|
untilTime = &t
|
|
}
|
|
}
|
|
|
|
// If time range specified, query from LogRecord table
|
|
if sinceTime != nil || untilTime != nil {
|
|
h.getAPIKeyStatsFromLogs(c, sinceTime, untilTime)
|
|
return
|
|
}
|
|
|
|
// Default: use aggregated stats from APIKey table (all-time)
|
|
var totals struct {
|
|
TotalRequests int64 `json:"total_requests"`
|
|
SuccessRequests int64 `json:"success_requests"`
|
|
FailureRequests int64 `json:"failure_requests"`
|
|
}
|
|
|
|
if err := h.db.Model(&model.APIKey{}).
|
|
Select("COALESCE(SUM(total_requests),0) as total_requests, COALESCE(SUM(success_requests),0) as success_requests").
|
|
Scan(&totals).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to aggregate api key stats", "details": err.Error()})
|
|
return
|
|
}
|
|
|
|
total := totals.TotalRequests
|
|
success := totals.SuccessRequests
|
|
failure := total - success
|
|
if failure < 0 {
|
|
failure = 0
|
|
}
|
|
|
|
var successRate float64
|
|
var failureRate float64
|
|
if total > 0 {
|
|
successRate = float64(success) / float64(total)
|
|
failureRate = float64(failure) / float64(total)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, APIKeyStatsSummaryResponse{
|
|
TotalRequests: total,
|
|
SuccessRequests: success,
|
|
FailureRequests: failure,
|
|
SuccessRate: successRate,
|
|
FailureRate: failureRate,
|
|
})
|
|
}
|
|
|
|
// getAPIKeyStatsFromLogs calculates stats from log records for a specific time range
|
|
func (h *AdminHandler) getAPIKeyStatsFromLogs(c *gin.Context, sinceTime, untilTime *time.Time) {
|
|
q := h.logBaseQuery()
|
|
|
|
if sinceTime != nil {
|
|
q = q.Where("created_at >= ?", *sinceTime)
|
|
}
|
|
if untilTime != nil {
|
|
q = q.Where("created_at <= ?", *untilTime)
|
|
}
|
|
|
|
// Only count requests that went through a provider (provider_id > 0)
|
|
q = q.Where("provider_id > 0")
|
|
|
|
var result struct {
|
|
TotalRequests int64
|
|
SuccessRequests int64
|
|
}
|
|
|
|
if err := q.Select(`
|
|
COUNT(*) as total_requests,
|
|
SUM(CASE WHEN status_code >= 200 AND status_code < 400 THEN 1 ELSE 0 END) as success_requests
|
|
`).Scan(&result).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to aggregate api key stats from logs", "details": err.Error()})
|
|
return
|
|
}
|
|
|
|
total := result.TotalRequests
|
|
success := result.SuccessRequests
|
|
failure := total - success
|
|
if failure < 0 {
|
|
failure = 0
|
|
}
|
|
|
|
var successRate float64
|
|
var failureRate float64
|
|
if total > 0 {
|
|
successRate = float64(success) / float64(total)
|
|
failureRate = float64(failure) / float64(total)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, APIKeyStatsSummaryResponse{
|
|
TotalRequests: total,
|
|
SuccessRequests: success,
|
|
FailureRequests: failure,
|
|
SuccessRate: successRate,
|
|
FailureRate: failureRate,
|
|
})
|
|
}
|