mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-14 00:55:44 +00:00
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:
@@ -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"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user