fix: 코드 리뷰 기반 보안·안정성 개선 (14건)
All checks were successful
Server CI/CD / deploy (push) Successful in 1m36s
All checks were successful
Server CI/CD / deploy (push) Successful in 1m36s
- unsafe 타입 단언 → safe assertion (chain handler 11곳, auth Logout) - Repository 에러 시 nil 반환으로 통일 (chain, auth, announcement) - string ID → uint 파싱으로 타입 안전성 확보 (auth, announcement) - CORS AllowHeaders에 Idempotency-Key, X-API-Key 추가 - /verify 엔드포인트 rate limiter 적용 - Redis 호출에 context timeout 적용 (auth, idempotency 미들웨어) - chain handler 에러 응답에서 내부 정보 노출 방지 - f.Close() 에러 검사 추가 (download service 2곳) - 공지사항 Delete 404 응답 추가 - 회원가입 롤백 시 Delete 에러 로깅 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
package announcement
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
svc *Service
|
||||
@@ -34,6 +39,10 @@ func (h *Handler) Create(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (h *Handler) Update(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "유효하지 않은 공지사항 ID입니다"})
|
||||
}
|
||||
var body struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
@@ -44,15 +53,25 @@ func (h *Handler) Update(c *fiber.Ctx) error {
|
||||
if body.Title == "" && body.Content == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "수정할 내용을 입력해주세요"})
|
||||
}
|
||||
a, err := h.svc.Update(c.Params("id"), body.Title, body.Content)
|
||||
a, err := h.svc.Update(uint(id), body.Title, body.Content)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": err.Error()})
|
||||
if strings.Contains(err.Error(), "찾을 수 없습니다") {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(a)
|
||||
}
|
||||
|
||||
func (h *Handler) Delete(c *fiber.Ctx) error {
|
||||
if err := h.svc.Delete(c.Params("id")); err != nil {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "유효하지 않은 공지사항 ID입니다"})
|
||||
}
|
||||
if err := h.svc.Delete(uint(id)); err != nil {
|
||||
if strings.Contains(err.Error(), "찾을 수 없습니다") {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "삭제에 실패했습니다"})
|
||||
}
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
|
||||
@@ -16,10 +16,12 @@ func (r *Repository) FindAll() ([]Announcement, error) {
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (r *Repository) FindByID(id string) (*Announcement, error) {
|
||||
func (r *Repository) FindByID(id uint) (*Announcement, error) {
|
||||
var a Announcement
|
||||
err := r.db.First(&a, id).Error
|
||||
return &a, err
|
||||
if err := r.db.First(&a, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Create(a *Announcement) error {
|
||||
@@ -30,6 +32,6 @@ func (r *Repository) Save(a *Announcement) error {
|
||||
return r.db.Save(a).Error
|
||||
}
|
||||
|
||||
func (r *Repository) Delete(id string) error {
|
||||
func (r *Repository) Delete(id uint) error {
|
||||
return r.db.Delete(&Announcement{}, id).Error
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func (s *Service) Create(title, content string) (*Announcement, error) {
|
||||
return a, s.repo.Create(a)
|
||||
}
|
||||
|
||||
func (s *Service) Update(id, title, content string) (*Announcement, error) {
|
||||
func (s *Service) Update(id uint, title, content string) (*Announcement, error) {
|
||||
a, err := s.repo.FindByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("공지사항을 찾을 수 없습니다")
|
||||
@@ -33,6 +33,9 @@ func (s *Service) Update(id, title, content string) (*Announcement, error) {
|
||||
return a, s.repo.Save(a)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(id string) error {
|
||||
func (s *Service) Delete(id uint) error {
|
||||
if _, err := s.repo.FindByID(id); err != nil {
|
||||
return fmt.Errorf("공지사항을 찾을 수 없습니다")
|
||||
}
|
||||
return s.repo.Delete(id)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -81,7 +82,10 @@ func (h *Handler) Refresh(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (h *Handler) Logout(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, ok := c.Locals("userID").(uint)
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "인증 정보가 올바르지 않습니다"})
|
||||
}
|
||||
if err := h.svc.Logout(userID); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "로그아웃 처리 중 오류가 발생했습니다"})
|
||||
}
|
||||
@@ -97,13 +101,17 @@ func (h *Handler) GetAllUsers(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateRole(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "유효하지 않은 유저 ID입니다"})
|
||||
}
|
||||
var body struct {
|
||||
Role string `json:"role"`
|
||||
}
|
||||
if err := c.BodyParser(&body); err != nil || (body.Role != "admin" && body.Role != "user") {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "role은 admin 또는 user여야 합니다"})
|
||||
}
|
||||
if err := h.svc.UpdateRole(c.Params("id"), Role(body.Role)); err != nil {
|
||||
if err := h.svc.UpdateRole(uint(id), Role(body.Role)); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "권한 변경에 실패했습니다"})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "권한이 변경되었습니다"})
|
||||
@@ -154,7 +162,11 @@ func (h *Handler) SSAFYCallback(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteUser(c *fiber.Ctx) error {
|
||||
if err := h.svc.DeleteUser(c.Params("id")); err != nil {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "유효하지 않은 유저 ID입니다"})
|
||||
}
|
||||
if err := h.svc.DeleteUser(uint(id)); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "유저 삭제에 실패했습니다"})
|
||||
}
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
|
||||
@@ -12,8 +12,10 @@ func NewRepository(db *gorm.DB) *Repository {
|
||||
|
||||
func (r *Repository) FindByUsername(username string) (*User, error) {
|
||||
var user User
|
||||
err := r.db.Where("username = ?", username).First(&user).Error
|
||||
return &user, err
|
||||
if err := r.db.Where("username = ?", username).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Create(user *User) error {
|
||||
@@ -26,22 +28,26 @@ func (r *Repository) FindAll() ([]User, error) {
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (r *Repository) FindByID(id string) (*User, error) {
|
||||
func (r *Repository) FindByID(id uint) (*User, error) {
|
||||
var user User
|
||||
err := r.db.First(&user, id).Error
|
||||
return &user, err
|
||||
if err := r.db.First(&user, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateRole(id string, role Role) error {
|
||||
func (r *Repository) UpdateRole(id uint, role Role) error {
|
||||
return r.db.Model(&User{}).Where("id = ?", id).Update("role", role).Error
|
||||
}
|
||||
|
||||
func (r *Repository) Delete(id string) error {
|
||||
func (r *Repository) Delete(id uint) error {
|
||||
return r.db.Delete(&User{}, id).Error
|
||||
}
|
||||
|
||||
func (r *Repository) FindBySsafyID(ssafyID string) (*User, error) {
|
||||
var user User
|
||||
err := r.db.Where("ssafy_id = ?", ssafyID).First(&user).Error
|
||||
return &user, err
|
||||
if err := r.db.Where("ssafy_id = ?", ssafyID).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
@@ -165,11 +165,11 @@ func (s *Service) GetAllUsers() ([]User, error) {
|
||||
return s.repo.FindAll()
|
||||
}
|
||||
|
||||
func (s *Service) UpdateRole(id string, role Role) error {
|
||||
func (s *Service) UpdateRole(id uint, role Role) error {
|
||||
return s.repo.UpdateRole(id, role)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteUser(id string) error {
|
||||
func (s *Service) DeleteUser(id uint) error {
|
||||
return s.repo.Delete(id)
|
||||
}
|
||||
|
||||
@@ -192,7 +192,9 @@ func (s *Service) Register(username, password string) error {
|
||||
if s.walletCreator != nil {
|
||||
if err := s.walletCreator(user.ID); err != nil {
|
||||
log.Printf("wallet creation failed for user %d: %v — rolling back", user.ID, err)
|
||||
s.repo.Delete(fmt.Sprintf("%d", user.ID))
|
||||
if delErr := s.repo.Delete(user.ID); delErr != nil {
|
||||
log.Printf("WARNING: rollback delete also failed for user %d: %v", user.ID, delErr)
|
||||
}
|
||||
return fmt.Errorf("계정 초기화에 실패했습니다. 잠시 후 다시 시도해주세요")
|
||||
}
|
||||
}
|
||||
@@ -304,7 +306,9 @@ func (s *Service) SSAFYLogin(code string) (accessToken, refreshToken string, use
|
||||
if s.walletCreator != nil {
|
||||
if err := s.walletCreator(user.ID); err != nil {
|
||||
log.Printf("wallet creation failed for SSAFY user %d: %v — rolling back", user.ID, err)
|
||||
s.repo.Delete(fmt.Sprintf("%d", user.ID))
|
||||
if delErr := s.repo.Delete(user.ID); delErr != nil {
|
||||
log.Printf("WARNING: rollback delete also failed for SSAFY user %d: %v", user.ID, delErr)
|
||||
}
|
||||
return "", "", nil, fmt.Errorf("계정 초기화에 실패했습니다. 잠시 후 다시 시도해주세요")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/tolelom/tolchain/core"
|
||||
)
|
||||
|
||||
const maxLimit = 200
|
||||
|
||||
type Handler struct {
|
||||
svc *Service
|
||||
}
|
||||
@@ -15,10 +18,40 @@ func NewHandler(svc *Service) *Handler {
|
||||
return &Handler{svc: svc}
|
||||
}
|
||||
|
||||
func getUserID(c *fiber.Ctx) (uint, error) {
|
||||
uid, ok := c.Locals("userID").(uint)
|
||||
if !ok {
|
||||
return 0, fiber.NewError(fiber.StatusUnauthorized, "인증이 필요합니다")
|
||||
}
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
func parsePagination(c *fiber.Ctx) (int, int) {
|
||||
offset, _ := strconv.Atoi(c.Query("offset", "0"))
|
||||
limit, _ := strconv.Atoi(c.Query("limit", "50"))
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
} else if limit > maxLimit {
|
||||
limit = maxLimit
|
||||
}
|
||||
return offset, limit
|
||||
}
|
||||
|
||||
func chainError(c *fiber.Ctx, status int, userMsg string, err error) error {
|
||||
log.Printf("chain error: %s: %v", userMsg, err)
|
||||
return c.Status(status).JSON(fiber.Map{"error": userMsg})
|
||||
}
|
||||
|
||||
// ---- Query Handlers ----
|
||||
|
||||
func (h *Handler) GetWalletInfo(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w, err := h.svc.GetWallet(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "지갑을 찾을 수 없습니다"})
|
||||
@@ -30,21 +63,26 @@ func (h *Handler) GetWalletInfo(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (h *Handler) GetBalance(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.svc.GetBalance(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "잔액 조회에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) GetAssets(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
offset, _ := strconv.Atoi(c.Query("offset", "0"))
|
||||
limit, _ := strconv.Atoi(c.Query("limit", "50"))
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset, limit := parsePagination(c)
|
||||
result, err := h.svc.GetAssets(userID, offset, limit)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "에셋 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
@@ -57,28 +95,30 @@ func (h *Handler) GetAsset(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.GetAsset(assetID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "에셋 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
func (h *Handler) GetInventory(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.svc.GetInventory(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "인벤토리 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
}
|
||||
|
||||
func (h *Handler) GetMarketListings(c *fiber.Ctx) error {
|
||||
offset, _ := strconv.Atoi(c.Query("offset", "0"))
|
||||
limit, _ := strconv.Atoi(c.Query("limit", "50"))
|
||||
offset, limit := parsePagination(c)
|
||||
result, err := h.svc.GetMarketListings(offset, limit)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "마켓 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
@@ -91,7 +131,7 @@ func (h *Handler) GetMarketListing(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.GetListing(listingID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "마켓 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
@@ -100,7 +140,10 @@ func (h *Handler) GetMarketListing(c *fiber.Ctx) error {
|
||||
// ---- User Transaction Handlers ----
|
||||
|
||||
func (h *Handler) Transfer(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
To string `json:"to"`
|
||||
Amount uint64 `json:"amount"`
|
||||
@@ -113,13 +156,16 @@ func (h *Handler) Transfer(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.Transfer(userID, req.To, req.Amount)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "전송에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) TransferAsset(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
AssetID string `json:"assetId"`
|
||||
To string `json:"to"`
|
||||
@@ -132,13 +178,16 @@ func (h *Handler) TransferAsset(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.TransferAsset(userID, req.AssetID, req.To)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "에셋 전송에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) ListOnMarket(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
AssetID string `json:"assetId"`
|
||||
Price uint64 `json:"price"`
|
||||
@@ -151,13 +200,16 @@ func (h *Handler) ListOnMarket(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.ListOnMarket(userID, req.AssetID, req.Price)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "마켓 등록에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) BuyFromMarket(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
ListingID string `json:"listingId"`
|
||||
}
|
||||
@@ -169,13 +221,16 @@ func (h *Handler) BuyFromMarket(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.BuyFromMarket(userID, req.ListingID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "마켓 구매에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) CancelListing(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
ListingID string `json:"listingId"`
|
||||
}
|
||||
@@ -187,13 +242,16 @@ func (h *Handler) CancelListing(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.CancelListing(userID, req.ListingID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "마켓 취소에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) EquipItem(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
AssetID string `json:"assetId"`
|
||||
Slot string `json:"slot"`
|
||||
@@ -206,13 +264,16 @@ func (h *Handler) EquipItem(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.EquipItem(userID, req.AssetID, req.Slot)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "장착에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
func (h *Handler) UnequipItem(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(uint)
|
||||
userID, err := getUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req struct {
|
||||
AssetID string `json:"assetId"`
|
||||
}
|
||||
@@ -224,7 +285,7 @@ func (h *Handler) UnequipItem(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.UnequipItem(userID, req.AssetID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "장착 해제에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
@@ -245,7 +306,7 @@ func (h *Handler) MintAsset(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.MintAsset(req.TemplateID, req.OwnerPubKey, req.Properties)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "에셋 발행에 실패했습니다", err)
|
||||
}
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
@@ -264,7 +325,7 @@ func (h *Handler) GrantReward(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.GrantReward(req.RecipientPubKey, req.TokenAmount, req.Assets)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "보상 지급에 실패했습니다", err)
|
||||
}
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
@@ -284,7 +345,7 @@ func (h *Handler) RegisterTemplate(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.RegisterTemplate(req.ID, req.Name, req.Schema, req.Tradeable)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "템플릿 등록에 실패했습니다", err)
|
||||
}
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
@@ -306,7 +367,7 @@ func (h *Handler) InternalGrantReward(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.GrantRewardByUsername(req.Username, req.TokenAmount, req.Assets)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "보상 지급에 실패했습니다", err)
|
||||
}
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
@@ -326,7 +387,7 @@ func (h *Handler) InternalMintAsset(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.MintAssetByUsername(req.TemplateID, req.Username, req.Properties)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "에셋 발행에 실패했습니다", err)
|
||||
}
|
||||
return c.Status(fiber.StatusCreated).JSON(result)
|
||||
}
|
||||
@@ -339,7 +400,7 @@ func (h *Handler) InternalGetBalance(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.GetBalanceByUsername(username)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "잔액 조회에 실패했습니다", err)
|
||||
}
|
||||
return c.JSON(result)
|
||||
}
|
||||
@@ -350,11 +411,10 @@ func (h *Handler) InternalGetAssets(c *fiber.Ctx) error {
|
||||
if username == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username은 필수입니다"})
|
||||
}
|
||||
offset, _ := strconv.Atoi(c.Query("offset", "0"))
|
||||
limit, _ := strconv.Atoi(c.Query("limit", "50"))
|
||||
offset, limit := parsePagination(c)
|
||||
result, err := h.svc.GetAssetsByUsername(username, offset, limit)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "에셋 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
@@ -368,7 +428,7 @@ func (h *Handler) InternalGetInventory(c *fiber.Ctx) error {
|
||||
}
|
||||
result, err := h.svc.GetInventoryByUsername(username)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
return chainError(c, fiber.StatusInternalServerError, "인벤토리 조회에 실패했습니다", err)
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Send(result)
|
||||
|
||||
@@ -16,12 +16,16 @@ func (r *Repository) Create(w *UserWallet) error {
|
||||
|
||||
func (r *Repository) FindByUserID(userID uint) (*UserWallet, error) {
|
||||
var w UserWallet
|
||||
err := r.db.Where("user_id = ?", userID).First(&w).Error
|
||||
return &w, err
|
||||
if err := r.db.Where("user_id = ?", userID).First(&w).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindByPubKeyHex(pubKeyHex string) (*UserWallet, error) {
|
||||
var w UserWallet
|
||||
err := r.db.Where("pub_key_hex = ?", pubKeyHex).First(&w).Error
|
||||
return &w, err
|
||||
if err := r.db.Where("pub_key_hex = ?", pubKeyHex).First(&w).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@ func (s *Service) UploadLauncher(body io.Reader, baseURL string) (*Info, error)
|
||||
}
|
||||
|
||||
n, err := io.Copy(f, body)
|
||||
f.Close()
|
||||
if closeErr := f.Close(); closeErr != nil && err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
if err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return nil, fmt.Errorf("파일 저장 실패: %w", err)
|
||||
@@ -89,7 +91,9 @@ func (s *Service) Upload(filename string, body io.Reader, baseURL string) (*Info
|
||||
}
|
||||
|
||||
n, err := io.Copy(f, body)
|
||||
f.Close()
|
||||
if closeErr := f.Close(); closeErr != nil && err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
if err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return nil, fmt.Errorf("파일 저장 실패: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user