package user import ( "crypto/sha256" "encoding/json" "errors" "fmt" "math/big" "net/http" "github.com/ethereum/go-ethereum/core/types" "github.com/gin-gonic/gin" "github.com/guregu/null" "github.com/metarare/metarare_api/auth" "github.com/metarare/metarare_api/common" "github.com/metarare/metarare_api/helpers" "github.com/metarare/metarare_api/helpers/gauth" "github.com/metarare/metarare_api/helpers/gerror" "github.com/metarare/metarare_api/models" "github.com/metarare/metarare_api/util" "github.com/metarare/metarare_api/view" "github.com/spf13/viper" "gorm.io/gorm" ) type UserV1Router struct { group *gin.RouterGroup mDB *gorm.DB rDB *gorm.DB awsConf util.AWSConfs Env *viper.Viper } type AuthenticationSNS struct { Code string `json:"code" binding:"required"` Type string `json:"type" binding:"required"` } type ProfileInfo struct { Name string `json:"name" binding:"required"` Description string `json:"description" binding:"required"` Phone string `json:"phone"` Twitter string `json:"twitter"` } type Transfer struct { Currency string `json:"currency" binding:"required"` ToAddress string `json:"to_address" binding:"required"` Amount float64 `json:"amount" binding:"required"` Code string `json:"code" binding:"required"` } type CertificationBody struct { Currency string `json:"currency" binding:"required"` Amount float64 `json:"amount" binding:"required"` } func NewUserV1Router(r common.Router, basePath string) UserV1Router { u := UserV1Router{ group: r.Version.Group(basePath), mDB: r.Db.MasterDB, rDB: r.Db.ReadDB, awsConf: util.AWSConfs{ Region: r.Env.GetString("storage.region"), BucketName: r.Env.GetString("storage.bucket_name"), Access_key_id: r.Env.GetString("storage.access_key_id"), Access_key: r.Env.GetString("storage.access_key"), }, Env: r.Env, } u.group.GET("redirect/url", u.getRedirectURL) u.group.POST("authenticate", u.userAuthentication) u.group.GET("authenticate/temp", u.tempAuthentication) u.group.GET("profile/:name", u.getUserProfile) u.group.PATCH("profile", u.updateUserProfile) u.group.POST("onsale/:name", u.getOnSaleItems) u.group.POST("collection/:name", u.getRelatedCollections) u.group.POST("like/:name", u.getLikeItems) u.group.POST("owned/:name", u.getOwnedItems) u.group.GET("activity/:name", u.getUserActivities) u.group.POST("bid", u.getuserBidHistory) u.group.GET("profile", u.getUserSimpleProfile) u.group.GET("duplicate/:name", u.duplicateName) u.group.POST("transfer", u.tokenTransfer) u.group.POST("certification/code", u.certificationCode) return u } // getRedirectURL godoc // @Summary sns redirect url // @Description SNS 로그인 버튼 클릭시 redirect_url을 받는 API, {type=google, kakao} // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param type query string true "type" // @Success 200 {string} redirectURL // @Router /user/redirect/url [get] func (u UserV1Router) getRedirectURL(c *gin.Context) { var redirectURL string signinType := c.Query("type") if signinType != "google" && signinType != "kakao" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } if signinType == "google" { redirectURL = auth.GetGoogleSignInURL() } else { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, nil) return } gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, redirectURL, nil) return } // tempAuthentication godoc // @Summary temp user authentication // @Description SNS로그인 테스트 이전에 동일한 로직으로 유저 회원가입, 세션 획득을 위한 임시 함수, 입력된 이메일이 없으면 회원가입 이후 로그인, 있으면 로그인 // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param email query string true "email" // @Success 200 {string} authContainer // @Router /user/authenticate/temp [get] func (u UserV1Router) tempAuthentication(c *gin.Context) { request := c.Query("email") if request == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _auth := models.UserAuthentication{} if err := u.rDB.Where("email = ?", request).Find(&_auth).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } else if _auth.ID != 0 { //NOTE x 상태값 확인 후 로그인 세션 생성 _user := models.User{} err := u.rDB.Where("id = ?", _auth.UserID).Find(&_user).Error if errors.Is(err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } else if _user.Status != "stable" { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, errors.New("")) return } token := common.MakeJwtToken(_auth.UserID) gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil) return } //NOTE x 회원가입 이후 로그인 세션 생성 tx := u.mDB.Begin() defer common.DBTransaction(tx) user := models.User{ UID: common.GenerateUserUID(tx, "user_"), Status: "stable", } if err := tx.Save(&user).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } authentication := models.UserAuthentication{ UserID: user.ID, Email: request, Type: null.StringFrom("google"), } if err := tx.Save(&authentication).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } profile := models.UserProfile{ UserID: user.ID, Name: null.StringFrom(common.GenerateUserName(tx)), } if err := tx.Save(&profile).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } pk, addr := helpers.GenerateWallet() str := addr + pk + "1" checkSum := sha256.Sum256([]byte(str)) wallet := models.UserWallet{ UserID: user.ID, Address: addr, PrivateKey: pk, CheckSum: fmt.Sprintf("%x", checkSum), EncVersion: 1, } if err := tx.Save(&wallet).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } if err := tx.Commit().Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } token := common.MakeJwtToken(user.ID) gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil) return } // userAuthentication godoc // @Summary sign-in // @Description redirect url 호출이후 받은 코드로 기존유저라면 로그인, 새로운 유저라면 가입 후 로그인 // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param AuthenticationSNS body AuthenticationSNS true "oauth data" // @Success 200 {string} authContainer // @Router /user/authenticate [post] func (u UserV1Router) userAuthentication(c *gin.Context) { var email string var _err error var req AuthenticationSNS if err := c.ShouldBindJSON(&req); err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) return } switch req.Type { case "google": g, err := auth.GetGoogleUserInfo(req.Code) if err != nil { _err = err break } email = g.Email default: gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("required type parameter")) return } if _err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.GetSNSUserInfoError, nil, _err) return } _auth := models.UserAuthentication{} if err := u.rDB.Where("email = ?", email).Find(&_auth).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, _err) return } else if _auth.ID != 0 { //NOTE x 상태 확인 후 로그인 세션 생성 _user := models.User{} err := u.rDB.Where("id = ?", _auth.UserID).Find(&_user).Error if errors.Is(err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } else if _user.Status != "stable" { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, errors.New("")) return } token := common.MakeJwtToken(_auth.UserID) gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil) return } //NOTE x 회원가입 이후 로그인 세션 생성 tx := u.mDB.Begin() defer common.DBTransaction(tx) user := models.User{ UID: common.GenerateUserUID(tx, "user_"), Status: "stable", } if err := tx.Save(&user).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } authentication := models.UserAuthentication{ UserID: user.ID, Email: email, Type: null.StringFrom(req.Type), } if err := tx.Save(&authentication).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } profile := models.UserProfile{ UserID: user.ID, Name: null.StringFrom(common.GenerateUserName(tx)), } if err := tx.Save(&profile).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } pk, addr := helpers.GenerateWallet() str := addr + pk + "1" checkSum := sha256.Sum256([]byte(str)) wallet := models.UserWallet{ UserID: user.ID, Address: addr, PrivateKey: pk, CheckSum: fmt.Sprintf("%x", checkSum), EncVersion: 1, } if err := tx.Save(&wallet).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } if err := tx.Commit().Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } token := common.MakeJwtToken(user.ID) gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil) return } ///////////////////////////////// func (u UserV1Router) extractFilterData(c *gin.Context) (error, common.Filter) { var _filter common.Filter if err := c.ShouldBindJSON(&_filter); err != nil { return err, common.Filter{} } else { return nil, _filter } } // getUserProfile godoc // @Summary user basic profile data // @Description 유저 기본 정보 가져오기 // @Schemes // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param name path string true "user profile name" // @Success 200 {string} authContainer // @Router /user/profile/{name} [get] func (u UserV1Router) getUserProfile(c *gin.Context) { userID, err := gauth.GetCurrentUserIDToInt64(c) name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _id, err := common.ConvertToId(u.rDB, name) if _id == 0 || err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err) return } err, userProfile := view.SelectUserInfo(u.rDB, _id, userID) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "There is no matched user profile", }) return } c.JSON(http.StatusOK, userProfile) } // updateUserProfile godoc // @Summary update user profile // @Description 유저 정보 업데이트 // @Schemes // @security ApiKeyAuth // @Tags user // @Accept multipart/form-data // @Produce json // @Param thumbnailImage formData file true "thumbnail image" // @Param coverImage formData file true "cover image" // @Param json formData common.SwagStruct true "object" // @Success 200 {string} authContainer // @Router /user/profile [patch] func (u UserV1Router) updateUserProfile(c *gin.Context) { userID, err := gauth.GetCurrentUserIDToInt64(c) if err != nil || userID == 0 { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, err) return } form, err := c.MultipartForm() if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MultipartError, nil, err) return } _request := form.Value["json"] if _request == nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } request := ProfileInfo{} if err := json.Unmarshal([]byte(_request[0]), &request); err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, nil) return } thumbnailImage := form.File["thumbnailImage"] coverImage := form.File["coverImage"] profile := models.UserProfile{} _err := u.rDB.Where("user_id = ?", userID).Find(&profile).Error if errors.Is(_err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, _err) return } tx := u.mDB.Begin() defer common.DBTransaction(tx) var thumbnailImageURL string if thumbnailImage != nil { thumbnailImagePath := fmt.Sprintf("profile/thumbnail/%d", userID) for _, f := range thumbnailImage { thumbnailImageURL, err = util.UploadToS3(u.awsConf, thumbnailImagePath, f) if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err) tx.Rollback() return } } profile.ThumbnailImage = null.StringFrom(thumbnailImageURL) } var coverImageURL string if coverImage != nil { coverImagePath := fmt.Sprintf("profile/cover/%d", userID) for _, f := range coverImage { coverImageURL, err = util.UploadToS3(u.awsConf, coverImagePath, f) if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err) tx.Rollback() return } } profile.CoverImage = null.StringFrom(coverImageURL) } profile.Name = null.StringFrom(request.Name) profile.Description = null.StringFrom(request.Description) if request.Twitter != "" { profile.Twitter = null.StringFrom(request.Twitter) } if request.Phone != "" { profile.Phone = null.StringFrom(request.Phone) } if err := tx.Save(&profile).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) return } if err := tx.Commit().Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, err) } // getOnSaleItems godoc // @Summary onesale list // @Description 판매중인 NFT 리스트 // @Schemes // @security ApiKeyAuth // @Tags user // @Accept json // @Produce json // @Param common.Filter body common.Filter true "filter object" // @Param name path string true "user profile name" // @Success 200 {object} []common.ExpItem // @Router /user/onsale/{name} [post] func (u UserV1Router) getOnSaleItems(c *gin.Context) { name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _id, err := common.ConvertToId(u.rDB, name) if _id == 0 || err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err) return } err, filter := u.extractFilterData(c) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, filter) return } err, _sales := view.SelectOnsaleItems(u.rDB, _id, filter) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "there is no onSale item", }) return } c.JSON(http.StatusOK, _sales) } // getOwnedItems godoc // @Summary owned list // @Description 보유중인 NFT 리스트 // @Schemes // @security ApiKeyAuth // @Tags user // @Accept json // @Produce json // @Param common.Filter body common.Filter true "filter object" // @Success 200 {object} []common.ExpItem // @Router /user/owned [post] func (u UserV1Router) getOwnedItems(c *gin.Context) { name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _id, err := common.ConvertToId(u.rDB, name) if _id == 0 || err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err) return } err, filter := u.extractFilterData(c) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, filter) return } err, _tokenInfos := view.SelectOwnedTokens(u.rDB, _id, filter) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "NO matched tokenDatas", }) return } c.JSON(http.StatusOK, _tokenInfos) } // getRelatedCollections godoc // @Summary collection list // @Description 유저 프로필 컬렉션 탭 // @Schemes // @security ApiKeyAuth // @Tags user // @Accept json // @Produce json // @Param common.Filter body common.Filter true "filter object" // @Param name path string true "user profile name" // @Success 200 {object} []common.CollectionItem // @Router /user/collection/{name} [post] func (u UserV1Router) getRelatedCollections(c *gin.Context) { name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _id, err := common.ConvertToId(u.rDB, name) if _id == 0 || err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err) return } err, filter := u.extractFilterData(c) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, filter) return } err, _collections := view.SelectRelatedCollection(u.rDB, _id, filter) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "No Matched collections", }) return } c.JSON(http.StatusOK, _collections) } // getLikeItems godoc // @Summary user like list // @Description 유저 프로필 좋아요 탭 // @Schemes // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param common.Filter body common.Filter true "filter object" // @Param name path string true "user profile name" // @Success 200 {object} []common.ExpItem // @Router /user/like/{name} [post] func (u UserV1Router) getLikeItems(c *gin.Context) { name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _id, err := common.ConvertToId(u.rDB, name) if _id == 0 || err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err) return } err, filter := u.extractFilterData(c) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, filter) return } err, likeItems := view.SelectLikedTokens(u.rDB, _id, filter) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "There is no liked items", }) return } c.JSON(http.StatusOK, likeItems) } // getUserActivities godoc // @Summary user activity list // @Description 유저 프로필 활동 탭 // @Schemes // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param name path string true "user profile name" // @Success 200 {object} []common.ActivityItem // @Router /user/activity/{name} [get] func (u UserV1Router) getUserActivities(c *gin.Context) { name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _id, err := common.ConvertToId(u.rDB, name) if _id == 0 || err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err) return } err, likeItems := view.SelectUserActivities(u.rDB, _id) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "There is no liked items", }) return } c.JSON(http.StatusOK, likeItems) } // getuserBidHistory godoc // @Summary user bid list // @Description 유저 경매 히스토리 // @Schemes // @security ApiKeyAuth // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Success 200 {object} []common.ExpItem // @Router /user/bid [post] func (u UserV1Router) getuserBidHistory(c *gin.Context) { userID, err := gauth.GetCurrentUserIDToInt64(c) if err != nil || userID == 0 { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, err) return } if err != nil { c.AbortWithStatus(http.StatusUnauthorized) return } err, filter := u.extractFilterData(c) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, filter) return } err, likeItems := view.SelectUserBidHistory(u.rDB, userID, filter) if errors.Is(err, gorm.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusOK, gin.H{ "message": "There is no liked items", }) return } c.JSON(http.StatusOK, likeItems) } // getUserSimpleProfile godoc // @Summary user simple profile data // @Description 헤더, 프로필 편집에 사용할 데이터 // @Schemes // @Tags user // @security ApiKeyAuth // @name get-string-by-int // @Accept json // @Produce json // @Success 200 {object} view.SimpleProfile // @Router /user/profile [get] func (u UserV1Router) getUserSimpleProfile(c *gin.Context) { userID, err := gauth.GetCurrentUserIDToInt64(c) if err != nil || userID == 0 { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, err) return } response := view.SimpleProfile{} _err := view.GetSimpleProfile(u.rDB, userID).Find(&response.SimpleProfile).Error if errors.Is(_err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err) return } _err = u.rDB.Select("setting.commission AS service_commission, setting.gas_deposit AS network_commission").Table("setting").Find(&response.Setting).Error if errors.Is(_err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err) return } response.SimpleProfile.EthBalance, response.SimpleProfile.AvailableEthBalance = helpers.GetSelectedBalance(u.rDB, userID, "eth", response.SimpleProfile.Address) response.SimpleProfile.MfBalance, response.SimpleProfile.AvailableMfBalance = helpers.GetSelectedBalance(u.rDB, userID, "mf", response.SimpleProfile.Address) response.SimpleProfile.MrBalance, response.SimpleProfile.AvailableMrBalance = helpers.GetSelectedBalance(u.rDB, userID, "mr", response.SimpleProfile.Address) gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, response, nil) } // duplicateName godoc // @Summary User profile name duplicate check. // @Description 유저 이름 중복체크 // @Schemes // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param name path string true "check duplicate name" // @Success 200 {string} OK! // @Router /user/duplicate/{name} [get] func (u UserV1Router) duplicateName(c *gin.Context) { name := c.Param("name") if name == "" { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value")) return } _bool, err := common.DuplicateValue(u.rDB, "user", name) if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) return } else if _bool { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.DuplicateValue, nil, errors.New("duplicate value")) return } gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, nil) } // tokenTransfer godoc // @Summary token transfer // @Description 출금하기 // @Schemes // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param Transfer body Transfer true "currency: mf1, mr, eth" // @Success 200 {string} OK! // @Router /user/transfer [post] func (u UserV1Router) tokenTransfer(c *gin.Context) { userID, _err := gauth.GetCurrentUserIDToInt64(c) if _err != nil || userID == 0 { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, _err) return } request := Transfer{} if err := c.ShouldBindJSON(&request); err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) return } wallet := models.UserWallet{} err := u.rDB.Where("user_id = ?", userID).Find(&wallet).Error if errors.Is(err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } setting := models.Setting{} err = u.rDB.Find(&setting).Error if errors.Is(err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } if request.Code != wallet.CertificationCode.String || request.Amount != wallet.RequestBalance.Float64 || request.Currency != wallet.RequestCurrency { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } //NOTE x 가용가능한 값만 출금할 수 있다. if request.Currency == "eth" { _, availableBalance := helpers.GetSelectedBalance(u.rDB, userID, request.Currency, wallet.Address) if (request.Amount + setting.GasDeposit) > availableBalance { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("lack of holding balance")) return } } else { _, availableBalance := helpers.GetSelectedBalance(u.rDB, userID, request.Currency, wallet.Address) if request.Amount > availableBalance { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("lack of holding balance")) return } _, availableEthBalance := helpers.GetSelectedBalance(u.rDB, userID, "eth", wallet.Address) if setting.GasDeposit > availableEthBalance { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("lack of holding balance")) return } } // ToWallet := models.UserWallet{} // err = u.rDB.Where("address = ?", request.ToAddress).Find(&ToWallet).Error // if errors.Is(err, gorm.ErrRecordNotFound) { // gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) // return // } var txHash *types.Transaction amount := int64(request.Amount * 100000) // //FIXME b 출금하기 함수 호출 if request.Currency == "eth" { err, tx := helpers.TransferETH(wallet.PrivateKey, request.ToAddress, big.NewInt(amount).Mul(big.NewInt(amount), big.NewInt(common.Decimal13))) if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } txHash = tx } else if request.Currency == "mf" { err, tx := helpers.TransferMetaFinance(wallet.PrivateKey, request.ToAddress, big.NewInt(amount).Mul(big.NewInt(amount), big.NewInt(common.Decimal13))) if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } txHash = tx } else if request.Currency == "mr" { err, tx := helpers.TransferMetaRare(wallet.PrivateKey, request.ToAddress, big.NewInt(amount).Mul(big.NewInt(amount), big.NewInt(common.Decimal13))) if err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } txHash = tx } gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, txHash.Hash().String(), nil) } // certificationCode godoc // @Summary send certification code // @Description 출금하기 2차 인증 // @Schemes // @Tags user // @name get-string-by-int // @Accept json // @Produce json // @Param CertificationBody body CertificationBody true "currency: mf, mr, eth" // @Success 200 {string} OK! // @Router /user/certification/code [post] func (u UserV1Router) certificationCode(c *gin.Context) { userID, _err := gauth.GetCurrentUserIDToInt64(c) if _err != nil || userID == 0 { gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, _err) return } request := CertificationBody{} if err := c.ShouldBindJSON(&request); err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) return } authentication := models.UserAuthentication{} err := u.rDB.Where("user_id = ?", userID).Find(&authentication).Error if errors.Is(err, gorm.ErrRecordNotFound) { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) return } tx := u.mDB.Begin() defer common.DBTransaction(tx) _email := authentication.Email code, err := common.SendCertificationCode(_email, u.Env.GetString("smtp.email"), u.Env.GetString("smtp.password")) if err != nil { tx.Rollback() gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err) return } if err := u.mDB.Model(&models.UserWallet{}).Where("user_id = ?", userID).Updates(models.UserWallet{CertificationCode: null.StringFrom(code), RequestBalance: null.FloatFrom(request.Amount), RequestCurrency: request.Currency}).Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err) tx.Rollback() return } if err := tx.Commit().Error; err != nil { gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err) tx.Rollback() return } gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, nil) }