package common import ( "crypto/rand" "crypto/tls" "errors" "fmt" "io" "math" "math/big" "time" "github.com/dgrijalva/jwt-go" "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" "github.com/metarare/metarare_api/contracts/operator" "github.com/metarare/metarare_api/helpers" "github.com/metarare/metarare_api/helpers/gauth" "github.com/metarare/metarare_api/models" "github.com/spf13/viper" gomail "gopkg.in/mail.v2" "gorm.io/gorm" ) var ( EMAIL_HOST string = "smtp.mailplug.co.kr" EMAIL_PORT int = 465 EMAIL_SUBJECT string = "Certification Code\n" EMAIL_MIME string = "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" ) type Email struct { From string `json:"from,omitempty"` Password string `json:"password,omitempty"` ToList string `json:"toList,omitempty"` Host string `json:"host,omitempty"` Port string `json:"port,omitempty"` Subject string `json:"subject,omitempty"` Mime string `json:"mime,omitempty"` Message string `json:"message,omitempty"` } type Router struct { Engine *gin.Engine Port string Version *gin.RouterGroup Db DataBases Env *viper.Viper } type DataBases struct { MasterDB *gorm.DB ReadDB *gorm.DB } type JsonObject map[string]interface{} type AuthContainer struct { AccessToken string `json:"access_token,omitempty"` } type SwagStruct struct { Json string `json:"json"` } type FILTER_KEY string var Decimal13 = int64(math.Pow(10, 13)) // 10^13 type MakeHashData struct { OwnerAddress string `json:"owner_address"` CollectionAddress string `json:"collection_address"` TokenType string `json:"token_type"` TokenID int64 `json:"token_id"` Currency string `json:"currency"` Price float64 `json:"price"` Index int64 `json:"index"` TotalIndex int64 `json:"total_index"` CreationTax int64 `json:"creation_tax"` TreasuryTax float64 `json:"treasury_tax"` TreasuryAddress string `json:"treasury_address"` TargetPrivateKey string `json:"target_private_key"` PrevTreasuryTax int64 `json:"prev_treasury_tax"` } const ( NETWORK_ETH = FILTER_KEY("eth") NETWORK_BSC = FILTER_KEY("bsc") NETWORK_MR = FILTER_KEY("mr") ) const ( CATEGORY_ALL = FILTER_KEY("all") CATEGORY_ART = FILTER_KEY("art") CATEGORY_GAME = FILTER_KEY("game") ) const ( SALETYPE_FIXED = FILTER_KEY("fixed") SALETYPE_BID = FILTER_KEY("bid") SALETYPE_TIME = FILTER_KEY("time") ) const ( CURRENCY_ETH = FILTER_KEY("eth") CURRENCY_MR = FILTER_KEY("mr") CURRENCY_MF = FILTER_KEY("mf") ) const ( CURRENCY_AMOUNT_START = FILTER_KEY("currency_amount_strart") CURRENCY_AMOUNT_END = FILTER_KEY("currency_amount_end") ) var NETWORKS = []FILTER_KEY{NETWORK_ETH, NETWORK_BSC, NETWORK_MR} var CATEGORIES = []FILTER_KEY{CATEGORY_ALL, CATEGORY_ART, CATEGORY_GAME} var SALETYPES = []FILTER_KEY{SALETYPE_FIXED, SALETYPE_BID, SALETYPE_TIME} var CURRENCIES = []FILTER_KEY{CURRENCY_ETH, CURRENCY_MF, CURRENCY_MR} var ( tt256 = BigPow(2, 256) tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) MaxBig256 = new(big.Int).Set(tt256m1) ) type Filter struct { Network string `json:"network,omitempty"` Category string `json:"category,omitempty"` CollectionName string `json:"collection_name,omitempty"` SaleType string `json:"sale_type,omitempty"` Currency string `json:"currency,omitempty"` CurrencyAmountStart string `json:"currency_amount_start,omitempty"` CurrencyAmountEnd string `json:"currency_amount_end,omitempty"` SaleStatus string `json:"sale_status,omitempty"` // for 개인페이지의 판매중 Offset int `json:"offset,omitempty" default:"-1"` Limit int `json:"limit,omitempty" default:"-1"` TokenName string `json:"token_name,omitempty"` } type ExpItem struct { // exploreItem CollectionID uint64 `json:"collection_id"` CollectionThumbnailImage string `json:"collection_thumbnail_image"` CollectionName string `json:"collection_name"` IsOfficialCollection bool `json:"is_official_collection"` UserID uint64 `json:"user_id"` IsArtist bool `json:"is_artist"` OwnerThumbnailImage string `json:"owner_thumbnail_image"` OwnerName string `json:"owner_name"` TokenID uint64 `json:"token_id"` ContentURL string `json:"content_url"` ContentTitle string `json:"content_title"` LastestPrice string `json:"lastest_price"` LastestCurrency string `json:"lastest_currency"` ItemIndex int `json:"item_index"` TotalCount int `json:"total_count"` SaleID uint64 `json:"sale_id"` SaleType string `json:"sale_type"` SaleStatus string `json:"sale_status"` FixedPrice string `json:"fixed_price"` CurrentCurrency string `json:"current_currency"` CurrentPrice string `json:"current_price"` BidCount int `json:"bid_count"` Likes int `json:"likes"` IsLike bool `json:"is_like"` StartPrice string `json:"start_price"` EndAt time.Time `json:"end_at"` CreatedAt time.Time `json:"created_at"` } type CollectionItem struct { CollectionID uint64 `json:"collection_id"` CoverImage string `json:"cover_image"` ThumbnailImage string `json:"thumbnail_image"` IsOfficial bool `json:"is_official"` CollectionName string `json:"collection_name"` UserID uint64 `json:"user_id"` OwnerName string `json:"owner_name"` TotalVolume float32 `json:"total_volume"` TotalItem int `json:"total_item"` } type ActivityItem struct { LogRelationID uint64 `json:"log_relation_id"` TokenContentURL string `json:"token_content_url"` TokenName string `json:"token_name"` LogType string `json:"log_type"` SaleUID string `json:"sale_uid"` ToAddress string `json:"to_address"` FromAddress string `json:"from_address"` Price string `json:"price"` Currency string `json:"currency"` SaleID uint64 `json:"sale_id"` TokenID uint64 `json:"token_id"` IsCancel bool `json:"is_cancel"` Tx string `json:"tx"` CreatedAt time.Time `json:"created_at"` FromUserProfile string `json:"from_user_profile"` FromUserName string `json:"from_user_name"` FromUserIsArtist bool `json:"from_user_is_artist"` ToUserProfile string `json:"to_user_profile"` ToUserName string `json:"to_user_name"` ToUserIsArtist bool `json:"to_user_is_artist"` } type BidItem struct { UserID uint64 UserThumbnailImage string UserName string IsCancel string ActionType string } type ArtistItem struct { ID uint64 UserID uint64 Team string Category string ThumbnailImage string } type TopSales struct { UserId uint64 `json:"artist_id"` ThumbnailImage string `json:"thumbnail_image"` CoverImage string `json:"cover_image"` Name string `json:"name"` Category string `json:"category"` AmountSales float64 `json:"amounts_sale"` } type SearchExpItems struct { Result []ExpItem Suggestion []CollectionItem } // SECTION jwt session func MakeClaims(userID int64) (JsonObject, JsonObject) { standardClaims := JsonObject{ "iss": "metarare.com", "aud": gauth.User, } customClaims := JsonObject{ gauth.Pack("user_id"): gauth.Pack(fmt.Sprintf("%d", userID)), } return standardClaims, customClaims } func MakeSession(standardClaims JsonObject, customClaims JsonObject) (error, AuthContainer) { currentTimestamp := time.Now() claims := jwt.MapClaims{} claims["sub"] = "access_key" claims["iat"] = currentTimestamp.Unix() claims["exp"] = currentTimestamp.Add(24 * time.Hour).Format(time.RFC3339) for k, v := range standardClaims { claims[k] = v } for k, v := range customClaims { claims[k] = v } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(gauth.ObtainJWTKey())) fmt.Println(tokenString, err) return nil, AuthContainer{AccessToken: tokenString} } func MakeJwtToken(userID uint64) AuthContainer { standardClaims, customClaims := MakeClaims(int64(userID)) _, authContainer := MakeSession(standardClaims, customClaims) return authContainer } // SECTION GenerateRandomHexString func GenerateRandomString(max int, prefix string) string { var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'} b := make([]byte, max) n, err := io.ReadAtLeast(rand.Reader, b, max) if n != max { panic(err) } for i := 0; i < len(b); i++ { b[i] = table[int(b[i])%len(table)] } return prefix + string(b) } func GenerateRandomNumber(max int) string { var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} b := make([]byte, max) n, err := io.ReadAtLeast(rand.Reader, b, max) if n != max { panic(err) } for i := 0; i < len(b); i++ { b[i] = table[int(b[i])%len(table)] } return string(b) } func GenerateUserUID(db *gorm.DB, prefix string) string { UID := GenerateRandomString(32, prefix) var count int64 for { db.Table("user").Where("uid = ?", UID).Count(&count) if count == 0 { break } UID = GenerateRandomString(32, prefix) } return string(UID) } func GenerateSaleUID(db *gorm.DB, prefix string) string { UID := GenerateRandomString(32, prefix) var count int64 for { db.Table("sale").Where("uid = ?", UID).Count(&count) if count == 0 { break } UID = GenerateRandomString(32, prefix) } return string(UID) } func GenerateTokenUID(db *gorm.DB, prefix string) string { UID := GenerateRandomString(32, prefix) var count int64 for { db.Table("token").Where("uid = ?", UID).Count(&count) if count == 0 { break } UID = GenerateRandomString(32, prefix) } return string(UID) } func GenerateUserName(db *gorm.DB) string { var name string var randName = [...]string{"가온해", "겨루", "여우별", "바람꽃", "이플", "소솜", "감또개", "옅구름", "뾰롱뾰롱", "나비잠", "도르레", "미쁘다", "산들림", "소록소록", "도담도담", "가온누리", "이쁘동이", "아슬라", "아리아", "개밥바라기", "다솜", "로운", "나르샤", "미리내", "하늬바람", "붙박이별", "그루잠", "여우비", "곰다시", "시나브로", "곰살굳다", "여낙낙하다", "달보드레", "눈바래다", "함초롬하다", "돋을별", "아련나래", "늘품", "아름드리", "넨다하다", "눅진하다", "안다미로", "초련", "닻별", "예그리나", "가시버시", "별하", "누리보듬", "비나리", "씨밀레"} idx, err := rand.Int(rand.Reader, big.NewInt(50)) if err != nil { panic(err) } number, err := rand.Int(rand.Reader, big.NewInt(1000000)) if err != nil { panic(err) } name = randName[idx.Int64()] + "_" + number.String() return name } // NOTE x 통화 업데이트 func UpdateCurrency(db *gorm.DB) (error, models.CurrencyPrice) { currencyPrice := models.CurrencyPrice{} err := db.Find(¤cyPrice).Error if errors.Is(err, gorm.ErrRecordNotFound) { return err, currencyPrice } t := time.Now().UTC().In(time.FixedZone("KST", 9*60*60)) compare := currencyPrice.UpdatedAt.Add(time.Hour * 1) if t.After(compare) { eth, mf1, mr := ObtainCurrencyPrice() if err := db.Model(¤cyPrice).Updates(models.CurrencyPrice{Eth: eth, Mf: mf1, Mr: mr, UpdatedAt: time.Now().UTC()}).Error; err != nil { return err, currencyPrice } } return nil, currencyPrice } func ConvertToId(db *gorm.DB, name string) (uint64, error) { _up := models.UserProfile{} err := db.Where("name = ?", name).Find(&_up).Error if errors.Is(err, gorm.ErrRecordNotFound) { return 0, err } return _up.UserID, nil } func ConvertToCollectionId(db *gorm.DB, name string) (uint64, error) { _cp := models.CollectionProfile{} err := db.Where("name = ?", name).Find(&_cp).Error if errors.Is(err, gorm.ErrRecordNotFound) { return 0, err } return _cp.CollectionID, nil } // NOTE x chekc duplicate value func DuplicateValue(db *gorm.DB, kind string, name string) (bool, error) { type Check struct { Name string `json:"name,omitempty"` } _check := Check{} var _table string if kind == "user" { _table = "user_profile" } else { _table = "collection_profile" } err := db.Select("name").Table(_table).Where("name = ?", name).Find(&_check).Error if err != nil { return true, err } else if _check.Name != "" { return true, nil } return false, nil } // NOTE x make sining func MakeSining(db *gorm.DB, info MakeHashData) (operator.MetaRareOperatorNFTVoucher, error) { _v := operator.MetaRareOperatorNFTVoucher{ Signer: common.HexToAddress(info.OwnerAddress), TokenAddress: common.HexToAddress(info.CollectionAddress), TokenId: big.NewInt(info.TokenID), CreationTax: big.NewInt(info.CreationTax), Uri: "https://nftinfo.meta-rare.net/", TreasuryAddress: common.HexToAddress(info.TreasuryAddress), TreasuryTax: big.NewInt(info.PrevTreasuryTax), } if info.TokenType == "erc721" { _v.TokenType = big.NewInt(0) _v.Balance = big.NewInt(0) _v.TotalBalance = big.NewInt(0) } else { _v.TokenType = big.NewInt(1) _v.Balance = big.NewInt(info.Index) _v.TotalBalance = big.NewInt(info.TotalIndex) } if info.Currency == "eth" { _v.AssetAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") } else if info.Currency == "mf" { _v.AssetAddress = common.HexToAddress(helpers.Addr_metaFinance) } else if info.Currency == "mr" { _v.AssetAddress = common.HexToAddress(helpers.Addr_metaRare) } // setting := models.Setting{} // _err := db.Find(&setting).Error // if errors.Is(_err, gorm.ErrRecordNotFound) { // return operator.MetaRareOperatorNFTVoucher{}, _err // } // _v.TreasuryTax = big.NewInt(int64(setting.Commission) * 100) // _v.TreasuryTax = big.NewInt(int64(info.PrevTreasuryTax)) // _v.TreasuryAddress = common.HexToAddress(setting.TreasuryAddress) _v.Price = big.NewInt(10).Mul(big.NewInt(int64(info.Price*100000)), big.NewInt(Decimal13)) return _v, nil } func BigPow(a, b int64) *big.Int { r := big.NewInt(a) return r.Exp(r, big.NewInt(b), nil) } // SECTION db trasaction func DBTransaction(db *gorm.DB) { if r := recover(); r != nil { db.Rollback() } } /* # NOTE x 2차 인증 이메일 발송 */ func SendCertificationCode(recipientList string, email string, password string) (string, error) { headers := make(map[string]string) headers["From"] = email headers["To"] = recipientList headers["Subject"] = EMAIL_SUBJECT message := "" for k, v := range headers { message += fmt.Sprintf("%s: %s\r", k, v) } _code := GenerateRandomNumber(6) msg := convertToEmailForm(_code) message += "\r" + msg body := (msg) m := gomail.NewMessage() // Set E-Mail sender m.SetHeader("From", email) // Set E-Mail receivers m.SetHeader("To", recipientList) // Set E-Mail subject m.SetHeader("Subject", EMAIL_SUBJECT) // Set E-Mail body. You can set plain text or html with text/html m.SetBody("text/html", fmt.Sprintf(body)) // Settings for SMTP server d := gomail.NewDialer(EMAIL_HOST, EMAIL_PORT, email, password) // This is only needed when SSL/TLS certificate is not valid on server. // In production this should be set to false. d.TLSConfig = &tls.Config{InsecureSkipVerify: true} // Now send E-Mail if err := d.DialAndSend(m); err != nil { fmt.Println(err) panic(err) } return _code, nil } func convertToEmailForm(certificationCode string) string { convertForm := `
` + certificationCode + `
@ MetaRare. Inc. All rights reserved.