aboutsummaryrefslogtreecommitdiffstats
path: root/backend/cmd/api/main.go
blob: 4884d128e8c3c69f7737b3a036eb8a8b68e5c77b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package main

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"finance/backend/internal/config"
	"finance/backend/internal/database"
	"finance/backend/internal/logger"
	"finance/backend/internal/middleware"
	"finance/backend/internal/models"
	"finance/backend/internal/router"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
)

func main() {
	// Initialize logger
	log := logger.NewLogger(os.Stdout, logger.INFO)
	log.Info("Starting API server...")

	// Load configuration
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal("Failed to load configuration:", err)
	}

	// Set Gin mode based on environment
	if cfg.Environment == "production" {
		gin.SetMode(gin.ReleaseMode)
	} else {
		gin.SetMode(gin.DebugMode)
	}

	// Initialize database
	err = database.InitDatabase(cfg)
	if err != nil {
		log.Fatal("Failed to connect to database:", err)
	}

	// Auto migrate database models
	err = database.DB.AutoMigrate(
		&models.User{},
		&models.Account{},
		&models.Transaction{},
		&models.Goal{},
		&models.Loan{},
	)
	if err != nil {
		log.Fatal("Failed to migrate database:", err)
	}

	// Initialize the router from the internal/router package
	r := router.SetupRouter(cfg)

	// Add custom middleware
	r.Use(middleware.RequestLogger(log))
	r.Use(middleware.ErrorHandler())

	// Configure CORS
	r.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Accept", "Authorization"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		MaxAge:           12 * time.Hour,
	}))

	// Set up custom handlers for 404 and 405
	r.NoRoute(middleware.NotFoundHandler())
	r.NoMethod(middleware.MethodNotAllowedHandler())

	// Public utility endpoints
	r.GET("/health", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"status": "ok", "timestamp": time.Now().Unix()})
	})

	r.GET("/dbstatus", func(c *gin.Context) {
		sqlDB, err := database.DB.DB()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Database connection error"})
			return
		}
		err = sqlDB.Ping()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Database ping failed"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"status": "ok", "message": "Database connection is healthy"})
	})

	// Start server
	serverAddr := fmt.Sprintf("%s:%d", cfg.ServerHost, cfg.ServerPort)
	log.Info("Server starting on", serverAddr)
	if err := r.Run(serverAddr); err != nil {
		log.Fatal("Server failed to start:", err)
	}
}