package router import ( "net/http" "accounting-app/internal/cache" "accounting-app/internal/config" "accounting-app/internal/handler" "accounting-app/internal/middleware" "accounting-app/internal/repository" "accounting-app/internal/service" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // Setup creates and configures the Gin router func Setup(db *gorm.DB, yunAPIClient *service.YunAPIClient, cfg *config.Config) *gin.Engine { r := gin.Default() // Add CORS middleware r.Use(corsMiddleware()) // Health check endpoint r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "ok", "message": "Accounting App API is running", }) }) // Initialize repositories accountRepo := repository.NewAccountRepository(db) categoryRepo := repository.NewCategoryRepository(db) tagRepo := repository.NewTagRepository(db) classificationRepo := repository.NewClassificationRepository(db) transactionRepo := repository.NewTransactionRepository(db) transactionImageRepo := repository.NewTransactionImageRepository(db) recurringRepo := repository.NewRecurringTransactionRepository(db) exchangeRateRepo := repository.NewExchangeRateRepository(db) reportRepo := repository.NewReportRepository(db) budgetRepo := repository.NewBudgetRepository(db) piggyBankRepo := repository.NewPiggyBankRepository(db) allocationRuleRepo := repository.NewAllocationRuleRepository(db) allocationRecordRepo := repository.NewAllocationRecordRepository(db) billingRepo := repository.NewBillingRepository(db) repaymentRepo := repository.NewRepaymentRepository(db) appLockRepo := repository.NewAppLockRepository(db) ledgerRepo := repository.NewLedgerRepository(db) userSettingsRepo := repository.NewUserSettingsRepository(db) userRepo := repository.NewUserRepository(db) // Initialize auth services authService := service.NewAuthService(userRepo, cfg) var gitHubOAuthService *service.GitHubOAuthService if cfg.GitHubClientID != "" && cfg.GitHubClientSecret != "" { gitHubOAuthService = service.NewGitHubOAuthService(userRepo, authService, cfg) } authHandler := handler.NewAuthHandlerWithConfig(authService, gitHubOAuthService, cfg) authMiddleware := middleware.NewAuthMiddleware(authService) // Initialize services accountService := service.NewAccountService(accountRepo, db) categoryService := service.NewCategoryService(categoryRepo) tagService := service.NewTagService(tagRepo) classificationService := service.NewClassificationService(classificationRepo, categoryRepo) transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, db) imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir) recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db) exchangeRateService := service.NewExchangeRateService(exchangeRateRepo) reportService := service.NewReportService(reportRepo, exchangeRateRepo) pdfExportService := service.NewPDFExportService(reportRepo, transactionRepo, exchangeRateRepo) excelExportService := service.NewExcelExportService(reportRepo, transactionRepo, exchangeRateRepo) budgetService := service.NewBudgetService(budgetRepo, db) piggyBankService := service.NewPiggyBankService(piggyBankRepo, accountRepo, db) allocationRuleService := service.NewAllocationRuleService(allocationRuleRepo, allocationRecordRepo, accountRepo, piggyBankRepo, db) allocationRecordService := service.NewAllocationRecordService(allocationRecordRepo) billingService := service.NewBillingService(billingRepo, accountRepo, transactionRepo, db) repaymentService := service.NewRepaymentService(repaymentRepo, billingRepo, accountRepo, db) backupService := service.NewBackupService(db) appLockService := service.NewAppLockService(appLockRepo) ledgerService := service.NewLedgerService(ledgerRepo, db) reimbursementService := service.NewReimbursementService(db, transactionRepo, accountRepo) refundService := service.NewRefundService(db, transactionRepo, accountRepo) userSettingsService := service.NewUserSettingsService(userSettingsRepo) // Feature: financial-core-upgrade - Initialize new services subAccountService := service.NewSubAccountService(accountRepo, db) savingsPotService := service.NewSavingsPotService(accountRepo, transactionRepo, db) interestService := service.NewInterestService(accountRepo, transactionRepo, db) userSettingsServiceWithAccounts := service.NewUserSettingsServiceWithAccountRepo(userSettingsRepo, accountRepo) // Initialize handlers accountHandler := handler.NewAccountHandler(accountService) categoryHandler := handler.NewCategoryHandler(categoryService) tagHandler := handler.NewTagHandler(tagService) classificationHandler := handler.NewClassificationHandler(classificationService) transactionHandler := handler.NewTransactionHandler(transactionService) imageHandler := handler.NewImageHandler(imageService) recurringHandler := handler.NewRecurringTransactionHandler(recurringService) exchangeRateHandler := handler.NewExchangeRateHandlerWithClient(exchangeRateService, yunAPIClient) reportHandler := handler.NewReportHandler(reportService, pdfExportService, excelExportService) budgetHandler := handler.NewBudgetHandler(budgetService) piggyBankHandler := handler.NewPiggyBankHandler(piggyBankService) allocationRuleHandler := handler.NewAllocationRuleHandler(allocationRuleService) allocationRecordHandler := handler.NewAllocationRecordHandler(allocationRecordService) creditAccountHandler := handler.NewCreditAccountHandler(billingService, repaymentService) repaymentHandler := handler.NewRepaymentHandler(repaymentService) backupHandler := handler.NewBackupHandler(backupService) appLockHandler := handler.NewAppLockHandler(appLockService) ledgerHandler := handler.NewLedgerHandler(ledgerService) reimbursementHandler := handler.NewReimbursementHandler(reimbursementService) refundHandler := handler.NewRefundHandler(refundService) settingsHandler := handler.NewSettingsHandler(userSettingsService) // Feature: financial-core-upgrade - Initialize new handlers subAccountHandler := handler.NewSubAccountHandler(subAccountService) savingsPotHandler := handler.NewSavingsPotHandler(savingsPotService) defaultAccountHandler := handler.NewDefaultAccountHandler(userSettingsServiceWithAccounts) interestHandler := handler.NewInterestHandler(interestService, nil) // AI Bookkeeping Service and Handler aiBookkeepingService := service.NewAIBookkeepingService( cfg, transactionRepo, accountRepo, categoryRepo, userSettingsRepo, db, ) aiHandler := handler.NewAIHandler(aiBookkeepingService) // API v1 routes v1 := r.Group("/api/v1") { // Placeholder routes - will be implemented in subsequent tasks v1.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) // Register auth routes (public) authHandler.RegisterRoutes(v1) // Protected routes group - all routes requiring authentication protected := v1.Group("") protected.Use(authMiddleware.RequireAuth()) { // Register auth protected routes authHandler.RegisterProtectedRoutes(protected) // Register account routes accountHandler.RegisterRoutes(protected) // Register category routes categoryHandler.RegisterRoutes(protected) // Register tag routes tagHandler.RegisterRoutes(protected) // Register classification routes (smart classification suggestion) classificationHandler.RegisterRoutes(protected) // Register transaction routes transactionHandler.RegisterRoutes(protected) // Register image routes imageHandler.RegisterRoutes(protected) // Register recurring transaction routes recurringHandler.RegisterRoutes(protected) // Register exchange rate routes exchangeRateHandler.RegisterRoutes(protected) // Register budget routes budgetHandler.RegisterRoutes(protected) // Register piggy bank routes piggyBankHandler.RegisterRoutes(protected) // Register allocation rule routes allocationRuleHandler.RegisterRoutes(protected) // Register allocation record routes allocationRecordHandler.RegisterRoutes(protected) // Register credit account routes (bills and repayment) creditAccountHandler.RegisterRoutes(protected) // Register repayment plan routes repaymentHandler.RegisterRoutes(protected) // Register ledger routes ledgerHandler.RegisterRoutes(protected) // Register reimbursement routes reimbursementHandler.RegisterRoutes(protected) // Register refund routes refundHandler.RegisterRoutes(protected) // Register settings routes settingsHandler.RegisterRoutes(protected) // Feature: financial-core-upgrade - Register new routes // Sub-account routes subAccountHandler.RegisterRoutes(protected) // Savings pot routes savingsPotHandler.RegisterRoutes(protected) // Default account routes defaultAccountHandler.RegisterRoutes(protected) // Interest routes interestHandler.RegisterRoutes(protected) // AI bookkeeping routes aiHandler.RegisterRoutes(protected) // Register report routes protected.GET("/reports/summary", reportHandler.GetTransactionSummary) protected.GET("/reports/category", reportHandler.GetCategorySummary) protected.GET("/reports/trend", reportHandler.GetTrendData) protected.GET("/reports/comparison", reportHandler.GetComparisonData) protected.GET("/reports/assets", reportHandler.GetAssetsSummary) protected.GET("/reports/consumption-habits", reportHandler.GetConsumptionHabits) protected.GET("/reports/asset-liability-analysis", reportHandler.GetAssetLiabilityAnalysis) protected.POST("/reports/export", reportHandler.ExportReport) // Register backup routes protected.POST("/backup/export", backupHandler.ExportBackup) protected.POST("/backup/import", backupHandler.ImportBackup) protected.POST("/backup/verify", backupHandler.VerifyBackup) // Register app lock routes protected.GET("/app-lock/status", appLockHandler.GetStatus) protected.POST("/app-lock/password", appLockHandler.SetPassword) protected.POST("/app-lock/verify", appLockHandler.VerifyPassword) protected.POST("/app-lock/disable", appLockHandler.DisableLock) protected.POST("/app-lock/password/change", appLockHandler.ChangePassword) } } return r } // corsMiddleware handles Cross-Origin Resource Sharing func corsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization") c.Header("Access-Control-Max-Age", "86400") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) return } c.Next() } } // SetupWithRedis creates and configures the Gin router with Redis support for exchange rates // This function uses ExchangeRateHandlerV2 with Redis caching and SyncScheduler // Requirements: 2.1, 2.2, 2.3, 2.5 func SetupWithRedis(db *gorm.DB, yunAPIClient *service.YunAPIClient, redisClient *cache.RedisClient, cfg *config.Config) (*gin.Engine, *service.SyncScheduler) { r := gin.Default() // Add CORS middleware r.Use(corsMiddleware()) // Health check endpoint r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "ok", "message": "Accounting App API is running", }) }) // Initialize repositories accountRepo := repository.NewAccountRepository(db) categoryRepo := repository.NewCategoryRepository(db) tagRepo := repository.NewTagRepository(db) classificationRepo := repository.NewClassificationRepository(db) transactionRepo := repository.NewTransactionRepository(db) transactionImageRepo := repository.NewTransactionImageRepository(db) recurringRepo := repository.NewRecurringTransactionRepository(db) exchangeRateRepo := repository.NewExchangeRateRepository(db) reportRepo := repository.NewReportRepository(db) budgetRepo := repository.NewBudgetRepository(db) piggyBankRepo := repository.NewPiggyBankRepository(db) allocationRuleRepo := repository.NewAllocationRuleRepository(db) allocationRecordRepo := repository.NewAllocationRecordRepository(db) billingRepo := repository.NewBillingRepository(db) repaymentRepo := repository.NewRepaymentRepository(db) appLockRepo := repository.NewAppLockRepository(db) ledgerRepo := repository.NewLedgerRepository(db) userSettingsRepo := repository.NewUserSettingsRepository(db) userRepo := repository.NewUserRepository(db) // Initialize auth services authService := service.NewAuthService(userRepo, cfg) var gitHubOAuthService *service.GitHubOAuthService if cfg.GitHubClientID != "" && cfg.GitHubClientSecret != "" { gitHubOAuthService = service.NewGitHubOAuthService(userRepo, authService, cfg) } authHandler := handler.NewAuthHandlerWithConfig(authService, gitHubOAuthService, cfg) authMiddleware := middleware.NewAuthMiddleware(authService) // Initialize services accountService := service.NewAccountService(accountRepo, db) categoryService := service.NewCategoryService(categoryRepo) tagService := service.NewTagService(tagRepo) classificationService := service.NewClassificationService(classificationRepo, categoryRepo) transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, db) imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir) recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db) reportService := service.NewReportService(reportRepo, exchangeRateRepo) pdfExportService := service.NewPDFExportService(reportRepo, transactionRepo, exchangeRateRepo) excelExportService := service.NewExcelExportService(reportRepo, transactionRepo, exchangeRateRepo) budgetService := service.NewBudgetService(budgetRepo, db) piggyBankService := service.NewPiggyBankService(piggyBankRepo, accountRepo, db) allocationRuleService := service.NewAllocationRuleService(allocationRuleRepo, allocationRecordRepo, accountRepo, piggyBankRepo, db) allocationRecordService := service.NewAllocationRecordService(allocationRecordRepo) billingService := service.NewBillingService(billingRepo, accountRepo, transactionRepo, db) repaymentService := service.NewRepaymentService(repaymentRepo, billingRepo, accountRepo, db) backupService := service.NewBackupService(db) appLockService := service.NewAppLockService(appLockRepo) ledgerService := service.NewLedgerService(ledgerRepo, db) reimbursementService := service.NewReimbursementService(db, transactionRepo, accountRepo) refundService := service.NewRefundService(db, transactionRepo, accountRepo) userSettingsService := service.NewUserSettingsService(userSettingsRepo) // Feature: financial-core-upgrade - Initialize new services subAccountService := service.NewSubAccountService(accountRepo, db) savingsPotService := service.NewSavingsPotService(accountRepo, transactionRepo, db) interestService := service.NewInterestService(accountRepo, transactionRepo, db) userSettingsServiceWithAccounts := service.NewUserSettingsServiceWithAccountRepo(userSettingsRepo, accountRepo) // Initialize ExchangeRateServiceV2 with Redis cache exchangeRateCache := cache.NewExchangeRateCache(redisClient, cfg) exchangeRateServiceV2 := service.NewExchangeRateServiceV2(exchangeRateCache, yunAPIClient) // Initialize SyncScheduler with configured interval syncScheduler := service.NewSyncScheduler(exchangeRateServiceV2, cfg.SyncInterval) // Initialize handlers accountHandler := handler.NewAccountHandler(accountService) categoryHandler := handler.NewCategoryHandler(categoryService) tagHandler := handler.NewTagHandler(tagService) classificationHandler := handler.NewClassificationHandler(classificationService) transactionHandler := handler.NewTransactionHandler(transactionService) imageHandler := handler.NewImageHandler(imageService) recurringHandler := handler.NewRecurringTransactionHandler(recurringService) reportHandler := handler.NewReportHandler(reportService, pdfExportService, excelExportService) budgetHandler := handler.NewBudgetHandler(budgetService) piggyBankHandler := handler.NewPiggyBankHandler(piggyBankService) allocationRuleHandler := handler.NewAllocationRuleHandler(allocationRuleService) allocationRecordHandler := handler.NewAllocationRecordHandler(allocationRecordService) creditAccountHandler := handler.NewCreditAccountHandler(billingService, repaymentService) repaymentHandler := handler.NewRepaymentHandler(repaymentService) backupHandler := handler.NewBackupHandler(backupService) appLockHandler := handler.NewAppLockHandler(appLockService) ledgerHandler := handler.NewLedgerHandler(ledgerService) reimbursementHandler := handler.NewReimbursementHandler(reimbursementService) refundHandler := handler.NewRefundHandler(refundService) settingsHandler := handler.NewSettingsHandler(userSettingsService) // Feature: financial-core-upgrade - Initialize new handlers subAccountHandler := handler.NewSubAccountHandler(subAccountService) savingsPotHandler := handler.NewSavingsPotHandler(savingsPotService) defaultAccountHandler := handler.NewDefaultAccountHandler(userSettingsServiceWithAccounts) interestHandler := handler.NewInterestHandler(interestService, nil) // AI Bookkeeping Service and Handler for Redis setup aiBookkeepingServiceRedis := service.NewAIBookkeepingService( cfg, transactionRepo, accountRepo, categoryRepo, userSettingsRepo, db, ) aiHandlerRedis := handler.NewAIHandler(aiBookkeepingServiceRedis) // Initialize ExchangeRateHandlerV2 with Redis-backed service and scheduler exchangeRateHandlerV2 := handler.NewExchangeRateHandlerV2(exchangeRateServiceV2, syncScheduler) // API v1 routes v1 := r.Group("/api/v1") { // Placeholder routes - will be implemented in subsequent tasks v1.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) // Register auth routes (public) authHandler.RegisterRoutes(v1) // Protected routes group protected := v1.Group("") protected.Use(authMiddleware.RequireAuth()) { // Register auth protected routes authHandler.RegisterProtectedRoutes(protected) } // Register account routes accountHandler.RegisterRoutes(v1) // Register category routes categoryHandler.RegisterRoutes(v1) // Register tag routes tagHandler.RegisterRoutes(v1) // Register classification routes (smart classification suggestion) classificationHandler.RegisterRoutes(v1) // Register transaction routes transactionHandler.RegisterRoutes(v1) // Register image routes imageHandler.RegisterRoutes(v1) // Register recurring transaction routes recurringHandler.RegisterRoutes(v1) // Register exchange rate routes (V2 with Redis caching) // Routes: // - GET /api/v1/exchange-rates - Get all rates with sync status // - GET /api/v1/exchange-rates/:currency - Get single currency rate // - POST /api/v1/exchange-rates/convert - Currency conversion // - POST /api/v1/exchange-rates/refresh - Manual refresh // - GET /api/v1/exchange-rates/sync-status - Get sync status exchangeRateHandlerV2.RegisterRoutes(v1) // Register budget routes budgetHandler.RegisterRoutes(v1) // Register piggy bank routes piggyBankHandler.RegisterRoutes(v1) // Register allocation rule routes allocationRuleHandler.RegisterRoutes(v1) // Register allocation record routes allocationRecordHandler.RegisterRoutes(v1) // Register credit account routes (bills and repayment) creditAccountHandler.RegisterRoutes(v1) // Register repayment plan routes repaymentHandler.RegisterRoutes(v1) // Register ledger routes ledgerHandler.RegisterRoutes(v1) // Register reimbursement routes reimbursementHandler.RegisterRoutes(v1) // Register refund routes refundHandler.RegisterRoutes(v1) // Register settings routes settingsHandler.RegisterRoutes(v1) // Feature: financial-core-upgrade - Register new routes // Sub-account routes subAccountHandler.RegisterRoutes(v1) // Savings pot routes savingsPotHandler.RegisterRoutes(v1) // Default account routes defaultAccountHandler.RegisterRoutes(v1) // Interest routes interestHandler.RegisterRoutes(v1) // AI bookkeeping routes aiHandlerRedis.RegisterRoutes(v1) // Register report routes v1.GET("/reports/summary", reportHandler.GetTransactionSummary) v1.GET("/reports/category", reportHandler.GetCategorySummary) v1.GET("/reports/trend", reportHandler.GetTrendData) v1.GET("/reports/comparison", reportHandler.GetComparisonData) v1.GET("/reports/assets", reportHandler.GetAssetsSummary) v1.GET("/reports/consumption-habits", reportHandler.GetConsumptionHabits) v1.GET("/reports/asset-liability-analysis", reportHandler.GetAssetLiabilityAnalysis) v1.POST("/reports/export", reportHandler.ExportReport) // Register backup routes v1.POST("/backup/export", backupHandler.ExportBackup) v1.POST("/backup/import", backupHandler.ImportBackup) v1.POST("/backup/verify", backupHandler.VerifyBackup) // Register app lock routes v1.GET("/app-lock/status", appLockHandler.GetStatus) v1.POST("/app-lock/password", appLockHandler.SetPassword) v1.POST("/app-lock/verify", appLockHandler.VerifyPassword) v1.POST("/app-lock/disable", appLockHandler.DisableLock) v1.POST("/app-lock/password/change", appLockHandler.ChangePassword) } return r, syncScheduler }