some changes

This commit is contained in:
2025-12-21 00:00:03 +02:00
parent 904f446447
commit 85f8ac60e7
14 changed files with 678 additions and 1475 deletions

View File

@@ -10,68 +10,56 @@ import (
"github.com/go-chi/chi/v5"
)
// @Summary Get all resources
// @Tags resources
// @Produce json
// @Success 200 {object} getResourcesResponse
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources [get]
// @Summary Get all resources
// @Tags resources
// @Produce json
// @Success 200 {array} getResourcesResponse
// @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources [get]
func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resources, err := h.a.GetResources()
if err != nil {
switch err {
case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
default:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: "Failed to get resources",
})
return
slog.Error("unexpected server error", "error", err.Error())
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
}
return
}
_ = json.NewEncoder(w).Encode(func() getResourcesResponse {
resp := make(getResourcesResponse, 0, len(resources))
for _, res := range resources {
resp = append(resp, struct {
ID uint `json:"id" example:"1"`
Key string `json:"key" example:"html.view"`
}{
ID: res.ID,
Key: res.Key,
})
}
return resp
}())
type R struct {
ID uint `json:"id" example:"1"`
Key string `json:"key" example:"html.view"`
}
resp := make([]R, 0, len(resources))
for _, res := range resources {
resp = append(resp, R{ID: res.ID, Key: res.Key})
}
_ = json.NewEncoder(w).Encode(resp)
}
// @Summary Get resource by ID
// @Tags resources
// @Produce json
// @Param resourceId path int true "Resource ID" example(1)
// @Success 200 {object} getResourceResponse
// @Failure 400 {object} getResourceErrorInvalidResourceID
// @Failure 404 {object} getResourceErrorResourceNotFound
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources/{resourceId} [get]
// @Summary Get resource by ID
// @Tags resources
// @Produce json
// @Param resourceId path int true "Resource ID" example(1)
// @Success 200 {object} getResourceResponse
// @Failure 400 {object} ProblemDetails
// @Failure 404 {object} ProblemDetails
// @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources/{resourceId} [get]
func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resourceIDStr := chi.URLParam(r, "resourceId")
resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil || resourceID < 0 {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(getResourceErrorInvalidResourceID{
Error: ErrorInvalidResourceID,
Details: "Resource ID must be positive integer",
})
writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r)
return
}
@@ -79,27 +67,14 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
case acl.ErrResourceNotFound:
w.WriteHeader(http.StatusNotFound)
_ = json.NewEncoder(w).Encode(getResourceErrorResourceNotFound{
Error: ErrorResourceNotFound,
Details: "No resource with ID " + resourceIDStr,
})
return
writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r)
default:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: "Failed to get resource with ID " + resourceIDStr,
})
return
slog.Error("unexpected server error", "error", err.Error())
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
}
return
}
_ = json.NewEncoder(w).Encode(getResourceResponse{
@@ -108,62 +83,41 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) {
})
}
// @Summary Create resource
// @Tags resources
// @Accept json
// @Produce json
// @Param request body createResourceRequest true "Resource"
// @Success 201 {object} createResourceResponse
// @Failure 400 {object} errorInvalidRequestBody
// @Failure 400 {object} createResourceErrorInvalidResourceKey
// @Failure 409 {object} createResourceErrorResourceAlreadyExists
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources [post]
// @Summary Create resource
// @Tags resources
// @Accept json
// @Produce json
// @Param request body createResourceRequest true "Resource"
// @Success 201 {object} createResourceResponse
// @Failure 400 {object} ProblemDetails
// @Failure 409 {object} ProblemDetails
// @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources [post]
func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var req createResourceRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(errorInvalidRequestBody{
Error: ErrorInvalidRequestBody,
Details: "Request body is not valid JSON",
})
writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r)
return
}
resourceID, err := h.a.CreateResource(req.Key)
if err != nil {
slog.Error("Failed to create resource", "error", err.Error())
slog.Error("Failed to create resource", "error", err)
switch err {
case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
case acl.ErrInvalidResourceKey:
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(createResourceErrorInvalidResourceKey{
Error: ErrorFailedToCreateResource,
Details: "Resource key must be non-empty",
})
return
writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r)
case acl.ErrResourceAlreadyExists:
w.WriteHeader(http.StatusConflict)
_ = json.NewEncoder(w).Encode(createResourceErrorResourceAlreadyExists{
Error: ErrorFailedToCreateResource,
Details: "Resource with key '" + req.Key + "' already exists",
})
return
writeProblem(w, http.StatusConflict, "/errors/acl/resource-already-exists", "Resource already exists", "Resource '"+req.Key+"' already exists", r)
default:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: "Failed to create resource with key '" + req.Key + "'",
})
return
slog.Error("unexpected server error", "error", err.Error())
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
}
return
}
w.WriteHeader(http.StatusCreated)
@@ -173,148 +127,96 @@ func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request)
})
}
// @Summary Update resource
// @Tags resources
// @Accept json
// @Produce json
// @Param resourceId path int true "Resource ID" example(1)
// @Param request body updateResourceRequest true "Resource"
// @Success 200 {object} updateResourceResponse
// @Failure 400 {object} errorInvalidRequestBody
// @Failure 400 {object} updateResourceErrorInvalidResourceID
// @Failure 400 {object} updateResourceErrorInvalidResourceKey
// @Failure 404 {object} updateResourceErrorResourceNotFound
// @Failure 409 {object} updateResourceErrorResourceKeyAlreadyExists
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources/{resourceId} [patch]
// @Summary Update resource
// @Tags resources
// @Accept json
// @Produce json
// @Param resourceId path int true "Resource ID" example(1)
// @Param request body updateResourceRequest true "Resource"
// @Success 200 {object} updateResourceResponse
// @Failure 400 {object} ProblemDetails
// @Failure 404 {object} ProblemDetails
// @Failure 409 {object} ProblemDetails
// @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources/{resourceId} [patch]
func (h *aclAdminHandler) updateResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var req updateResourceRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(errorInvalidRequestBody{
Error: ErrorInvalidRequestBody,
Details: "Request body is not valid JSON",
})
writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r)
return
}
resourceIDStr := chi.URLParam(r, "resourceId")
resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil || resourceID < 0 {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(updateResourceErrorInvalidResourceID{
Error: ErrorInvalidResourceID,
Details: "Resource ID must be positive integer",
})
writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r)
return
}
err = h.a.UpdateResource(uint(resourceID), req.Key)
if err != nil {
slog.Error("Failed to update resource", "error", err.Error())
slog.Error("Failed to update resource", "error", err)
switch err {
case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
case acl.ErrInvalidResourceKey:
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(updateResourceErrorInvalidResourceKey{
Error: ErrorFailedToUpdateResource,
Details: "Invalid resource key",
})
return
writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r)
case acl.ErrResourceNotFound:
w.WriteHeader(http.StatusNotFound)
_ = json.NewEncoder(w).Encode(updateResourceErrorResourceNotFound{
Error: ErrorFailedToUpdateResource,
Details: "No resource with ID " + resourceIDStr,
})
return
writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r)
case acl.ErrSameResourceKey:
w.WriteHeader(http.StatusConflict)
_ = json.NewEncoder(w).Encode(updateResourceErrorResourceKeyAlreadyExists{
Error: ErrorFailedToUpdateResource,
Details: "Resource with key '" + req.Key + "' already exists",
})
return
writeProblem(w, http.StatusConflict, "/errors/acl/resource-key-already-exists", "Resource key already exists", "Resource key '"+req.Key+"' already exists", r)
default:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: "Failed to update resource with key '" + req.Key + "'",
})
return
slog.Error("unexpected server error", "error", err.Error())
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
}
return
}
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(updateResourceResponse{
ID: uint(resourceID),
Key: req.Key,
})
}
// @Summary Delete resource
// @Tags resources
// @Produce json
// @Param resourceId path int true "Resource ID" example(1)
// @Success 200
// @Failure 400 {object} deleteResourceErrorInvalidResourceID
// @Failure 404 {object} deleteResourceErrorResourceNotFound
// @Failure 409 {object} deleteResourceErrorResourceInUse
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources/{resourceId} [delete]
// @Summary Delete resource
// @Tags resources
// @Produce json
// @Param resourceId path int true "Resource ID" example(1)
// @Success 200
// @Failure 400 {object} ProblemDetails
// @Failure 404 {object} ProblemDetails
// @Failure 409 {object} ProblemDetails
// @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources/{resourceId} [delete]
func (h *aclAdminHandler) deleteResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resourceIDStr := chi.URLParam(r, "resourceId")
resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil || resourceID < 0 {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(deleteResourceErrorInvalidResourceID{
Error: ErrorInvalidResourceID,
Details: "Resource ID must be positive integer",
})
writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r)
return
}
err = h.a.DeleteResource(uint(resourceID))
if err != nil {
slog.Error("Failed to delete resource", "error", err.Error())
slog.Error("Failed to delete resource", "error", err)
switch err {
case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
case acl.ErrResourceNotFound:
w.WriteHeader(http.StatusNotFound)
_ = json.NewEncoder(w).Encode(deleteResourceErrorResourceNotFound{
Error: ErrorFailedToDeleteResource,
Details: "No resource with ID " + resourceIDStr,
})
return
writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r)
case acl.ErrResourceInUse:
w.WriteHeader(http.StatusConflict)
_ = json.NewEncoder(w).Encode(deleteResourceErrorResourceInUse{
Error: ErrorFailedToDeleteResource,
Details: "Resource with ID " + resourceIDStr + " is used and cannot be deleted",
})
return
writeProblem(w, http.StatusConflict, "/errors/acl/resource-in-use", "Resource in use", "Resource "+resourceIDStr+" is in use", r)
default:
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: "Failed to delete resource with ID '" + resourceIDStr + "'",
})
return
slog.Error("unexpected server error", "error", err.Error())
writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
}
return
}
w.WriteHeader(http.StatusOK)