feat(api): add admin master key listing/revoke

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.
This commit is contained in:
zenfun
2026-01-10 01:10:36 +08:00
parent ac6a1858cf
commit 5349c9c833
27 changed files with 9407 additions and 1134 deletions

View File

@@ -343,6 +343,8 @@ func main() {
adminGroup.POST("/masters/batch", adminHandler.BatchMasters)
adminGroup.POST("/masters/:id/manage", adminHandler.ManageMaster)
adminGroup.POST("/masters/:id/keys", adminHandler.IssueChildKeyForMaster)
adminGroup.GET("/masters/:id/keys", adminHandler.ListKeysForMaster)
adminGroup.DELETE("/masters/:id/keys/:key_id", adminHandler.DeleteKeyForMaster)
adminGroup.GET("/masters/:id/access", handler.GetMasterAccess)
adminGroup.PUT("/masters/:id/access", handler.UpdateMasterAccess)
adminGroup.GET("/keys/:id/access", handler.GetKeyAccess)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -30,9 +30,9 @@ type UpdateAccessRequest struct {
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Success 200 {object} ResponseEnvelope{data=AccessResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id}/access [get]
func (h *Handler) GetMasterAccess(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -62,9 +62,9 @@ func (h *Handler) GetMasterAccess(c *gin.Context) {
// @Param id path int true "Master ID"
// @Param request body UpdateAccessRequest true "Access settings"
// @Success 200 {object} ResponseEnvelope{data=AccessResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id}/access [put]
func (h *Handler) UpdateMasterAccess(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -145,9 +145,9 @@ func (h *Handler) UpdateMasterAccess(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Key ID"
// @Success 200 {object} ResponseEnvelope{data=AccessResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/keys/{id}/access [get]
func (h *Handler) GetKeyAccess(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -175,9 +175,9 @@ func (h *Handler) GetKeyAccess(c *gin.Context) {
// @Param id path int true "Key ID"
// @Param request body UpdateAccessRequest true "Access settings"
// @Success 200 {object} ResponseEnvelope{data=AccessResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/keys/{id}/access [put]
func (h *Handler) UpdateKeyAccess(c *gin.Context) {
id, ok := parseUintParam(c, "id")

View File

@@ -54,9 +54,9 @@ type CreateMasterRequest struct {
// @Produce json
// @Security AdminAuth
// @Param master body CreateMasterRequest true "Master Info"
// @Success 201 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 201 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters [post]
func (h *AdminHandler) CreateMaster(c *gin.Context) {
var req CreateMasterRequest
@@ -136,7 +136,7 @@ func toMasterView(m model.Master) MasterView {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by name/group"
// @Success 200 {object} ResponseEnvelope{data=[]MasterView}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters [get]
func (h *AdminHandler) ListMasters(c *gin.Context) {
var masters []model.Master
@@ -163,9 +163,9 @@ func (h *AdminHandler) ListMasters(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Success 200 {object} ResponseEnvelope{data=MasterView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id} [get]
func (h *AdminHandler) GetMaster(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
@@ -209,9 +209,9 @@ type UpdateMasterRequest struct {
// @Param id path int true "Master ID"
// @Param request body UpdateMasterRequest true "Update payload"
// @Success 200 {object} ResponseEnvelope{data=MasterView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id} [put]
func (h *AdminHandler) UpdateMaster(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
@@ -314,9 +314,9 @@ type ManageMasterRequest struct {
// @Param id path int true "Master ID"
// @Param request body ManageMasterRequest true "Action"
// @Success 200 {object} ResponseEnvelope{data=MasterView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id}/manage [post]
func (h *AdminHandler) ManageMaster(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
@@ -370,10 +370,10 @@ func (h *AdminHandler) ManageMaster(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id} [delete]
func (h *AdminHandler) DeleteMaster(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
@@ -404,11 +404,11 @@ func (h *AdminHandler) DeleteMaster(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Param request body IssueChildKeyRequest true "Key Request"
// @Success 201 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 403 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 201 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 403 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id}/keys [post]
func (h *AdminHandler) IssueChildKeyForMaster(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
@@ -477,3 +477,89 @@ func (h *AdminHandler) IssueChildKeyForMaster(c *gin.Context) {
"issued_by": key.IssuedBy,
})
}
// ListKeysForMaster godoc
// @Summary List child keys for a master
// @Description List child keys issued under a master (admin view)
// @Tags admin
// @Produce json
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Param page query int false "page (1-based)"
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by group/scopes/namespaces/status"
// @Success 200 {object} ResponseEnvelope{data=[]TokenView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Router /admin/masters/{id}/keys [get]
func (h *AdminHandler) ListKeysForMaster(c *gin.Context) {
masterID, ok := parseUintParam(c, "id")
if !ok {
return
}
var keys []model.Key
q := h.db.Model(&model.Key{}).Where("master_id = ?", masterID).Order("id desc")
query := parseListQuery(c)
q = applyListSearch(q, query.Search, `"group"`, "scopes", "default_namespace", "namespaces", "status")
q = applyListPagination(q, query)
if err := q.Find(&keys).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list tokens", "details": err.Error()})
return
}
out := make([]TokenView, 0, len(keys))
for _, k := range keys {
out = append(out, toTokenView(k))
}
c.JSON(http.StatusOK, out)
}
// DeleteKeyForMaster godoc
// @Summary Delete (revoke) child key
// @Description Suspends a child key under the specified master
// @Tags admin
// @Produce json
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Param key_id path int true "Token ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Router /admin/masters/{id}/keys/{key_id} [delete]
func (h *AdminHandler) DeleteKeyForMaster(c *gin.Context) {
masterID, ok := parseUintParam(c, "id")
if !ok {
return
}
keyID, ok := parseUintParam(c, "key_id")
if !ok {
return
}
var k model.Key
if err := h.db.Where("master_id = ? AND id = ?", masterID, keyID).First(&k).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "token not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load token", "details": err.Error()})
return
}
if err := h.db.Model(&k).Update("status", "suspended").Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to revoke token", "details": err.Error()})
return
}
if err := h.db.Where("master_id = ? AND id = ?", masterID, keyID).First(&k).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to reload token", "details": err.Error()})
return
}
if err := h.syncService.SyncKey(&k); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to sync token", "details": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"status": "revoked"})
}

View File

@@ -91,7 +91,7 @@ type ListAlertsResponse struct {
// @Param severity query string false "filter by severity (info, warning, critical)"
// @Param type query string false "filter by type (rate_limit, error_spike, quota_exceeded, key_disabled, key_expired, provider_down, traffic_spike)"
// @Success 200 {object} ResponseEnvelope{data=ListAlertsResponse}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts [get]
func (h *AlertHandler) ListAlerts(c *gin.Context) {
limit, offset := parseLimitOffset(c)
@@ -141,8 +141,8 @@ func (h *AlertHandler) ListAlerts(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Alert ID"
// @Success 200 {object} ResponseEnvelope{data=AlertView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/{id} [get]
func (h *AlertHandler) GetAlert(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -181,8 +181,8 @@ type CreateAlertRequest struct {
// @Security AdminAuth
// @Param request body CreateAlertRequest true "Alert data"
// @Success 201 {object} ResponseEnvelope{data=AlertView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts [post]
func (h *AlertHandler) CreateAlert(c *gin.Context) {
var req CreateAlertRequest
@@ -248,9 +248,9 @@ type AckAlertRequest struct {
// @Param id path int true "Alert ID"
// @Param request body AckAlertRequest false "Ack data"
// @Success 200 {object} ResponseEnvelope{data=AlertView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/{id}/ack [post]
func (h *AlertHandler) AcknowledgeAlert(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -295,9 +295,9 @@ func (h *AlertHandler) AcknowledgeAlert(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Alert ID"
// @Success 200 {object} ResponseEnvelope{data=AlertView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/{id}/resolve [post]
func (h *AlertHandler) ResolveAlert(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -337,10 +337,10 @@ func (h *AlertHandler) ResolveAlert(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "Alert ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/{id} [delete]
func (h *AlertHandler) DismissAlert(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -380,7 +380,7 @@ type AlertStats struct {
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=AlertStats}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/stats [get]
func (h *AlertHandler) GetAlertStats(c *gin.Context) {
var total, active, acknowledged, resolved, critical, warning, info int64
@@ -436,7 +436,7 @@ func toAlertThresholdView(cfg model.AlertThresholdConfig) AlertThresholdView {
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=AlertThresholdView}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/thresholds [get]
func (h *AlertHandler) GetAlertThresholds(c *gin.Context) {
cfg, err := h.loadThresholdConfig()
@@ -467,8 +467,8 @@ type UpdateAlertThresholdsRequest struct {
// @Security AdminAuth
// @Param request body UpdateAlertThresholdsRequest true "Threshold configuration"
// @Success 200 {object} ResponseEnvelope{data=AlertThresholdView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/alerts/thresholds [put]
func (h *AlertHandler) UpdateAlertThresholds(c *gin.Context) {
var req UpdateAlertThresholdsRequest

View File

@@ -18,8 +18,8 @@ import (
// @Security AdminAuth
// @Param key body dto.APIKeyDTO true "API key payload"
// @Success 201 {object} ResponseEnvelope{data=model.APIKey}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/api-keys [post]
func (h *Handler) CreateAPIKey(c *gin.Context) {
var req dto.APIKeyDTO
@@ -101,7 +101,7 @@ func (h *Handler) CreateAPIKey(c *gin.Context) {
// @Param group_id query int false "filter by group_id"
// @Param status query string false "filter by status (active, suspended, auto_disabled, manual_disabled)"
// @Success 200 {object} ResponseEnvelope{data=[]model.APIKey}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/api-keys [get]
func (h *Handler) ListAPIKeys(c *gin.Context) {
var keys []model.APIKey
@@ -129,9 +129,9 @@ func (h *Handler) ListAPIKeys(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "APIKey ID"
// @Success 200 {object} ResponseEnvelope{data=model.APIKey}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/api-keys/{id} [get]
func (h *Handler) GetAPIKey(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -156,9 +156,9 @@ func (h *Handler) GetAPIKey(c *gin.Context) {
// @Param id path int true "APIKey ID"
// @Param key body dto.APIKeyDTO true "API key payload"
// @Success 200 {object} ResponseEnvelope{data=model.APIKey}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/api-keys/{id} [put]
func (h *Handler) UpdateAPIKey(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -275,10 +275,10 @@ func (h *Handler) UpdateAPIKey(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "APIKey ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/api-keys/{id} [delete]
func (h *Handler) DeleteAPIKey(c *gin.Context) {
id, ok := parseUintParam(c, "id")

View File

@@ -26,7 +26,7 @@ type APIKeyStatsSummaryResponse struct {
// @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=gin.H}
// @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 {

View File

@@ -172,7 +172,7 @@ type WhoamiResponse struct {
// @Security AdminAuth
// @Security MasterAuth
// @Success 200 {object} ResponseEnvelope{data=WhoamiResponse}
// @Failure 401 {object} ResponseEnvelope{data=gin.H} "Invalid or missing token"
// @Failure 401 {object} ResponseEnvelope{data=MapData} "Invalid or missing token"
// @Router /auth/whoami [get]
func (h *AuthHandler) Whoami(c *gin.Context) {
authHeader := c.GetHeader("Authorization")

View File

@@ -58,8 +58,8 @@ func isAllowedStatus(raw string, allowed ...string) bool {
// @Security AdminAuth
// @Param request body BatchActionRequest true "Batch payload"
// @Success 200 {object} ResponseEnvelope{data=BatchResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/batch [post]
func (h *AdminHandler) BatchMasters(c *gin.Context) {
var req BatchActionRequest
@@ -113,8 +113,8 @@ func (h *AdminHandler) BatchMasters(c *gin.Context) {
// @Security AdminAuth
// @Param request body BatchActionRequest true "Batch payload"
// @Success 200 {object} ResponseEnvelope{data=BatchResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/api-keys/batch [post]
func (h *Handler) BatchAPIKeys(c *gin.Context) {
var req BatchActionRequest
@@ -197,8 +197,8 @@ func (h *Handler) BatchAPIKeys(c *gin.Context) {
// @Security AdminAuth
// @Param request body BatchActionRequest true "Batch payload"
// @Success 200 {object} ResponseEnvelope{data=BatchResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/models/batch [post]
func (h *Handler) BatchModels(c *gin.Context) {
var req BatchActionRequest
@@ -249,8 +249,8 @@ func (h *Handler) BatchModels(c *gin.Context) {
// @Security AdminAuth
// @Param request body BatchActionRequest true "Batch payload"
// @Success 200 {object} ResponseEnvelope{data=BatchResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/bindings/batch [post]
func (h *Handler) BatchBindings(c *gin.Context) {
var req BatchActionRequest

View File

@@ -20,8 +20,8 @@ import (
// @Security AdminAuth
// @Param binding body dto.BindingDTO true "Binding Info"
// @Success 201 {object} ResponseEnvelope{data=model.Binding}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/bindings [post]
func (h *Handler) CreateBinding(c *gin.Context) {
var req dto.BindingDTO
@@ -85,7 +85,7 @@ func (h *Handler) CreateBinding(c *gin.Context) {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by namespace/public_model"
// @Success 200 {object} ResponseEnvelope{data=[]model.Binding}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/bindings [get]
func (h *Handler) ListBindings(c *gin.Context) {
var out []model.Binding
@@ -110,9 +110,9 @@ func (h *Handler) ListBindings(c *gin.Context) {
// @Param id path int true "Binding ID"
// @Param binding body dto.BindingDTO true "Binding Info"
// @Success 200 {object} ResponseEnvelope{data=model.Binding}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/bindings/{id} [put]
func (h *Handler) UpdateBinding(c *gin.Context) {
idParam := c.Param("id")
@@ -181,9 +181,9 @@ func (h *Handler) UpdateBinding(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Binding ID"
// @Success 200 {object} ResponseEnvelope{data=model.Binding}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/bindings/{id} [get]
func (h *Handler) GetBinding(c *gin.Context) {
idParam := c.Param("id")
@@ -207,10 +207,10 @@ func (h *Handler) GetBinding(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "Binding ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/bindings/{id} [delete]
func (h *Handler) DeleteBinding(c *gin.Context) {
idParam := c.Param("id")

View File

@@ -188,8 +188,8 @@ type DashboardSummaryResponse struct {
// @Param until query int false "unix seconds"
// @Param include_trends query bool false "include trend data comparing to previous period"
// @Success 200 {object} ResponseEnvelope{data=DashboardSummaryResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/dashboard/summary [get]
func (h *DashboardHandler) GetSummary(c *gin.Context) {
rng, err := parseStatsRange(c)

View File

@@ -37,8 +37,8 @@ func NewFeatureHandler(rdb *redis.Client) *FeatureHandler {
// @Tags admin
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/features [get]
func (h *FeatureHandler) ListFeatures(c *gin.Context) {
if h.rdb == nil {
@@ -75,9 +75,9 @@ type UpdateFeaturesRequest map[string]any
// @Produce json
// @Security AdminAuth
// @Param request body object true "Feature map"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/features [put]
func (h *FeatureHandler) UpdateFeatures(c *gin.Context) {
if h.rdb == nil {

View File

@@ -63,8 +63,8 @@ func (h *Handler) logBaseQuery() *gorm.DB {
// @Security AdminAuth
// @Param model body dto.ModelDTO true "Model Info"
// @Success 201 {object} ResponseEnvelope{data=model.Model}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/models [post]
func (h *Handler) CreateModel(c *gin.Context) {
var req dto.ModelDTO
@@ -124,7 +124,7 @@ func (h *Handler) CreateModel(c *gin.Context) {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by name/kind"
// @Success 200 {object} ResponseEnvelope{data=[]model.Model}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/models [get]
func (h *Handler) ListModels(c *gin.Context) {
var models []model.Model
@@ -149,9 +149,9 @@ func (h *Handler) ListModels(c *gin.Context) {
// @Param id path int true "Model ID"
// @Param model body dto.ModelDTO true "Model Info"
// @Success 200 {object} ResponseEnvelope{data=model.Model}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/models/{id} [put]
func (h *Handler) UpdateModel(c *gin.Context) {
idParam := c.Param("id")
@@ -222,10 +222,10 @@ func (h *Handler) UpdateModel(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "Model ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/models/{id} [delete]
func (h *Handler) DeleteModel(c *gin.Context) {
idParam := c.Param("id")
@@ -260,8 +260,8 @@ func (h *Handler) DeleteModel(c *gin.Context) {
// @Tags admin
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/sync/snapshot [post]
func (h *Handler) SyncSnapshot(c *gin.Context) {
if err := h.sync.SyncAll(h.db); err != nil {
@@ -278,8 +278,8 @@ func (h *Handler) SyncSnapshot(c *gin.Context) {
// @Accept json
// @Produce json
// @Param log body model.LogRecord true "Log Record"
// @Success 202 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Success 202 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Router /logs [post]
func (h *Handler) IngestLog(c *gin.Context) {
var rec model.LogRecord

View File

@@ -47,9 +47,9 @@ type apiKeyStatsFlushEntry struct {
// @Accept json
// @Produce json
// @Param request body statsFlushRequest true "Stats to flush"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /internal/stats/flush [post]
func (h *InternalHandler) FlushStats(c *gin.Context) {
if h == nil || h.db == nil {
@@ -124,9 +124,9 @@ func (h *InternalHandler) FlushStats(c *gin.Context) {
// @Accept json
// @Produce json
// @Param request body apiKeyStatsFlushRequest true "Stats to flush"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /internal/apikey-stats/flush [post]
func (h *InternalHandler) FlushAPIKeyStats(c *gin.Context) {
if h == nil || h.db == nil {
@@ -288,8 +288,8 @@ const alertDeduplicationCooldown = 5 * time.Minute
// @Produce json
// @Param request body reportAlertsRequest true "Alerts to report"
// @Success 200 {object} ResponseEnvelope{data=reportAlertsResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /internal/alerts/report [post]
func (h *InternalHandler) ReportAlerts(c *gin.Context) {
if h == nil || h.db == nil {

View File

@@ -90,9 +90,9 @@ func toIPBanView(ban *model.IPBan) IPBanView {
// @Security AdminAuth
// @Param ban body CreateIPBanRequest true "IP Ban Info"
// @Success 201 {object} ResponseEnvelope{data=IPBanView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 409 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 409 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/ip-bans [post]
func (h *IPBanHandler) Create(c *gin.Context) {
var req CreateIPBanRequest
@@ -140,7 +140,7 @@ func (h *IPBanHandler) Create(c *gin.Context) {
// @Security AdminAuth
// @Param status query string false "Filter by status (active, expired)"
// @Success 200 {object} ResponseEnvelope{data=[]IPBanView}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/ip-bans [get]
func (h *IPBanHandler) List(c *gin.Context) {
status := c.Query("status")
@@ -168,8 +168,8 @@ func (h *IPBanHandler) List(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "IP Ban ID"
// @Success 200 {object} ResponseEnvelope{data=IPBanView}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/ip-bans/{id} [get]
func (h *IPBanHandler) Get(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
@@ -201,10 +201,10 @@ func (h *IPBanHandler) Get(c *gin.Context) {
// @Param id path int true "IP Ban ID"
// @Param ban body UpdateIPBanRequest true "IP Ban Update"
// @Success 200 {object} ResponseEnvelope{data=IPBanView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 409 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 409 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/ip-bans/{id} [put]
func (h *IPBanHandler) Update(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
@@ -250,8 +250,8 @@ func (h *IPBanHandler) Update(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "IP Ban ID"
// @Success 204
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/ip-bans/{id} [delete]
func (h *IPBanHandler) Delete(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)

View File

@@ -168,7 +168,7 @@ func (h *MasterHandler) masterLogBase(masterID uint) (*gorm.DB, error) {
// @Param model query string false "model"
// @Param status_code query int false "status code"
// @Success 200 {object} ResponseEnvelope{data=ListLogsResponse}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/logs [get]
func (h *Handler) ListLogs(c *gin.Context) {
limit, offset := parseLimitOffset(c)
@@ -268,8 +268,8 @@ type DeleteLogsResponse struct {
// @Security AdminAuth
// @Param request body DeleteLogsRequest true "Delete filters"
// @Success 200 {object} ResponseEnvelope{data=DeleteLogsResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/logs [delete]
func (h *Handler) DeleteLogs(c *gin.Context) {
var req DeleteLogsRequest
@@ -359,8 +359,8 @@ func (h *Handler) deleteLogsBefore(cutoff time.Time, keyID uint, modelName strin
// @Param group_by query string false "group by dimension: model, day, month, hour, minute. Returns GroupedStatsResponse when specified." Enums(model, day, month, hour, minute)
// @Success 200 {object} ResponseEnvelope{data=LogStatsResponse} "Default aggregated stats (when group_by is not specified)"
// @Success 200 {object} ResponseEnvelope{data=GroupedStatsResponse} "Grouped stats (when group_by is specified)"
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/logs/stats [get]
func (h *Handler) LogStats(c *gin.Context) {
q := h.logBaseQuery()
@@ -768,8 +768,8 @@ func buildTrafficChartSeriesResponse(rows []trafficBucketRow, topN int, granular
// @Param until query int false "End time (unix seconds), defaults to now"
// @Param top_n query int false "Number of top models to return (1-20), defaults to 5"
// @Success 200 {object} ResponseEnvelope{data=TrafficChartResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/logs/stats/traffic-chart [get]
func (h *Handler) GetTrafficChart(c *gin.Context) {
// Parse granularity
@@ -870,8 +870,8 @@ func (h *Handler) GetTrafficChart(c *gin.Context) {
// @Param model query string false "model"
// @Param status_code query int false "status code"
// @Success 200 {object} ResponseEnvelope{data=ListMasterLogsResponse}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/logs [get]
func (h *MasterHandler) ListSelfLogs(c *gin.Context) {
master, exists := c.Get("master")
@@ -929,8 +929,8 @@ func (h *MasterHandler) ListSelfLogs(c *gin.Context) {
// @Param since query int false "unix seconds"
// @Param until query int false "unix seconds"
// @Success 200 {object} ResponseEnvelope{data=LogStatsResponse}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/logs/stats [get]
func (h *MasterHandler) GetSelfLogStats(c *gin.Context) {
master, exists := c.Get("master")

View File

@@ -14,7 +14,7 @@ import (
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=service.LogWebhookConfig}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/logs/webhook [get]
func (h *Handler) GetLogWebhookConfig(c *gin.Context) {
if h == nil || h.logWebhook == nil {
@@ -38,8 +38,8 @@ func (h *Handler) GetLogWebhookConfig(c *gin.Context) {
// @Security AdminAuth
// @Param request body service.LogWebhookConfig true "Webhook config"
// @Success 200 {object} ResponseEnvelope{data=service.LogWebhookConfig}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/logs/webhook [put]
func (h *Handler) UpdateLogWebhookConfig(c *gin.Context) {
if h == nil || h.logWebhook == nil {

View File

@@ -58,11 +58,11 @@ type IssueChildKeyRequest struct {
// @Produce json
// @Security MasterAuth
// @Param request body IssueChildKeyRequest true "Key Request"
// @Success 201 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 403 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 201 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 403 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/tokens [post]
func (h *MasterHandler) IssueChildKey(c *gin.Context) {
master, exists := c.Get("master")
@@ -146,7 +146,7 @@ func (h *MasterHandler) IssueChildKey(c *gin.Context) {
// @Produce json
// @Security MasterAuth
// @Success 200 {object} ResponseEnvelope{data=MasterView}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Router /v1/self [get]
func (h *MasterHandler) GetSelf(c *gin.Context) {
master, exists := c.Get("master")
@@ -220,8 +220,8 @@ func toTokenView(k model.Key) TokenView {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by group/scopes/namespaces/status"
// @Success 200 {object} ResponseEnvelope{data=[]TokenView}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/tokens [get]
func (h *MasterHandler) ListTokens(c *gin.Context) {
master, exists := c.Get("master")
@@ -255,10 +255,10 @@ func (h *MasterHandler) ListTokens(c *gin.Context) {
// @Security MasterAuth
// @Param id path int true "Token ID"
// @Success 200 {object} ResponseEnvelope{data=TokenView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/tokens/{id} [get]
func (h *MasterHandler) GetToken(c *gin.Context) {
master, exists := c.Get("master")
@@ -303,10 +303,10 @@ type UpdateTokenRequest struct {
// @Param id path int true "Token ID"
// @Param request body UpdateTokenRequest true "Update payload"
// @Success 200 {object} ResponseEnvelope{data=TokenView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/tokens/{id} [put]
func (h *MasterHandler) UpdateToken(c *gin.Context) {
master, exists := c.Get("master")
@@ -398,11 +398,11 @@ func (h *MasterHandler) UpdateToken(c *gin.Context) {
// @Produce json
// @Security MasterAuth
// @Param id path int true "Token ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/tokens/{id} [delete]
func (h *MasterHandler) DeleteToken(c *gin.Context) {
master, exists := c.Get("master")

View File

@@ -23,7 +23,7 @@ func NewModelRegistryHandler(reg *service.ModelRegistryService) *ModelRegistryHa
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=service.ModelRegistryStatus}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/model-registry/status [get]
func (h *ModelRegistryHandler) GetStatus(c *gin.Context) {
if h == nil || h.reg == nil {
@@ -51,8 +51,8 @@ type refreshModelRegistryRequest struct {
// @Security AdminAuth
// @Param body body refreshModelRegistryRequest false "optional override ref"
// @Success 200 {object} ResponseEnvelope{data=service.ModelRegistryCheckResult}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/model-registry/check [post]
func (h *ModelRegistryHandler) Check(c *gin.Context) {
if h == nil || h.reg == nil {
@@ -78,9 +78,9 @@ func (h *ModelRegistryHandler) Check(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param body body refreshModelRegistryRequest false "optional override ref"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/model-registry/refresh [post]
func (h *ModelRegistryHandler) Refresh(c *gin.Context) {
if h == nil || h.reg == nil {
@@ -103,8 +103,8 @@ func (h *ModelRegistryHandler) Refresh(c *gin.Context) {
// @Tags admin
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/model-registry/rollback [post]
func (h *ModelRegistryHandler) Rollback(c *gin.Context) {
if h == nil || h.reg == nil {

View File

@@ -23,8 +23,8 @@ type NamespaceRequest struct {
// @Security AdminAuth
// @Param namespace body NamespaceRequest true "Namespace payload"
// @Success 201 {object} ResponseEnvelope{data=model.Namespace}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/namespaces [post]
func (h *Handler) CreateNamespace(c *gin.Context) {
var req NamespaceRequest
@@ -65,7 +65,7 @@ func (h *Handler) CreateNamespace(c *gin.Context) {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by name/description"
// @Success 200 {object} ResponseEnvelope{data=[]model.Namespace}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/namespaces [get]
func (h *Handler) ListNamespaces(c *gin.Context) {
var out []model.Namespace
@@ -88,9 +88,9 @@ func (h *Handler) ListNamespaces(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "Namespace ID"
// @Success 200 {object} ResponseEnvelope{data=model.Namespace}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/namespaces/{id} [get]
func (h *Handler) GetNamespace(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -121,9 +121,9 @@ type UpdateNamespaceRequest struct {
// @Param id path int true "Namespace ID"
// @Param namespace body UpdateNamespaceRequest true "Update payload"
// @Success 200 {object} ResponseEnvelope{data=model.Namespace}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/namespaces/{id} [put]
func (h *Handler) UpdateNamespace(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -185,10 +185,10 @@ func (h *Handler) UpdateNamespace(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "Namespace ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/namespaces/{id} [delete]
func (h *Handler) DeleteNamespace(c *gin.Context) {
id, ok := parseUintParam(c, "id")

View File

@@ -49,7 +49,7 @@ func toOperationLogView(l model.OperationLog) OperationLogView {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by actor/method/path"
// @Success 200 {object} ResponseEnvelope{data=[]OperationLogView}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/operation-logs [get]
func (h *AdminHandler) ListOperationLogs(c *gin.Context) {
var rows []model.OperationLog

View File

@@ -20,8 +20,8 @@ import (
// @Security AdminAuth
// @Param group body dto.ProviderGroupDTO true "Provider group payload"
// @Success 201 {object} ResponseEnvelope{data=model.ProviderGroup}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/provider-groups [post]
func (h *Handler) CreateProviderGroup(c *gin.Context) {
var req dto.ProviderGroupDTO
@@ -79,7 +79,7 @@ func (h *Handler) CreateProviderGroup(c *gin.Context) {
// @Param limit query int false "limit (default 50, max 200)"
// @Param search query string false "search by name/type"
// @Success 200 {object} ResponseEnvelope{data=[]model.ProviderGroup}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/provider-groups [get]
func (h *Handler) ListProviderGroups(c *gin.Context) {
var groups []model.ProviderGroup
@@ -102,9 +102,9 @@ func (h *Handler) ListProviderGroups(c *gin.Context) {
// @Security AdminAuth
// @Param id path int true "ProviderGroup ID"
// @Success 200 {object} ResponseEnvelope{data=model.ProviderGroup}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/provider-groups/{id} [get]
func (h *Handler) GetProviderGroup(c *gin.Context) {
id, ok := parseUintParam(c, "id")
@@ -129,9 +129,9 @@ func (h *Handler) GetProviderGroup(c *gin.Context) {
// @Param id path int true "ProviderGroup ID"
// @Param group body dto.ProviderGroupDTO true "Provider group payload"
// @Success 200 {object} ResponseEnvelope{data=model.ProviderGroup}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/provider-groups/{id} [put]
func (h *Handler) UpdateProviderGroup(c *gin.Context) {
idParam := c.Param("id")
@@ -221,10 +221,10 @@ func (h *Handler) UpdateProviderGroup(c *gin.Context) {
// @Produce json
// @Security AdminAuth
// @Param id path int true "ProviderGroup ID"
// @Success 200 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Success 200 {object} ResponseEnvelope{data=MapData}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/provider-groups/{id} [delete]
func (h *Handler) DeleteProviderGroup(c *gin.Context) {
id, ok := parseUintParam(c, "id")

View File

@@ -45,9 +45,9 @@ func toMasterRealtimeView(stats service.MasterRealtimeSnapshot) *MasterRealtimeV
// @Security AdminAuth
// @Param id path int true "Master ID"
// @Success 200 {object} ResponseEnvelope{data=MasterRealtimeView}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 404 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 404 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/masters/{id}/realtime [get]
func (h *AdminHandler) GetMasterRealtime(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
@@ -88,8 +88,8 @@ func (h *AdminHandler) GetMasterRealtime(c *gin.Context) {
// @Produce json
// @Security MasterAuth
// @Success 200 {object} ResponseEnvelope{data=MasterRealtimeView}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/realtime [get]
func (h *MasterHandler) GetSelfRealtime(c *gin.Context) {
master, exists := c.Get("master")
@@ -136,7 +136,7 @@ type MasterRealtimeSummaryView struct {
// @Produce json
// @Security AdminAuth
// @Success 200 {object} ResponseEnvelope{data=SystemRealtimeView}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/realtime [get]
func (h *AdminHandler) GetAdminRealtime(c *gin.Context) {
if h.statsService == nil {

View File

@@ -14,3 +14,6 @@ type ResponseEnvelope struct {
TraceID string `json:"trace_id" example:"a1b2c3d4e5f6g7h8"`
Details any `json:"details,omitempty" swaggertype:"object"`
}
// MapData represents a generic JSON object for documentation purposes.
type MapData map[string]any

View File

@@ -42,9 +42,9 @@ type MasterUsageStatsResponse struct {
// @Param since query int false "unix seconds"
// @Param until query int false "unix seconds"
// @Success 200 {object} ResponseEnvelope{data=MasterUsageStatsResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 401 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 401 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /v1/stats [get]
func (h *MasterHandler) GetSelfStats(c *gin.Context) {
master, exists := c.Get("master")
@@ -148,8 +148,8 @@ type AdminUsageStatsResponse struct {
// @Param since query int false "unix seconds"
// @Param until query int false "unix seconds"
// @Success 200 {object} ResponseEnvelope{data=AdminUsageStatsResponse}
// @Failure 400 {object} ResponseEnvelope{data=gin.H}
// @Failure 500 {object} ResponseEnvelope{data=gin.H}
// @Failure 400 {object} ResponseEnvelope{data=MapData}
// @Failure 500 {object} ResponseEnvelope{data=MapData}
// @Router /admin/stats [get]
func (h *AdminHandler) GetAdminStats(c *gin.Context) {
rng, err := parseStatsRange(c)