package main import ( "log" "os" "path/filepath" "strings" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "airwallex-admin/config" "airwallex-admin/handlers" "airwallex-admin/middleware" "airwallex-admin/models" ) func main() { config.Load() models.InitDB() models.InitializeDefaults(models.DB) r := gin.Default() r.Use(cors.New(cors.Config{ AllowAllOrigins: true, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization", "X-API-Key"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: false, })) // Health check api := r.Group("/api") api.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) // Auth routes (no auth required) auth := api.Group("/auth") auth.POST("/login", handlers.Login) // Protected admin routes (JWT required) protected := api.Group("") protected.Use(middleware.JWTAuth()) protected.GET("/auth/me", handlers.GetMe) protected.GET("/dashboard", handlers.GetDashboard) // Cards protected.GET("/cards", handlers.ListCards) protected.POST("/cards", handlers.CreateCard) protected.GET("/cards/:id", handlers.GetCard) protected.GET("/cards/:id/details", handlers.GetCardDetails) protected.PUT("/cards/:id", handlers.UpdateCard) // Cardholders protected.GET("/cardholders", handlers.ListCardholders) protected.POST("/cardholders", handlers.CreateCardholder) // Transactions protected.GET("/transactions", handlers.ListTransactions) protected.GET("/authorizations", handlers.ListAuthorizations) // Settings protected.GET("/settings", handlers.GetSettings) protected.PUT("/settings", handlers.UpdateSettings) protected.POST("/settings/test-connection", handlers.TestConnection) protected.POST("/settings/test-proxy", handlers.TestProxy) // Tokens protected.GET("/tokens", handlers.ListTokens) protected.POST("/tokens", handlers.CreateToken) protected.DELETE("/tokens/:id", handlers.DeleteToken) // Logs protected.GET("/card-logs", handlers.ListCardLogs) protected.GET("/audit-logs", handlers.ListAuditLogs) // External API (API Key auth) v1 := api.Group("/v1") v1.Use(middleware.APIKeyAuth()) v1.POST("/cards/create", middleware.CheckPermission("create_cards"), handlers.ExternalCreateCard) v1.GET("/cards", middleware.CheckPermission("read_cards"), handlers.ExternalListCards) v1.GET("/cards/:id", middleware.CheckPermission("read_cards"), handlers.ExternalGetCard) v1.POST("/cards/:id/freeze", middleware.CheckPermission("create_cards"), handlers.ExternalFreezeCard) v1.GET("/transactions", middleware.CheckPermission("read_transactions"), handlers.ExternalListTransactions) v1.GET("/balance", middleware.CheckPermission("read_balance"), handlers.ExternalGetBalance) // Static files + SPA fallback distPath := "frontend/dist" if _, err := os.Stat(distPath); err == nil { r.Static("/assets", filepath.Join(distPath, "assets")) r.StaticFile("/favicon.ico", filepath.Join(distPath, "favicon.ico")) r.NoRoute(func(c *gin.Context) { if strings.HasPrefix(c.Request.URL.Path, "/api/") { c.JSON(404, gin.H{"detail": "Not found"}) return } c.File(filepath.Join(distPath, "index.html")) }) } port := config.Cfg.Port if port == "" { port = "8000" } log.Printf("Starting server on :%s", port) if err := r.Run(":" + port); err != nil { log.Fatalf("Failed to start server: %v", err) } }