From 78e247d71bcc748d41f77fa989e22ce52458a8bc Mon Sep 17 00:00:00 2001 From: watapro26B Date: Sun, 28 Jun 2026 13:27:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E3=83=8F=E3=83=B3=E3=83=89=E3=83=A9=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Qpid/handler/icons.go | 76 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) 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 } From 89578c59c4fcf4cb54d70f71f28ad2f6943edc56 Mon Sep 17 00:00:00 2001 From: watapro26B Date: Sun, 28 Jun 2026 14:04:05 +0900 Subject: [PATCH 2/2] =?UTF-8?q?icon=5Frepogitory.go=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E4=BB=8A=E7=94=BB=E5=83=8F=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=A7=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3=E3=81=8C=E8=A6=8B?= =?UTF-8?q?=E3=81=A4=E3=81=8B=E3=82=89=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=81=A8=E3=81=8D=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Qpid/infrastructure/icon_repository.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Qpid/infrastructure/icon_repository.go b/Qpid/infrastructure/icon_repository.go index 14bb7f8..dc34e7a 100644 --- a/Qpid/infrastructure/icon_repository.go +++ b/Qpid/infrastructure/icon_repository.go @@ -1,6 +1,9 @@ package infrastructure import ( + "database/sql" + "errors" + "github.com/traP-jp/hackathon26spring_05/Qpid/domain" ) @@ -21,7 +24,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 }