diff --git a/api/acl_admin/resources.go b/api/acl_admin/resources.go index 4d4d52e..4a3508d 100644 --- a/api/acl_admin/resources.go +++ b/api/acl_admin/resources.go @@ -7,6 +7,7 @@ import ( "strconv" "git.oblat.lv/alex/triggerssmith/internal/acl" + "git.oblat.lv/alex/triggerssmith/internal/server" "github.com/go-chi/chi/v5" ) @@ -14,7 +15,7 @@ import ( // @Tags acl/resources // @Produce json // @Success 200 {object} getResourcesResponse -// @Failure 500 {object} ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/resources [get] func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -23,10 +24,10 @@ func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) { if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -49,9 +50,9 @@ func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) { // @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 +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/resources/{resourceId} [get] func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -59,7 +60,7 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) { resourceIDStr := chi.URLParam(r, "resourceId") resourceID, err := strconv.Atoi(resourceIDStr) if err != nil || resourceID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) return } @@ -67,12 +68,12 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) { if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) case acl.ErrResourceNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -89,16 +90,16 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) { // @Produce json // @Param request body createResourceRequest true "Resource" // @Success 201 {object} createResourceResponse -// @Failure 400 {object} ProblemDetails -// @Failure 409 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.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 { - writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) return } @@ -108,14 +109,14 @@ func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) case acl.ErrInvalidResourceKey: - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r) case acl.ErrResourceAlreadyExists: - writeProblem(w, http.StatusConflict, "/errors/acl/resource-already-exists", "Resource already exists", "Resource '"+req.Key+"' already exists", r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/resource-already-exists", "Resource already exists", "Resource '"+req.Key+"' already exists", r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -134,24 +135,24 @@ func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request) // @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 +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.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 { - writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) + server.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 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) return } @@ -161,16 +162,16 @@ func (h *aclAdminHandler) updateResource(w http.ResponseWriter, r *http.Request) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) case acl.ErrInvalidResourceKey: - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r) case acl.ErrResourceNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r) case acl.ErrSameResourceKey: - writeProblem(w, http.StatusConflict, "/errors/acl/resource-key-already-exists", "Resource key already exists", "Resource key '"+req.Key+"' already exists", r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/resource-key-already-exists", "Resource key already exists", "Resource key '"+req.Key+"' already exists", r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -186,10 +187,10 @@ func (h *aclAdminHandler) updateResource(w http.ResponseWriter, r *http.Request) // @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 +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/resources/{resourceId} [delete] func (h *aclAdminHandler) deleteResource(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -197,7 +198,7 @@ func (h *aclAdminHandler) deleteResource(w http.ResponseWriter, r *http.Request) resourceIDStr := chi.URLParam(r, "resourceId") resourceID, err := strconv.Atoi(resourceIDStr) if err != nil || resourceID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) return } @@ -207,14 +208,14 @@ func (h *aclAdminHandler) deleteResource(w http.ResponseWriter, r *http.Request) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r) case acl.ErrResourceNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r) case acl.ErrResourceInUse: - writeProblem(w, http.StatusConflict, "/errors/acl/resource-in-use", "Resource in use", "Resource "+resourceIDStr+" is in use", r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/resource-in-use", "Resource in use", "Resource "+resourceIDStr+" is in use", r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } diff --git a/api/acl_admin/roles.go b/api/acl_admin/roles.go index d791f54..0bd9fe8 100644 --- a/api/acl_admin/roles.go +++ b/api/acl_admin/roles.go @@ -7,6 +7,7 @@ import ( "strconv" "git.oblat.lv/alex/triggerssmith/internal/acl" + "git.oblat.lv/alex/triggerssmith/internal/server" "github.com/go-chi/chi/v5" ) @@ -14,7 +15,7 @@ import ( // @Tags acl/roles // @Produce json // @Success 200 {array} getRolesResponse -// @Failure 500 {object} ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles [get] func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -22,10 +23,10 @@ func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) { if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -48,16 +49,16 @@ func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) { // @Produce json // @Param roleId path int true "Role ID" example(1) // @Success 200 {object} getRoleResponse -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId} [get] func (h *aclAdminHandler) getRole(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } @@ -65,12 +66,12 @@ func (h *aclAdminHandler) getRole(w http.ResponseWriter, r *http.Request) { if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -86,16 +87,16 @@ func (h *aclAdminHandler) getRole(w http.ResponseWriter, r *http.Request) { // @Produce json // @Param roleId path int true "Role ID" example(1) // @Success 200 {array} getRoleUsersResponse -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId}/users [get] func (h *aclAdminHandler) getRoleUsers(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } @@ -103,17 +104,17 @@ func (h *aclAdminHandler) getRoleUsers(w http.ResponseWriter, r *http.Request) { if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } if len(role.Users) == 0 { - writeProblem(w, http.StatusNotFound, "/errors/acl/role-has-no-users", "Role has no users", "Role has no users", r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-has-no-users", "Role has no users", "Role has no users", r) return } var respUsers getRoleUsersResponse @@ -132,33 +133,33 @@ func (h *aclAdminHandler) getRoleUsers(w http.ResponseWriter, r *http.Request) { // @Produce json // @Param roleId path int true "Role ID" example(1) // @Success 200 {array} getRoleResourcesResponse -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId}/resources [get] func (h *aclAdminHandler) getRoleResources(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } role, err := h.a.GetRoleByID(uint(roleID)) if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } if len(role.Resources) == 0 { - writeProblem(w, http.StatusNotFound, "/errors/acl/role-has-no-users", "Role has no users", "Role has no users", r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-has-no-users", "Role has no users", "Role has no users", r) return } var respResources getRoleResourcesResponse @@ -177,16 +178,16 @@ func (h *aclAdminHandler) getRoleResources(w http.ResponseWriter, r *http.Reques // @Produce json // @Param request body createRoleRequest true "Role" // @Success 201 {object} createRoleResponse -// @Failure 400 {object} ProblemDetails -// @Failure 409 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles [post] func (h *aclAdminHandler) createRole(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var req createRoleRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) return } @@ -195,13 +196,13 @@ func (h *aclAdminHandler) createRole(w http.ResponseWriter, r *http.Request) { slog.Error("Failed to create role", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrInvalidRoleName: - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-name", "Invalid role name", "Role name must be non-empty", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-name", "Invalid role name", "Role name must be non-empty", r) case acl.ErrRoleAlreadyExists: - writeProblem(w, http.StatusConflict, "/errors/acl/role-already-exists", "Role already exists", "Role '"+req.Name+"' already exists", r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/role-already-exists", "Role already exists", "Role '"+req.Name+"' already exists", r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -220,24 +221,24 @@ func (h *aclAdminHandler) createRole(w http.ResponseWriter, r *http.Request) { // @Param roleId path int true "Role ID" example(1) // @Param request body updateRoleRequest true "Role" // @Success 200 {object} updateRoleResponse -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 409 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId} [patch] func (h *aclAdminHandler) updateRole(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var req updateRoleRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r) return } roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } @@ -246,15 +247,15 @@ func (h *aclAdminHandler) updateRole(w http.ResponseWriter, r *http.Request) { slog.Error("Failed to update role", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrInvalidRoleName: - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-name", "Invalid role name", "Role name must be non-empty", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-name", "Invalid role name", "Role name must be non-empty", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) case acl.ErrSameRoleName: - writeProblem(w, http.StatusConflict, "/errors/acl/role-name-already-exists", "Role name already exists", "Role '"+req.Name+"' already exists", r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/role-name-already-exists", "Role name already exists", "Role '"+req.Name+"' already exists", r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -270,17 +271,17 @@ func (h *aclAdminHandler) updateRole(w http.ResponseWriter, r *http.Request) { // @Produce json // @Param roleId path int true "Role ID" example(1) // @Success 204 -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 409 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId} [delete] func (h *aclAdminHandler) deleteRole(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } @@ -289,13 +290,13 @@ func (h *aclAdminHandler) deleteRole(w http.ResponseWriter, r *http.Request) { slog.Error("Failed to delete role", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) case acl.ErrRoleInUse: - writeProblem(w, http.StatusConflict, "/errors/acl/role-in-use", "Role in use", "Role "+roleIDStr+" is assigned to at least one user and cannot be deleted", r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/role-in-use", "Role in use", "Role "+roleIDStr+" is assigned to at least one user and cannot be deleted", r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -309,37 +310,37 @@ func (h *aclAdminHandler) deleteRole(w http.ResponseWriter, r *http.Request) { // @Param roleId path int true "Role ID" example(1) // @Param request body assignResourceToRoleRequest true "Resource" // @Success 201 -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 409 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId}/resources [post] func (h *aclAdminHandler) assignResourceToRole(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } var req assignResourceToRoleRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-request-body", "Invalid request body", "Invalid JSON body", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-request-body", "Invalid request body", "Invalid JSON body", r) return } if err := h.a.AssignResourceToRole(uint(roleID), req.ResourceID); err != nil { slog.Error("Failed to assign resource to role", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) case acl.ErrResourceNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+strconv.Itoa(int(req.ResourceID)), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+strconv.Itoa(int(req.ResourceID)), r) case acl.ErrResourceAlreadyAssigned: - writeProblem(w, http.StatusConflict, "/errors/acl/resource-already-assigned", "Resource already assigned", "Resource with ID "+strconv.Itoa(int(req.ResourceID))+" is already assigned to role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/resource-already-assigned", "Resource already assigned", "Resource with ID "+strconv.Itoa(int(req.ResourceID))+" is already assigned to role with ID "+roleIDStr, r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -352,37 +353,37 @@ func (h *aclAdminHandler) assignResourceToRole(w http.ResponseWriter, r *http.Re // @Param roleId path int true "Role ID" example(1) // @Param resId path int true "Resource ID" example(1) // @Success 204 -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/roles/{roleId}/resources/{resId} [delete] func (h *aclAdminHandler) removeResourceFromRole(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } resourceIDStr := chi.URLParam(r, "resId") resourceID, err := strconv.Atoi(resourceIDStr) if err != nil || resourceID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r) return } if err := h.a.RemoveResourceFromRole(uint(roleID), uint(resourceID)); err != nil { slog.Error("Failed to remove resource from role", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r) case acl.ErrResourceNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+strconv.Itoa(int(resourceID)), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+strconv.Itoa(int(resourceID)), r) case acl.ErrRoleResourceNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/role-resource-not-found", "Role resource not found", "No role-resource pair with role ID "+roleIDStr+" and resource ID "+strconv.Itoa(int(resourceID)), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/role-resource-not-found", "Role resource not found", "No role-resource pair with role ID "+roleIDStr+" and resource ID "+strconv.Itoa(int(resourceID)), r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } diff --git a/api/acl_admin/users.go b/api/acl_admin/users.go index 7da53ae..1b180b8 100644 --- a/api/acl_admin/users.go +++ b/api/acl_admin/users.go @@ -7,6 +7,7 @@ import ( "strconv" "git.oblat.lv/alex/triggerssmith/internal/acl" + "git.oblat.lv/alex/triggerssmith/internal/server" "github.com/go-chi/chi/v5" ) @@ -15,30 +16,30 @@ import ( // @Produce json // @Param userId path int true "User ID" example(1) // @Success 200 {object} getUserRolesResponse -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/users/{userId}/roles [get] func (h *aclAdminHandler) getUserRoles(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") userIDStr := chi.URLParam(r, "userId") userID, err := strconv.Atoi(userIDStr) if err != nil { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) return } roles, err := h.a.GetUserRoles(uint(userID)) if err != nil { switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrUserNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) default: slog.Error("unexpected server error", "error", err.Error()) - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -55,37 +56,37 @@ func (h *aclAdminHandler) getUserRoles(w http.ResponseWriter, r *http.Request) { // @Param userId path int true "User ID" example(1) // @Param body body assignRoleToUserRequest true "Role ID" // @Success 201 -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 409 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 409 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/users/{userId}/roles [post] func (h *aclAdminHandler) assignRoleToUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") userIDStr := chi.URLParam(r, "userId") userID, err := strconv.Atoi(userIDStr) if err != nil || userID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) return } var req assignRoleToUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-request-body", "Invalid request body", "Invalid JSON body", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-request-body", "Invalid request body", "Invalid JSON body", r) return } if err := h.a.AssignRoleToUser(req.RoleID, uint(userID)); err != nil { slog.Error("Failed to assign role to user", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrUserNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) case acl.ErrRoleAlreadyAssigned: - writeProblem(w, http.StatusConflict, "/errors/acl/role-already-assigned", "Role already assigned", "Role with ID "+strconv.Itoa(int(req.RoleID))+" is already assigned to user "+strconv.Itoa(userID), r) + server.WriteProblem(w, http.StatusConflict, "/errors/acl/role-already-assigned", "Role already assigned", "Role with ID "+strconv.Itoa(int(req.RoleID))+" is already assigned to user "+strconv.Itoa(userID), r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } @@ -98,22 +99,22 @@ func (h *aclAdminHandler) assignRoleToUser(w http.ResponseWriter, r *http.Reques // @Param userId path int true "User ID" example(1) // @Param roleId path int true "Role ID" example(1) // @Success 204 -// @Failure 400 {object} ProblemDetails -// @Failure 404 {object} ProblemDetails -// @Failure 500 {object} ProblemDetails +// @Failure 400 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails // @Router /api/acl/users/{userId}/roles/{roleId} [delete] func (h *aclAdminHandler) removeRoleFromUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") userIDStr := chi.URLParam(r, "userId") userID, err := strconv.Atoi(userIDStr) if err != nil || userID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) return } roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { - writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) + server.WriteProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } err = h.a.RemoveRoleFromUser(uint(roleID), uint(userID)) @@ -121,15 +122,15 @@ func (h *aclAdminHandler) removeRoleFromUser(w http.ResponseWriter, r *http.Requ slog.Error("Failed to remove role from user", "error", err.Error()) switch err { case acl.ErrNotInitialized: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrUserNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) case acl.ErrRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) case acl.ErrUserRoleNotFound: - writeProblem(w, http.StatusNotFound, "/errors/acl/user-role-not-found", "User role not found", "User "+strconv.Itoa(userID)+" does not have role "+strconv.Itoa(roleID), r) + server.WriteProblem(w, http.StatusNotFound, "/errors/acl/user-role-not-found", "User role not found", "User "+strconv.Itoa(userID)+" does not have role "+strconv.Itoa(roleID), r) default: - writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } } w.WriteHeader(http.StatusNoContent) diff --git a/api/auth/handle.go b/api/auth/handle.go index c6b717f..21e5fc3 100644 --- a/api/auth/handle.go +++ b/api/auth/handle.go @@ -3,24 +3,19 @@ package api_auth import ( - "encoding/json" - "fmt" - "log/slog" "net/http" "time" "git.oblat.lv/alex/triggerssmith/internal/auth" "git.oblat.lv/alex/triggerssmith/internal/config" - "git.oblat.lv/alex/triggerssmith/internal/server" "github.com/go-chi/chi/v5" - "github.com/golang-jwt/jwt/v5" ) func setRefreshCookie(w http.ResponseWriter, token string, ttl time.Duration, secure bool) { http.SetCookie(w, &http.Cookie{ Name: "refresh_token", Value: token, - Path: "/api/auth/refresh", + Path: "/api/auth/", HttpOnly: true, SameSite: http.SameSiteLaxMode, MaxAge: int(ttl.Seconds()), @@ -58,165 +53,3 @@ func MustRoute(config *config.Config, authService *auth.Service) func(chi.Router r.Post("/revoke", h.handleRevoke) // not implemented } } - -type registerRequest struct { - Username string `json:"username"` - Email string `json:"email"` - Password string `json:"password"` -} - -type registerResponse struct { - UserID uint `json:"id"` - Username string `json:"username"` -} - -func (h *authHandler) handleRegister(w http.ResponseWriter, r *http.Request) { - var req registerRequest - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, "Invalid request payload", http.StatusBadRequest) - return - } - - user, err := h.a.Register(req.Username, req.Email, req.Password) - if err != nil { - slog.Error("Failed to register user", "error", err) - http.Error(w, "Registration failed", http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(registerResponse{ - UserID: user.ID, - Username: user.Username, - }) - if err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusCreated) -} - -type loginRequest struct { - Username string `json:"username"` - Password string `json:"password"` -} - -type loginResponse struct { - Token string `json:"accessToken"` -} - -func (h *authHandler) handleLogin(w http.ResponseWriter, r *http.Request) { - var req loginRequest - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, "Invalid request payload", http.StatusBadRequest) - return - } - - tokens, err := h.a.Login(req.Username, req.Password) - if err != nil { - http.Error(w, "Authentication failed", http.StatusUnauthorized) - return - } - - setRefreshCookie(w, tokens.Refresh, h.cfg.Auth.RefreshTokenTTL, false) - - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(loginResponse{Token: tokens.Access}) - if err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - return - } -} - -func (h *authHandler) handleLogout(w http.ResponseWriter, r *http.Request) { - claims, err := h.a.AuthenticateRequest(r) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - return - } - rjti := claims.(jwt.MapClaims)["rjti"].(string) - err = h.a.Logout(rjti) - if err != nil { - http.Error(w, "Failed to logout, taking cookie anyways", http.StatusInternalServerError) - } - http.SetCookie(w, &http.Cookie{ - Name: "refresh_token", - Value: "", - MaxAge: -1, - Path: "/api/users/refresh", - HttpOnly: true, - SameSite: http.SameSiteLaxMode, - }) - if err == nil { - w.WriteHeader(http.StatusOK) - } -} - -type meResponse struct { - UserID uint `json:"id"` - Username string `json:"username"` - Email string `json:"email"` -} - -func (h *authHandler) handleMe(w http.ResponseWriter, r *http.Request) { - refresh_token_cookie, err := r.Cookie("refresh_token") - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - return - } - userID, err := h.a.ValidateRefreshToken(refresh_token_cookie.Value) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - return - } - user, err := h.a.Get("id", fmt.Sprint(userID)) - if err != nil { - http.Error(w, "Failed to get user", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(meResponse{ - UserID: user.ID, - Username: user.Username, - Email: user.Email, - }) - if err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - return - } -} - -type GetUserDataResponse meResponse - -func (h *authHandler) handleGetUserData(w http.ResponseWriter, r *http.Request) { - by := r.URL.Query().Get("by") - value := r.URL.Query().Get("value") - if value == "" { - value = r.URL.Query().Get(by) - } - user, err := h.a.Get(by, value) - if err != nil { - http.Error(w, "Failed to get user", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(meResponse{ - UserID: user.ID, - Username: user.Username, - Email: user.Email, - }) - if err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - return - } -} - -func (h *authHandler) handleRevoke(w http.ResponseWriter, r *http.Request) { - server.NotImplemented(w) -} - -func (h *authHandler) handleRefresh(w http.ResponseWriter, r *http.Request) { - server.NotImplemented(w) -} diff --git a/api/block/handle.go b/api/block/handle.go index 90154cd..03e6b7c 100644 --- a/api/block/handle.go +++ b/api/block/handle.go @@ -16,6 +16,7 @@ import ( "path/filepath" "git.oblat.lv/alex/triggerssmith/internal/config" + "git.oblat.lv/alex/triggerssmith/internal/server" "github.com/go-chi/chi/v5" ) @@ -41,16 +42,26 @@ func MustRoute(config *config.Config) func(chi.Router) { } } +// @Summary Get block +// @Tags block +// @Produce json +// @Param blockPath path string true "Block Path" example(menu) +// @Success 200 {object} Block +// @Failure 403 {object} server.ProblemDetails +// @Failure 404 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails +// @Router /api/block/{blockPath} [get] func (h *blockHandler) handleBlock(w http.ResponseWriter, r *http.Request) { if !h.cfg.Server.BlockConfig.Enabled { - http.Error(w, "Block serving is disabled", http.StatusForbidden) + server.WriteProblem(w, http.StatusForbidden, "/errors/block/block-serving-disabled", "Block serving is disabled", "Block serving is disabled", r) return } blockPath := r.URL.Path[len("/api/block/"):] block, err := LoadBlock(blockPath, h.cfg) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + slog.Error("failed to load block", slog.String("path", blockPath), slog.String("err", err.Error())) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "failed to load block", r) return } w.Header().Set("Content-Type", "application/json")