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) } }