201 lines
6.4 KiB
Go
201 lines
6.4 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"log"
|
||
|
|
|
||
|
|
"github.com/gin-contrib/cors"
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/admin"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/alp"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/articles"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/common"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/config"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/fundmarket"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/holders"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/lending"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/middleware"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/models"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/points"
|
||
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/users"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
)
|
||
|
|
|
||
|
|
func Migrate(db *gorm.DB) {
|
||
|
|
// Original tables
|
||
|
|
users.AutoMigrate()
|
||
|
|
db.AutoMigrate(&articles.ArticleModel{})
|
||
|
|
db.AutoMigrate(&articles.TagModel{})
|
||
|
|
db.AutoMigrate(&articles.FavoriteModel{})
|
||
|
|
db.AutoMigrate(&articles.ArticleUserModel{})
|
||
|
|
db.AutoMigrate(&articles.CommentModel{})
|
||
|
|
|
||
|
|
// AssetX core tables
|
||
|
|
db.AutoMigrate(&models.User{})
|
||
|
|
db.AutoMigrate(&models.HolderSnapshot{})
|
||
|
|
db.AutoMigrate(&models.Asset{})
|
||
|
|
db.AutoMigrate(&models.AssetPerformance{})
|
||
|
|
db.AutoMigrate(&models.APYSnapshot{})
|
||
|
|
db.AutoMigrate(&models.AssetCustody{})
|
||
|
|
db.AutoMigrate(&models.AssetAuditReport{})
|
||
|
|
|
||
|
|
// Config / content tables
|
||
|
|
db.AutoMigrate(&models.SystemContract{})
|
||
|
|
db.AutoMigrate(&models.ProductLink{})
|
||
|
|
|
||
|
|
// Points system tables
|
||
|
|
db.AutoMigrate(&models.PointsRule{})
|
||
|
|
db.AutoMigrate(&models.Season{})
|
||
|
|
db.AutoMigrate(&models.VIPTier{})
|
||
|
|
db.AutoMigrate(&models.InviteCode{})
|
||
|
|
db.AutoMigrate(&models.Invitation{})
|
||
|
|
db.AutoMigrate(&models.UserPointsSummary{})
|
||
|
|
db.AutoMigrate(&models.UserPointsRecord{})
|
||
|
|
db.AutoMigrate(&models.UserTeam{})
|
||
|
|
|
||
|
|
// ALP pool snapshots
|
||
|
|
db.AutoMigrate(&models.ALPSnapshot{})
|
||
|
|
|
||
|
|
// Liquidation bot records
|
||
|
|
db.AutoMigrate(&models.LiquidationRecord{})
|
||
|
|
db.AutoMigrate(&models.KnownBorrower{})
|
||
|
|
|
||
|
|
// Collateral buyer bot records
|
||
|
|
db.AutoMigrate(&models.CollateralBuyRecord{})
|
||
|
|
|
||
|
|
// Scanner state: drop old single-column unique index, then migrate with composite index
|
||
|
|
db.Exec("ALTER TABLE scanner_state DROP INDEX `uni_chain_id`") // ignore error if not exists
|
||
|
|
db.AutoMigrate(&models.ScannerState{})
|
||
|
|
|
||
|
|
// YT Swap event records (for "Your Total Earning" calculation)
|
||
|
|
db.AutoMigrate(&models.YTSwapRecord{})
|
||
|
|
|
||
|
|
// Audit / ops
|
||
|
|
db.AutoMigrate(&models.OperationLog{})
|
||
|
|
|
||
|
|
log.Println("✓ Database migration completed")
|
||
|
|
}
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
cfg := config.Load()
|
||
|
|
log.Println("✓ Configuration loaded")
|
||
|
|
|
||
|
|
var db *gorm.DB
|
||
|
|
if cfg.DBType == "mysql" {
|
||
|
|
db = common.InitMySQL()
|
||
|
|
} else {
|
||
|
|
db = common.Init()
|
||
|
|
}
|
||
|
|
|
||
|
|
Migrate(db)
|
||
|
|
|
||
|
|
sqlDB, err := db.DB()
|
||
|
|
if err != nil {
|
||
|
|
log.Println("failed to get sql.DB:", err)
|
||
|
|
} else {
|
||
|
|
defer sqlDB.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
common.InitRedis()
|
||
|
|
|
||
|
|
// Start holder scanner (continuous block scanning)
|
||
|
|
go holders.StartAllScanners()
|
||
|
|
|
||
|
|
// Start background snapshot services (run every hour)
|
||
|
|
go fundmarket.StartPriceSnapshot(cfg)
|
||
|
|
go alp.StartALPSnapshot(cfg)
|
||
|
|
go lending.StartLendingAPYSnapshot(cfg)
|
||
|
|
|
||
|
|
// Start liquidation bot (requires LIQUIDATOR_PRIVATE_KEY)
|
||
|
|
go lending.StartLiquidationBot(cfg)
|
||
|
|
|
||
|
|
// Start collateral buyer bot (requires COLLATERAL_BUYER_PRIVATE_KEY)
|
||
|
|
go lending.StartCollateralBuyerBot(cfg)
|
||
|
|
|
||
|
|
if cfg.GinMode == "release" {
|
||
|
|
gin.SetMode(gin.ReleaseMode)
|
||
|
|
}
|
||
|
|
r := gin.Default()
|
||
|
|
|
||
|
|
r.Use(cors.New(cors.Config{
|
||
|
|
AllowAllOrigins: true,
|
||
|
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||
|
|
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||
|
|
ExposeHeaders: []string{"Content-Length"},
|
||
|
|
AllowCredentials: false,
|
||
|
|
}))
|
||
|
|
|
||
|
|
r.RedirectTrailingSlash = false
|
||
|
|
|
||
|
|
// Serve uploaded files
|
||
|
|
r.Static("/uploads", "./uploads")
|
||
|
|
|
||
|
|
v1 := r.Group("/api")
|
||
|
|
|
||
|
|
// Public routes
|
||
|
|
users.UsersRegister(v1.Group("/users"))
|
||
|
|
articles.ArticlesAnonymousRegister(v1.Group("/articles"))
|
||
|
|
articles.TagsAnonymousRegister(v1.Group("/tags"))
|
||
|
|
users.ProfileRetrieveRegister(v1.Group("/profiles"))
|
||
|
|
|
||
|
|
v1.GET("/holders/stats", holders.GetStats)
|
||
|
|
v1.GET("/holders/:tokenType", holders.GetHoldersByType)
|
||
|
|
|
||
|
|
v1.GET("/contracts", admin.GetContracts)
|
||
|
|
|
||
|
|
v1.GET("/fundmarket/products", fundmarket.GetProducts)
|
||
|
|
v1.GET("/fundmarket/stats", fundmarket.GetStats)
|
||
|
|
v1.GET("/fundmarket/products/:id", fundmarket.GetProductByID)
|
||
|
|
v1.GET("/fundmarket/products/:id/history", fundmarket.GetProductHistory)
|
||
|
|
v1.GET("/fundmarket/products/:id/daily-returns", fundmarket.GetDailyReturns)
|
||
|
|
v1.GET("/fundmarket/net-deposited", fundmarket.GetNetDeposited)
|
||
|
|
|
||
|
|
v1.GET("/alp/stats", alp.GetALPStats)
|
||
|
|
v1.GET("/alp/history", alp.GetALPHistory)
|
||
|
|
|
||
|
|
v1.GET("/lending/position/:address", lending.GetUserPosition)
|
||
|
|
v1.GET("/lending/stats", lending.GetLendingStats)
|
||
|
|
v1.GET("/lending/markets", lending.GetLendingMarkets)
|
||
|
|
v1.GET("/lending/apy-history", lending.GetLendingAPYHistory)
|
||
|
|
v1.GET("/lending/tokens", lending.GetTokensInfo)
|
||
|
|
v1.GET("/lending/tokens/:assetCode", lending.GetTokenInfo)
|
||
|
|
v1.POST("/lending/supply", lending.SupplyUSDC)
|
||
|
|
v1.POST("/lending/withdraw", lending.WithdrawUSDC)
|
||
|
|
v1.POST("/lending/supply-collateral", lending.SupplyCollateral)
|
||
|
|
v1.POST("/lending/withdraw-collateral", lending.WithdrawCollateral)
|
||
|
|
v1.POST("/lending/borrow", lending.BorrowUSDC)
|
||
|
|
v1.POST("/lending/repay", lending.RepayUSDC)
|
||
|
|
|
||
|
|
v1.POST("/points/wallet-register", points.WalletRegister)
|
||
|
|
v1.GET("/points/dashboard", points.GetDashboard)
|
||
|
|
v1.GET("/points/leaderboard", points.GetLeaderboard)
|
||
|
|
v1.GET("/points/invite-code", points.GetInviteCode)
|
||
|
|
v1.POST("/points/bind-invite", points.BindInvite)
|
||
|
|
v1.GET("/points/team", points.GetTeamTVL)
|
||
|
|
v1.GET("/points/activities", points.GetActivities)
|
||
|
|
|
||
|
|
// Admin API (has its own auth + RequireAdmin middleware internally)
|
||
|
|
admin.RegisterRoutes(v1)
|
||
|
|
|
||
|
|
// Protected routes
|
||
|
|
v1.Use(middleware.AuthMiddleware(true))
|
||
|
|
{
|
||
|
|
users.UserRegister(v1.Group("/user"))
|
||
|
|
users.ProfileRegister(v1.Group("/profiles"))
|
||
|
|
articles.ArticlesRegister(v1.Group("/articles"))
|
||
|
|
|
||
|
|
holdersGroup := v1.Group("/holders")
|
||
|
|
holdersGroup.POST("/update", middleware.RequireAdmin(), holders.UpdateHolders)
|
||
|
|
}
|
||
|
|
|
||
|
|
r.GET("/api/ping", func(c *gin.Context) {
|
||
|
|
c.JSON(200, gin.H{"message": "pong", "version": "1.0.0-assetx"})
|
||
|
|
})
|
||
|
|
|
||
|
|
port := ":" + cfg.Port
|
||
|
|
log.Printf("✓ Server starting on port %s\n", cfg.Port)
|
||
|
|
if err := r.Run(port); err != nil {
|
||
|
|
log.Fatal("failed to start server:", err)
|
||
|
|
}
|
||
|
|
}
|