user.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. package user
  2. import (
  3. "crypto/sha256"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "math/big"
  8. "net/http"
  9. "github.com/ethereum/go-ethereum/core/types"
  10. "github.com/gin-gonic/gin"
  11. "github.com/guregu/null"
  12. "github.com/metarare/metarare_api/auth"
  13. "github.com/metarare/metarare_api/common"
  14. "github.com/metarare/metarare_api/helpers"
  15. "github.com/metarare/metarare_api/helpers/gauth"
  16. "github.com/metarare/metarare_api/helpers/gerror"
  17. "github.com/metarare/metarare_api/models"
  18. "github.com/metarare/metarare_api/util"
  19. "github.com/metarare/metarare_api/view"
  20. "github.com/spf13/viper"
  21. "gorm.io/gorm"
  22. )
  23. type UserV1Router struct {
  24. group *gin.RouterGroup
  25. mDB *gorm.DB
  26. rDB *gorm.DB
  27. awsConf util.AWSConfs
  28. Env *viper.Viper
  29. }
  30. type AuthenticationSNS struct {
  31. Code string `json:"code" binding:"required"`
  32. Type string `json:"type" binding:"required"`
  33. }
  34. type ProfileInfo struct {
  35. Name string `json:"name" binding:"required"`
  36. Description string `json:"description" binding:"required"`
  37. Phone string `json:"phone"`
  38. Twitter string `json:"twitter"`
  39. }
  40. type Transfer struct {
  41. Currency string `json:"currency" binding:"required"`
  42. ToAddress string `json:"to_address" binding:"required"`
  43. Amount float64 `json:"amount" binding:"required"`
  44. Code string `json:"code" binding:"required"`
  45. }
  46. type CertificationBody struct {
  47. Currency string `json:"currency" binding:"required"`
  48. Amount float64 `json:"amount" binding:"required"`
  49. }
  50. func NewUserV1Router(r common.Router, basePath string) UserV1Router {
  51. u := UserV1Router{
  52. group: r.Version.Group(basePath),
  53. mDB: r.Db.MasterDB,
  54. rDB: r.Db.ReadDB,
  55. awsConf: util.AWSConfs{
  56. Region: r.Env.GetString("storage.region"),
  57. BucketName: r.Env.GetString("storage.bucket_name"),
  58. Access_key_id: r.Env.GetString("storage.access_key_id"),
  59. Access_key: r.Env.GetString("storage.access_key"),
  60. },
  61. Env: r.Env,
  62. }
  63. u.group.GET("redirect/url", u.getRedirectURL)
  64. u.group.POST("authenticate", u.userAuthentication)
  65. u.group.GET("authenticate/temp", u.tempAuthentication)
  66. u.group.GET("profile/:name", u.getUserProfile)
  67. u.group.PATCH("profile", u.updateUserProfile)
  68. u.group.POST("onsale/:name", u.getOnSaleItems)
  69. u.group.POST("collection/:name", u.getRelatedCollections)
  70. u.group.POST("like/:name", u.getLikeItems)
  71. u.group.POST("owned/:name", u.getOwnedItems)
  72. u.group.GET("activity/:name", u.getUserActivities)
  73. u.group.POST("bid", u.getuserBidHistory)
  74. u.group.GET("profile", u.getUserSimpleProfile)
  75. u.group.GET("duplicate/:name", u.duplicateName)
  76. u.group.POST("transfer", u.tokenTransfer)
  77. u.group.POST("certification/code", u.certificationCode)
  78. return u
  79. }
  80. // getRedirectURL godoc
  81. // @Summary sns redirect url
  82. // @Description SNS 로그인 버튼 클릭시 redirect_url을 받는 API, {type=google, kakao}
  83. // @Tags user
  84. // @name get-string-by-int
  85. // @Accept json
  86. // @Produce json
  87. // @Param type query string true "type"
  88. // @Success 200 {string} redirectURL
  89. // @Router /user/redirect/url [get]
  90. func (u UserV1Router) getRedirectURL(c *gin.Context) {
  91. var redirectURL string
  92. signinType := c.Query("type")
  93. if signinType != "google" && signinType != "kakao" {
  94. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  95. return
  96. }
  97. if signinType == "google" {
  98. redirectURL = auth.GetGoogleSignInURL()
  99. } else {
  100. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, nil)
  101. return
  102. }
  103. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, redirectURL, nil)
  104. return
  105. }
  106. // tempAuthentication godoc
  107. // @Summary temp user authentication
  108. // @Description SNS로그인 테스트 이전에 동일한 로직으로 유저 회원가입, 세션 획득을 위한 임시 함수, 입력된 이메일이 없으면 회원가입 이후 로그인, 있으면 로그인
  109. // @Tags user
  110. // @name get-string-by-int
  111. // @Accept json
  112. // @Produce json
  113. // @Param email query string true "email"
  114. // @Success 200 {string} authContainer
  115. // @Router /user/authenticate/temp [get]
  116. func (u UserV1Router) tempAuthentication(c *gin.Context) {
  117. request := c.Query("email")
  118. if request == "" {
  119. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  120. return
  121. }
  122. _auth := models.UserAuthentication{}
  123. if err := u.rDB.Where("email = ?", request).Find(&_auth).Error; err != nil {
  124. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  125. return
  126. } else if _auth.ID != 0 {
  127. //NOTE x 상태값 확인 후 로그인 세션 생성
  128. _user := models.User{}
  129. err := u.rDB.Where("id = ?", _auth.UserID).Find(&_user).Error
  130. if errors.Is(err, gorm.ErrRecordNotFound) {
  131. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  132. return
  133. } else if _user.Status != "stable" {
  134. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, errors.New(""))
  135. return
  136. }
  137. token := common.MakeJwtToken(_auth.UserID)
  138. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil)
  139. return
  140. }
  141. //NOTE x 회원가입 이후 로그인 세션 생성
  142. tx := u.mDB.Begin()
  143. defer common.DBTransaction(tx)
  144. user := models.User{
  145. UID: common.GenerateUserUID(tx, "user_"),
  146. Status: "stable",
  147. }
  148. if err := tx.Save(&user).Error; err != nil {
  149. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  150. tx.Rollback()
  151. return
  152. }
  153. authentication := models.UserAuthentication{
  154. UserID: user.ID,
  155. Email: request,
  156. Type: null.StringFrom("google"),
  157. }
  158. if err := tx.Save(&authentication).Error; err != nil {
  159. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  160. tx.Rollback()
  161. return
  162. }
  163. profile := models.UserProfile{
  164. UserID: user.ID,
  165. Name: null.StringFrom(common.GenerateUserName(tx)),
  166. }
  167. if err := tx.Save(&profile).Error; err != nil {
  168. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  169. tx.Rollback()
  170. return
  171. }
  172. pk, addr := helpers.GenerateWallet()
  173. str := addr + pk + "1"
  174. checkSum := sha256.Sum256([]byte(str))
  175. wallet := models.UserWallet{
  176. UserID: user.ID,
  177. Address: addr,
  178. PrivateKey: pk,
  179. CheckSum: fmt.Sprintf("%x", checkSum),
  180. EncVersion: 1,
  181. }
  182. if err := tx.Save(&wallet).Error; err != nil {
  183. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  184. tx.Rollback()
  185. return
  186. }
  187. if err := tx.Commit().Error; err != nil {
  188. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  189. tx.Rollback()
  190. return
  191. }
  192. token := common.MakeJwtToken(user.ID)
  193. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil)
  194. return
  195. }
  196. // userAuthentication godoc
  197. // @Summary sign-in
  198. // @Description redirect url 호출이후 받은 코드로 기존유저라면 로그인, 새로운 유저라면 가입 후 로그인
  199. // @Tags user
  200. // @name get-string-by-int
  201. // @Accept json
  202. // @Produce json
  203. // @Param AuthenticationSNS body AuthenticationSNS true "oauth data"
  204. // @Success 200 {string} authContainer
  205. // @Router /user/authenticate [post]
  206. func (u UserV1Router) userAuthentication(c *gin.Context) {
  207. var email string
  208. var _err error
  209. var req AuthenticationSNS
  210. if err := c.ShouldBindJSON(&req); err != nil {
  211. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  212. return
  213. }
  214. switch req.Type {
  215. case "google":
  216. g, err := auth.GetGoogleUserInfo(req.Code)
  217. if err != nil {
  218. _err = err
  219. break
  220. }
  221. email = g.Email
  222. default:
  223. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("required type parameter"))
  224. return
  225. }
  226. if _err != nil {
  227. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.GetSNSUserInfoError, nil, _err)
  228. return
  229. }
  230. _auth := models.UserAuthentication{}
  231. if err := u.rDB.Where("email = ?", email).Find(&_auth).Error; err != nil {
  232. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, _err)
  233. return
  234. } else if _auth.ID != 0 {
  235. //NOTE x 상태 확인 후 로그인 세션 생성
  236. _user := models.User{}
  237. err := u.rDB.Where("id = ?", _auth.UserID).Find(&_user).Error
  238. if errors.Is(err, gorm.ErrRecordNotFound) {
  239. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  240. return
  241. } else if _user.Status != "stable" {
  242. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, errors.New(""))
  243. return
  244. }
  245. token := common.MakeJwtToken(_auth.UserID)
  246. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil)
  247. return
  248. }
  249. //NOTE x 회원가입 이후 로그인 세션 생성
  250. tx := u.mDB.Begin()
  251. defer common.DBTransaction(tx)
  252. user := models.User{
  253. UID: common.GenerateUserUID(tx, "user_"),
  254. Status: "stable",
  255. }
  256. if err := tx.Save(&user).Error; err != nil {
  257. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  258. tx.Rollback()
  259. return
  260. }
  261. authentication := models.UserAuthentication{
  262. UserID: user.ID,
  263. Email: email,
  264. Type: null.StringFrom(req.Type),
  265. }
  266. if err := tx.Save(&authentication).Error; err != nil {
  267. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  268. tx.Rollback()
  269. return
  270. }
  271. profile := models.UserProfile{
  272. UserID: user.ID,
  273. Name: null.StringFrom(common.GenerateUserName(tx)),
  274. }
  275. if err := tx.Save(&profile).Error; err != nil {
  276. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  277. tx.Rollback()
  278. return
  279. }
  280. pk, addr := helpers.GenerateWallet()
  281. str := addr + pk + "1"
  282. checkSum := sha256.Sum256([]byte(str))
  283. wallet := models.UserWallet{
  284. UserID: user.ID,
  285. Address: addr,
  286. PrivateKey: pk,
  287. CheckSum: fmt.Sprintf("%x", checkSum),
  288. EncVersion: 1,
  289. }
  290. if err := tx.Save(&wallet).Error; err != nil {
  291. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  292. tx.Rollback()
  293. return
  294. }
  295. if err := tx.Commit().Error; err != nil {
  296. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  297. tx.Rollback()
  298. return
  299. }
  300. token := common.MakeJwtToken(user.ID)
  301. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, token, nil)
  302. return
  303. }
  304. /////////////////////////////////
  305. func (u UserV1Router) extractFilterData(c *gin.Context) (error, common.Filter) {
  306. var _filter common.Filter
  307. if err := c.ShouldBindJSON(&_filter); err != nil {
  308. return err, common.Filter{}
  309. } else {
  310. return nil, _filter
  311. }
  312. }
  313. // getUserProfile godoc
  314. // @Summary user basic profile data
  315. // @Description 유저 기본 정보 가져오기
  316. // @Schemes
  317. // @Tags user
  318. // @name get-string-by-int
  319. // @Accept json
  320. // @Produce json
  321. // @Param name path string true "user profile name"
  322. // @Success 200 {string} authContainer
  323. // @Router /user/profile/{name} [get]
  324. func (u UserV1Router) getUserProfile(c *gin.Context) {
  325. userID, err := gauth.GetCurrentUserIDToInt64(c)
  326. name := c.Param("name")
  327. if name == "" {
  328. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  329. return
  330. }
  331. _id, err := common.ConvertToId(u.rDB, name)
  332. if _id == 0 || err != nil {
  333. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err)
  334. return
  335. }
  336. err, userProfile := view.SelectUserInfo(u.rDB, _id, userID)
  337. if errors.Is(err, gorm.ErrRecordNotFound) {
  338. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  339. "message": "There is no matched user profile",
  340. })
  341. return
  342. }
  343. c.JSON(http.StatusOK, userProfile)
  344. }
  345. // updateUserProfile godoc
  346. // @Summary update user profile
  347. // @Description 유저 정보 업데이트
  348. // @Schemes
  349. // @security ApiKeyAuth
  350. // @Tags user
  351. // @Accept multipart/form-data
  352. // @Produce json
  353. // @Param thumbnailImage formData file true "thumbnail image"
  354. // @Param coverImage formData file true "cover image"
  355. // @Param json formData common.SwagStruct true "object"
  356. // @Success 200 {string} authContainer
  357. // @Router /user/profile [patch]
  358. func (u UserV1Router) updateUserProfile(c *gin.Context) {
  359. userID, err := gauth.GetCurrentUserIDToInt64(c)
  360. if err != nil || userID == 0 {
  361. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, err)
  362. return
  363. }
  364. form, err := c.MultipartForm()
  365. if err != nil {
  366. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MultipartError, nil, err)
  367. return
  368. }
  369. _request := form.Value["json"]
  370. if _request == nil {
  371. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  372. return
  373. }
  374. request := ProfileInfo{}
  375. if err := json.Unmarshal([]byte(_request[0]), &request); err != nil {
  376. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, nil)
  377. return
  378. }
  379. thumbnailImage := form.File["thumbnailImage"]
  380. coverImage := form.File["coverImage"]
  381. profile := models.UserProfile{}
  382. _err := u.rDB.Where("user_id = ?", userID).Find(&profile).Error
  383. if errors.Is(_err, gorm.ErrRecordNotFound) {
  384. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, _err)
  385. return
  386. }
  387. tx := u.mDB.Begin()
  388. defer common.DBTransaction(tx)
  389. var thumbnailImageURL string
  390. if thumbnailImage != nil {
  391. thumbnailImagePath := fmt.Sprintf("profile/thumbnail/%d", userID)
  392. for _, f := range thumbnailImage {
  393. thumbnailImageURL, err = util.UploadToS3(u.awsConf, thumbnailImagePath, f)
  394. if err != nil {
  395. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err)
  396. tx.Rollback()
  397. return
  398. }
  399. }
  400. profile.ThumbnailImage = null.StringFrom(thumbnailImageURL)
  401. }
  402. var coverImageURL string
  403. if coverImage != nil {
  404. coverImagePath := fmt.Sprintf("profile/cover/%d", userID)
  405. for _, f := range coverImage {
  406. coverImageURL, err = util.UploadToS3(u.awsConf, coverImagePath, f)
  407. if err != nil {
  408. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err)
  409. tx.Rollback()
  410. return
  411. }
  412. }
  413. profile.CoverImage = null.StringFrom(coverImageURL)
  414. }
  415. profile.Name = null.StringFrom(request.Name)
  416. profile.Description = null.StringFrom(request.Description)
  417. if request.Twitter != "" {
  418. profile.Twitter = null.StringFrom(request.Twitter)
  419. }
  420. if request.Phone != "" {
  421. profile.Phone = null.StringFrom(request.Phone)
  422. }
  423. if err := tx.Save(&profile).Error; err != nil {
  424. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  425. return
  426. }
  427. if err := tx.Commit().Error; err != nil {
  428. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  429. tx.Rollback()
  430. return
  431. }
  432. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, err)
  433. }
  434. // getOnSaleItems godoc
  435. // @Summary onesale list
  436. // @Description 판매중인 NFT 리스트
  437. // @Schemes
  438. // @security ApiKeyAuth
  439. // @Tags user
  440. // @Accept json
  441. // @Produce json
  442. // @Param common.Filter body common.Filter true "filter object"
  443. // @Param name path string true "user profile name"
  444. // @Success 200 {object} []common.ExpItem
  445. // @Router /user/onsale/{name} [post]
  446. func (u UserV1Router) getOnSaleItems(c *gin.Context) {
  447. name := c.Param("name")
  448. if name == "" {
  449. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  450. return
  451. }
  452. _id, err := common.ConvertToId(u.rDB, name)
  453. if _id == 0 || err != nil {
  454. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err)
  455. return
  456. }
  457. err, filter := u.extractFilterData(c)
  458. if err != nil {
  459. c.AbortWithStatusJSON(http.StatusBadRequest, filter)
  460. return
  461. }
  462. err, _sales := view.SelectOnsaleItems(u.rDB, _id, filter)
  463. if errors.Is(err, gorm.ErrRecordNotFound) {
  464. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  465. "message": "there is no onSale item",
  466. })
  467. return
  468. }
  469. c.JSON(http.StatusOK, _sales)
  470. }
  471. // getOwnedItems godoc
  472. // @Summary owned list
  473. // @Description 보유중인 NFT 리스트
  474. // @Schemes
  475. // @security ApiKeyAuth
  476. // @Tags user
  477. // @Accept json
  478. // @Produce json
  479. // @Param common.Filter body common.Filter true "filter object"
  480. // @Success 200 {object} []common.ExpItem
  481. // @Router /user/owned [post]
  482. func (u UserV1Router) getOwnedItems(c *gin.Context) {
  483. name := c.Param("name")
  484. if name == "" {
  485. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  486. return
  487. }
  488. _id, err := common.ConvertToId(u.rDB, name)
  489. if _id == 0 || err != nil {
  490. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err)
  491. return
  492. }
  493. err, filter := u.extractFilterData(c)
  494. if err != nil {
  495. c.AbortWithStatusJSON(http.StatusBadRequest, filter)
  496. return
  497. }
  498. err, _tokenInfos := view.SelectOwnedTokens(u.rDB, _id, filter)
  499. if errors.Is(err, gorm.ErrRecordNotFound) {
  500. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  501. "message": "NO matched tokenDatas",
  502. })
  503. return
  504. }
  505. c.JSON(http.StatusOK, _tokenInfos)
  506. }
  507. // getRelatedCollections godoc
  508. // @Summary collection list
  509. // @Description 유저 프로필 컬렉션 탭
  510. // @Schemes
  511. // @security ApiKeyAuth
  512. // @Tags user
  513. // @Accept json
  514. // @Produce json
  515. // @Param common.Filter body common.Filter true "filter object"
  516. // @Param name path string true "user profile name"
  517. // @Success 200 {object} []common.CollectionItem
  518. // @Router /user/collection/{name} [post]
  519. func (u UserV1Router) getRelatedCollections(c *gin.Context) {
  520. name := c.Param("name")
  521. if name == "" {
  522. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  523. return
  524. }
  525. _id, err := common.ConvertToId(u.rDB, name)
  526. if _id == 0 || err != nil {
  527. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err)
  528. return
  529. }
  530. err, filter := u.extractFilterData(c)
  531. if err != nil {
  532. c.AbortWithStatusJSON(http.StatusBadRequest, filter)
  533. return
  534. }
  535. err, _collections := view.SelectRelatedCollection(u.rDB, _id, filter)
  536. if errors.Is(err, gorm.ErrRecordNotFound) {
  537. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  538. "message": "No Matched collections",
  539. })
  540. return
  541. }
  542. c.JSON(http.StatusOK, _collections)
  543. }
  544. // getLikeItems godoc
  545. // @Summary user like list
  546. // @Description 유저 프로필 좋아요 탭
  547. // @Schemes
  548. // @Tags user
  549. // @name get-string-by-int
  550. // @Accept json
  551. // @Produce json
  552. // @Param common.Filter body common.Filter true "filter object"
  553. // @Param name path string true "user profile name"
  554. // @Success 200 {object} []common.ExpItem
  555. // @Router /user/like/{name} [post]
  556. func (u UserV1Router) getLikeItems(c *gin.Context) {
  557. name := c.Param("name")
  558. if name == "" {
  559. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  560. return
  561. }
  562. _id, err := common.ConvertToId(u.rDB, name)
  563. if _id == 0 || err != nil {
  564. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err)
  565. return
  566. }
  567. err, filter := u.extractFilterData(c)
  568. if err != nil {
  569. c.AbortWithStatusJSON(http.StatusBadRequest, filter)
  570. return
  571. }
  572. err, likeItems := view.SelectLikedTokens(u.rDB, _id, filter)
  573. if errors.Is(err, gorm.ErrRecordNotFound) {
  574. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  575. "message": "There is no liked items",
  576. })
  577. return
  578. }
  579. c.JSON(http.StatusOK, likeItems)
  580. }
  581. // getUserActivities godoc
  582. // @Summary user activity list
  583. // @Description 유저 프로필 활동 탭
  584. // @Schemes
  585. // @Tags user
  586. // @name get-string-by-int
  587. // @Accept json
  588. // @Produce json
  589. // @Param name path string true "user profile name"
  590. // @Success 200 {object} []common.ActivityItem
  591. // @Router /user/activity/{name} [get]
  592. func (u UserV1Router) getUserActivities(c *gin.Context) {
  593. name := c.Param("name")
  594. if name == "" {
  595. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  596. return
  597. }
  598. _id, err := common.ConvertToId(u.rDB, name)
  599. if _id == 0 || err != nil {
  600. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.NotFoundRecord, nil, err)
  601. return
  602. }
  603. err, likeItems := view.SelectUserActivities(u.rDB, _id)
  604. if errors.Is(err, gorm.ErrRecordNotFound) {
  605. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  606. "message": "There is no liked items",
  607. })
  608. return
  609. }
  610. c.JSON(http.StatusOK, likeItems)
  611. }
  612. // getuserBidHistory godoc
  613. // @Summary user bid list
  614. // @Description 유저 경매 히스토리
  615. // @Schemes
  616. // @security ApiKeyAuth
  617. // @Tags user
  618. // @name get-string-by-int
  619. // @Accept json
  620. // @Produce json
  621. // @Success 200 {object} []common.ExpItem
  622. // @Router /user/bid [post]
  623. func (u UserV1Router) getuserBidHistory(c *gin.Context) {
  624. userID, err := gauth.GetCurrentUserIDToInt64(c)
  625. if err != nil || userID == 0 {
  626. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, err)
  627. return
  628. }
  629. if err != nil {
  630. c.AbortWithStatus(http.StatusUnauthorized)
  631. return
  632. }
  633. err, filter := u.extractFilterData(c)
  634. if err != nil {
  635. c.AbortWithStatusJSON(http.StatusBadRequest, filter)
  636. return
  637. }
  638. err, likeItems := view.SelectUserBidHistory(u.rDB, userID, filter)
  639. if errors.Is(err, gorm.ErrRecordNotFound) {
  640. c.AbortWithStatusJSON(http.StatusOK, gin.H{
  641. "message": "There is no liked items",
  642. })
  643. return
  644. }
  645. c.JSON(http.StatusOK, likeItems)
  646. }
  647. // getUserSimpleProfile godoc
  648. // @Summary user simple profile data
  649. // @Description 헤더, 프로필 편집에 사용할 데이터
  650. // @Schemes
  651. // @Tags user
  652. // @security ApiKeyAuth
  653. // @name get-string-by-int
  654. // @Accept json
  655. // @Produce json
  656. // @Success 200 {object} view.SimpleProfile
  657. // @Router /user/profile [get]
  658. func (u UserV1Router) getUserSimpleProfile(c *gin.Context) {
  659. userID, err := gauth.GetCurrentUserIDToInt64(c)
  660. if err != nil || userID == 0 {
  661. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, err)
  662. return
  663. }
  664. response := view.SimpleProfile{}
  665. _err := view.GetSimpleProfile(u.rDB, userID).Find(&response.SimpleProfile).Error
  666. if errors.Is(_err, gorm.ErrRecordNotFound) {
  667. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err)
  668. return
  669. }
  670. _err = u.rDB.Select("setting.commission AS service_commission, setting.gas_deposit AS network_commission").Table("setting").Find(&response.Setting).Error
  671. if errors.Is(_err, gorm.ErrRecordNotFound) {
  672. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err)
  673. return
  674. }
  675. response.SimpleProfile.EthBalance, response.SimpleProfile.AvailableEthBalance = helpers.GetSelectedBalance(u.rDB, userID, "eth", response.SimpleProfile.Address)
  676. response.SimpleProfile.MfBalance, response.SimpleProfile.AvailableMfBalance = helpers.GetSelectedBalance(u.rDB, userID, "mf", response.SimpleProfile.Address)
  677. response.SimpleProfile.MrBalance, response.SimpleProfile.AvailableMrBalance = helpers.GetSelectedBalance(u.rDB, userID, "mr", response.SimpleProfile.Address)
  678. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, response, nil)
  679. }
  680. // duplicateName godoc
  681. // @Summary User profile name duplicate check.
  682. // @Description 유저 이름 중복체크
  683. // @Schemes
  684. // @Tags user
  685. // @name get-string-by-int
  686. // @Accept json
  687. // @Produce json
  688. // @Param name path string true "check duplicate name"
  689. // @Success 200 {string} OK!
  690. // @Router /user/duplicate/{name} [get]
  691. func (u UserV1Router) duplicateName(c *gin.Context) {
  692. name := c.Param("name")
  693. if name == "" {
  694. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
  695. return
  696. }
  697. _bool, err := common.DuplicateValue(u.rDB, "user", name)
  698. if err != nil {
  699. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  700. return
  701. } else if _bool {
  702. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.DuplicateValue, nil, errors.New("duplicate value"))
  703. return
  704. }
  705. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, nil)
  706. }
  707. // tokenTransfer godoc
  708. // @Summary token transfer
  709. // @Description 출금하기
  710. // @Schemes
  711. // @Tags user
  712. // @name get-string-by-int
  713. // @Accept json
  714. // @Produce json
  715. // @Param Transfer body Transfer true "currency: mf1, mr, eth"
  716. // @Success 200 {string} OK!
  717. // @Router /user/transfer [post]
  718. func (u UserV1Router) tokenTransfer(c *gin.Context) {
  719. userID, _err := gauth.GetCurrentUserIDToInt64(c)
  720. if _err != nil || userID == 0 {
  721. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, _err)
  722. return
  723. }
  724. request := Transfer{}
  725. if err := c.ShouldBindJSON(&request); err != nil {
  726. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  727. return
  728. }
  729. wallet := models.UserWallet{}
  730. err := u.rDB.Where("user_id = ?", userID).Find(&wallet).Error
  731. if errors.Is(err, gorm.ErrRecordNotFound) {
  732. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  733. return
  734. }
  735. setting := models.Setting{}
  736. err = u.rDB.Find(&setting).Error
  737. if errors.Is(err, gorm.ErrRecordNotFound) {
  738. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  739. return
  740. }
  741. if request.Code != wallet.CertificationCode.String || request.Amount != wallet.RequestBalance.Float64 || request.Currency != wallet.RequestCurrency {
  742. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  743. return
  744. }
  745. //NOTE x 가용가능한 값만 출금할 수 있다.
  746. if request.Currency == "eth" {
  747. _, availableBalance := helpers.GetSelectedBalance(u.rDB, userID, request.Currency, wallet.Address)
  748. if (request.Amount + setting.GasDeposit) > availableBalance {
  749. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("lack of holding balance"))
  750. return
  751. }
  752. } else {
  753. _, availableBalance := helpers.GetSelectedBalance(u.rDB, userID, request.Currency, wallet.Address)
  754. if request.Amount > availableBalance {
  755. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("lack of holding balance"))
  756. return
  757. }
  758. _, availableEthBalance := helpers.GetSelectedBalance(u.rDB, userID, "eth", wallet.Address)
  759. if setting.GasDeposit > availableEthBalance {
  760. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("lack of holding balance"))
  761. return
  762. }
  763. }
  764. // ToWallet := models.UserWallet{}
  765. // err = u.rDB.Where("address = ?", request.ToAddress).Find(&ToWallet).Error
  766. // if errors.Is(err, gorm.ErrRecordNotFound) {
  767. // gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  768. // return
  769. // }
  770. var txHash *types.Transaction
  771. amount := int64(request.Amount * 100000)
  772. // //FIXME b 출금하기 함수 호출
  773. if request.Currency == "eth" {
  774. err, tx := helpers.TransferETH(wallet.PrivateKey, request.ToAddress, big.NewInt(amount).Mul(big.NewInt(amount), big.NewInt(common.Decimal13)))
  775. if err != nil {
  776. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  777. return
  778. }
  779. txHash = tx
  780. } else if request.Currency == "mf" {
  781. err, tx := helpers.TransferMetaFinance(wallet.PrivateKey, request.ToAddress, big.NewInt(amount).Mul(big.NewInt(amount), big.NewInt(common.Decimal13)))
  782. if err != nil {
  783. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  784. return
  785. }
  786. txHash = tx
  787. } else if request.Currency == "mr" {
  788. err, tx := helpers.TransferMetaRare(wallet.PrivateKey, request.ToAddress, big.NewInt(amount).Mul(big.NewInt(amount), big.NewInt(common.Decimal13)))
  789. if err != nil {
  790. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  791. return
  792. }
  793. txHash = tx
  794. }
  795. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, txHash.Hash().String(), nil)
  796. }
  797. // certificationCode godoc
  798. // @Summary send certification code
  799. // @Description 출금하기 2차 인증
  800. // @Schemes
  801. // @Tags user
  802. // @name get-string-by-int
  803. // @Accept json
  804. // @Produce json
  805. // @Param CertificationBody body CertificationBody true "currency: mf, mr, eth"
  806. // @Success 200 {string} OK!
  807. // @Router /user/certification/code [post]
  808. func (u UserV1Router) certificationCode(c *gin.Context) {
  809. userID, _err := gauth.GetCurrentUserIDToInt64(c)
  810. if _err != nil || userID == 0 {
  811. gerror.IntegratedResponseToRequest(c, http.StatusUnauthorized, gerror.Unauthorized, nil, _err)
  812. return
  813. }
  814. request := CertificationBody{}
  815. if err := c.ShouldBindJSON(&request); err != nil {
  816. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  817. return
  818. }
  819. authentication := models.UserAuthentication{}
  820. err := u.rDB.Where("user_id = ?", userID).Find(&authentication).Error
  821. if errors.Is(err, gorm.ErrRecordNotFound) {
  822. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  823. return
  824. }
  825. tx := u.mDB.Begin()
  826. defer common.DBTransaction(tx)
  827. _email := authentication.Email
  828. code, err := common.SendCertificationCode(_email, u.Env.GetString("smtp.email"), u.Env.GetString("smtp.password"))
  829. if err != nil {
  830. tx.Rollback()
  831. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
  832. return
  833. }
  834. 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 {
  835. gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
  836. tx.Rollback()
  837. return
  838. }
  839. if err := tx.Commit().Error; err != nil {
  840. gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
  841. tx.Rollback()
  842. return
  843. }
  844. gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, nil)
  845. }