diff --git a/Qpid/handler/icons.go b/Qpid/handler/icons.go index 0fe2631..437a96d 100644 --- a/Qpid/handler/icons.go +++ b/Qpid/handler/icons.go @@ -1,18 +1,86 @@ package handler -import "github.com/labstack/echo/v5" +import ( + "io" + "net/http" + + "github.com/labstack/echo/v5" + "github.com/traP-jp/hackathon26spring_05/Qpid/domain" + "github.com/traP-jp/hackathon26spring_05/Qpid/handler/middleware" +) // PATCH /api/me/icon func (h *handler) updateMyIcon(c *echo.Context) error { - return notImplemented(c) + username := middleware.GetUsername(c) + + //ユーザー認証 + if username == nil { + return unauthorized(c) + } + + file, err := (*c).FormFile("icon") + if err != nil { + return c.JSON(http.StatusBadRequest, errorResponse{Message: "icon file is required"}) + } + + src, err := file.Open() + if err != nil { + return c.JSON(http.StatusBadRequest, errorResponse{Message: "cannot open icon file"}) + + } + defer src.Close() + + blob, err := io.ReadAll(src) + if err != nil { + return c.JSON(http.StatusInternalServerError, errorResponse{Message: "failed to load user"}) + } + + mimeType := domain.IconMimeType(http.DetectContentType(blob)) + if !isAllowedIconMimeType(mimeType) { + return c.JSON(http.StatusBadRequest, errorResponse{Message: "unsupported icon mime type"}) + } + + if err := h.repository.SaveIcon(*username, domain.Icon{ + Blob: blob, + MimeType: mimeType, + }); err != nil { + return c.JSON(http.StatusInternalServerError, errorResponse{Message: "failed to save icon"}) + } + + return (*c).NoContent(http.StatusNoContent) } // DELETE /api/me/icon func (h *handler) deleteMyIcon(c *echo.Context) error { - return notImplemented(c) + username := middleware.GetUsername(c) + if username == nil { + return unauthorized(c) + } + + if err := h.repository.DeleteIcon(*username); err != nil { + return c.JSON(http.StatusInternalServerError, errorResponse{Message: "failed to delete icon"}) + } + + return (*c).NoContent(http.StatusNoContent) } // GET /api/users/:id/icon func (h *handler) getUserIcon(c *echo.Context) error { - return notImplemented(c) + username := (*c).Param("id") + + icon, err := h.repository.FindIconByUsername(username) + if err != nil { + return c.JSON(http.StatusInternalServerError, errorResponse{Message: "failed to load user"}) + } + if icon == nil { + return notFound(c) + } + + return (*c).Blob(http.StatusOK, string(icon.MimeType), icon.Blob) +} + +func isAllowedIconMimeType(mimeType domain.IconMimeType) bool { + return mimeType == domain.IconMimeTypePNG || + mimeType == domain.IconMimeTypeJPEG || + mimeType == domain.IconMimeTypeWebp } diff --git a/Qpid/infrastructure/icon_repository.go b/Qpid/infrastructure/icon_repository.go index 3982d42..28510bb 100644 --- a/Qpid/infrastructure/icon_repository.go +++ b/Qpid/infrastructure/icon_repository.go @@ -1,6 +1,7 @@ package infrastructure import ( + "database/sql" "errors" "github.com/traP-jp/hackathon26spring_05/Qpid/domain" @@ -48,7 +49,11 @@ func (r *repositoryImpl) FindIconByUsername(username string) (*domain.Icon, erro "SELECT icon, mime_type FROM icons WHERE username = ? LIMIT 1", username, ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } return nil, err }