123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- package collection
- import (
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "github.com/gin-gonic/gin"
- "github.com/guregu/null"
- "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 CollectionV1Router struct {
- group *gin.RouterGroup
- mDB *gorm.DB
- rDB *gorm.DB
- awsConf util.AWSConfs
- Env *viper.Viper
- }
- type RegisterCollection struct {
- Name string `json:"name" binding:"required"`
- Symbol string `json:"symbol" binding:"required"`
- Description string `json:"description" binding:"required"`
- Type string `json:"type" binding:"required"`
- }
- type ModifyStruct struct {
- CollectionID uint64 `json:"collection_id"`
- Description string `json:"description"`
- }
- type ReturnCollectionData struct {
- Collection models.Collection `json:"collection"`
- CollectionProfile models.CollectionProfile `json:"collection_profile"`
- TxHash string `json:"tx_hash"`
- }
- func NewCollectionV1Router(r common.Router, basePath string) CollectionV1Router {
- co := CollectionV1Router{
- 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"),
- },
- }
- co.group.POST("", co.registerCollection)
- co.group.GET(":collection_name", co.getCollectionInfo)
- co.group.POST("item/:collection_name", co.getItemsInCollection)
- co.group.POST("activity/:collection_name", co.getActivitiesInCollection)
- co.group.GET("duplicate/:name", co.duplicateName)
- co.group.PATCH("", co.updateCollection)
- return co
- }
- // registerCollection godoc
- // @Summary create collection
- // @Description 컬렉션 등록, json struct = {name: string, symbol: string, description: string, type: string}
- // @Schemes
- // @security ApiKeyAuth
- // @Tags collection
- // @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 "name, symbol, description"
- // @Success 200 {string} OK
- // @Router /collection [post]
- func (co CollectionV1Router) registerCollection(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 := RegisterCollection{}
- if err := json.Unmarshal([]byte(_request[0]), &request); err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, nil)
- return
- }
- thumbnailImage := form.File["thumbnailImage"]
- if thumbnailImage == nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- coverImage := form.File["coverImage"]
- if coverImage == nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- wallet := models.UserWallet{}
- if err := co.rDB.Where("user_id = ?", userID).Find(&wallet).Error; err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
- return
- }
- /*
- FIXME ethclient 최소 네트워크 수수료 보유 여부를 확인하는 로직
- 현재 보유중인 이더와, 플랫폼 내에서 사용가능한 이더 가져오기.
- 사용가능한 이더 값으로 최소 가스비를 확인한다.
- */
- setting := models.Setting{}
- _err := co.rDB.Find(&setting).Error
- if errors.Is(_err, gorm.ErrRecordNotFound) {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.NotFoundRecord, nil, _err)
- return
- }
- _, availableEth := helpers.GetSelectedBalance(co.rDB, userID, "eth", wallet.Address)
- if setting.GasDeposit > availableEth {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("you don't have enough points"))
- return
- }
- tx := co.mDB.Begin()
- defer common.DBTransaction(tx)
- collection := models.Collection{
- ContractAddress: "temp",
- OwnerAddress: wallet.Address,
- Network: "eth",
- Symbol: request.Symbol,
- IsOfficial: 0,
- Status: "visible",
- Type: request.Type, //erc721, erc1155
- }
- if err := tx.Save(&collection).Error; err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
- tx.Rollback()
- return
- }
- var thumbnailURL string
- thumbnailPath := fmt.Sprintf("collection/thumbnail/%d", collection.ID)
- for _, f := range thumbnailImage {
- _thumbnailURL, err := util.UploadToS3(co.awsConf, thumbnailPath, f)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err)
- tx.Rollback()
- return
- }
- thumbnailURL = _thumbnailURL
- }
- var coverURL string
- coverPath := fmt.Sprintf("collection/cover/%d", collection.ID)
- for _, f := range coverImage {
- _coverURL, err := util.UploadToS3(co.awsConf, coverPath, f)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err)
- tx.Rollback()
- return
- }
- coverURL = _coverURL
- }
- profile := models.CollectionProfile{
- CollectionID: collection.ID,
- Name: request.Name,
- Description: request.Description,
- ThumbnailImage: thumbnailURL,
- CoverImage: null.StringFrom(coverURL),
- }
- if err := tx.Save(&profile).Error; err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
- tx.Rollback()
- return
- }
- res := ReturnCollectionData{}
- res.Collection = collection
- res.CollectionProfile = profile
- var _collectionAddress string
- if request.Type == "erc721" {
- body := helpers.EIP721{
- NFTBasic: &helpers.NFTBasic{
- Name: request.Name,
- Symbol: request.Symbol,
- OwnerAddress: wallet.Address,
- },
- BaseURI: "https://nftinfo.meta-rare.net/",
- }
- err, EIP721 := helpers.CreateCollection721(body, wallet.PrivateKey)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
- tx.Rollback()
- return
- }
- res.TxHash = EIP721.CollectionInfo.Tx.Hash().String()
- _collectionAddress = EIP721.CollectionInfo.CollectionAddress
- } else if request.Type == "erc1155" {
- body := helpers.EIP1155{
- NFTBasic: &helpers.NFTBasic{
- Name: request.Name,
- Symbol: request.Symbol,
- OwnerAddress: wallet.Address,
- },
- CustomURI: "https://nftinfo.meta-rare.net/",
- }
- err, EIP1155 := helpers.CreateCollection1155(body, wallet.PrivateKey)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.InvalidParameterValue, nil, err)
- tx.Rollback()
- return
- }
- res.TxHash = EIP1155.CollectionInfo.Tx.Hash().String()
- _collectionAddress = EIP1155.CollectionInfo.CollectionAddress
- }
- if err := tx.Commit().Error; err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
- tx.Rollback()
- return
- }
- if err := co.mDB.Model(&models.Collection{}).Where("id = ?", collection.ID).Update("contract_address", _collectionAddress).Error; err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
- return
- }
- gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, res, nil)
- }
- // getCollectionInfo godoc
- // @Summary get collection info
- // @Description 컬렉션 페이지에 있는 기본 정보 (탭 데이터는 따로 가져와야 한다.)
- // @Schemes
- // @Tags collection
- // @Accept json
- // @Produce json
- // @Param collection_name path string true "collection name"
- // @Success 200 {object} view.CollectionInfo
- // @Router /collection/{collection_name} [get]
- func (co CollectionV1Router) getCollectionInfo(c *gin.Context) {
- userID, _ := gauth.GetCurrentUserIDToInt64(c)
- _collectionName := c.Param("collection_name")
- if _collectionName == "" {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- _id, err := common.ConvertToCollectionId(co.rDB, _collectionName)
- if _id == 0 || err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- ids := []uint64{_id}
- response, err := view.GetCollectionInfomation(co.rDB, ids, userID)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
- return
- } else if len(response) == 0 {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("not found recored"))
- return
- }
- gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, response[0], nil)
- }
- // getItemsInCollection godoc
- // @Summary get items in collection
- // @Description 컬렉션 페이지 아이템 탭
- // @Schemes
- // @Tags collection
- // @Accept json
- // @Produce json
- // @Param collection_name path string true "collection name"
- // @Param common.Filter body common.Filter true "filter data"
- // @Success 200 {object} []common.ExpItem
- // @Router /collection/item/{collection_name} [post]
- func (co CollectionV1Router) getItemsInCollection(c *gin.Context) {
- userID, _ := gauth.GetCurrentUserIDToInt64(c)
- _collectionName := c.Param("collection_name")
- if _collectionName == "" {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- _id, err := common.ConvertToCollectionId(co.rDB, _collectionName)
- if _id == 0 || err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- _filter := common.Filter{}
- if err := c.ShouldBindJSON(&_filter); err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, err)
- return
- }
- response := []common.ExpItem{}
- _err := view.GetExploreItemQuery(co.rDB, _filter, userID).Where("collection.id in (?)", _id).Find(&response).Error
- if errors.Is(_err, gorm.ErrRecordNotFound) {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.NotFoundRecord, nil, err)
- return
- }
- gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, response, nil)
- }
- // getActivitiesInCollection godoc
- // @Summary get activities in collection
- // @Description 컬렉션 페이지 활동 탭
- // @Schemes
- // @Tags collection
- // @Accept json
- // @Produce json
- // @Param collection_name path string true "collection name"
- // @Success 200 {object} []common.ExpItem
- // @Router /collection/activity/{collection_name} [post]
- func (co CollectionV1Router) getActivitiesInCollection(c *gin.Context) {
- //ANCHOR 수정 필요 컬렉션 조인이 안되어있음
- _collectionName := c.Param("collection_name")
- if _collectionName == "" {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- _id, err := common.ConvertToCollectionId(co.rDB, _collectionName)
- if _id == 0 || err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("invalid parameter value"))
- return
- }
- response := []common.ActivityItem{}
- _err := view.GetActivityItemQuery(co.rDB).Where("log_relation.collection_id = ?", _id).Find(&response).Error
- if errors.Is(_err, gorm.ErrRecordNotFound) {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.NotFoundRecord, nil, err)
- return
- }
- gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, response, nil)
- }
- // duplicateName godoc
- // @Summary collection profile name duplicate check.
- // @Description 컬렉션 이름 중복체크
- // @Schemes
- // @Tags collection
- // @name get-string-by-int
- // @Accept json
- // @Produce json
- // @Param name path string true "check duplicate name"
- // @Success 200 {string} OK!
- // @Router /collection/duplicate/{name} [get]
- func (co CollectionV1Router) 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(co.rDB, "collection", 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)
- }
- // updateCollection godoc
- // @Summary update collection
- // @Description 컬렉션 수정, json struct = {description: string, collection_id: uin64}
- // @Schemes
- // @security ApiKeyAuth
- // @Tags collection
- // @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 "description"
- // @Success 200 {string} OK
- // @Router /collection [patch]
- func (co CollectionV1Router) updateCollection(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 := ModifyStruct{}
- 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"]
- collection := models.Collection{}
- _err := co.rDB.Where("id = ?", request.CollectionID).Find(&collection).Error
- if errors.Is(_err, gorm.ErrRecordNotFound) {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err)
- return
- }
- _uw := models.UserWallet{}
- _err = co.rDB.Where("user_id = ?", userID).Find(&_uw).Error
- if errors.Is(_err, gorm.ErrRecordNotFound) {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err)
- return
- }
- if collection.OwnerAddress != _uw.Address {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, errors.New("permission not found"))
- return
- }
- collectionProfile := models.CollectionProfile{}
- _err = co.rDB.Where("collection_id = ?", collection.ID).Find(&collectionProfile).Error
- if errors.Is(_err, gorm.ErrRecordNotFound) {
- gerror.IntegratedResponseToRequest(c, http.StatusBadRequest, gerror.InvalidParameterValue, nil, _err)
- return
- }
- if thumbnailImage != nil {
- thumbnailPath := fmt.Sprintf("collection/thumbnail/%d", collection.ID)
- for _, f := range thumbnailImage {
- _thumbnailURL, err := util.UploadToS3(co.awsConf, thumbnailPath, f)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err)
- return
- }
- collectionProfile.ThumbnailImage = _thumbnailURL
- }
- }
- if coverImage != nil {
- coverPath := fmt.Sprintf("collection/cover/%d", collection.ID)
- for _, f := range coverImage {
- _coverURL, err := util.UploadToS3(co.awsConf, coverPath, f)
- if err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.Error3rdParty, nil, err)
- return
- }
- collectionProfile.CoverImage = null.StringFrom(_coverURL)
- }
- }
- if request.Description != "" {
- collectionProfile.Description = request.Description
- }
- if err := co.mDB.Save(&collectionProfile).Error; err != nil {
- gerror.IntegratedResponseToRequest(c, http.StatusInternalServerError, gerror.MysqlSaveError, nil, err)
- return
- }
- gerror.IntegratedResponseToRequest(c, http.StatusOK, gerror.OK, nil, nil)
- }
|