package main import ( "auto-ssl/config" "auto-ssl/handlers" "auto-ssl/services" "log" "os" "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/robfig/cron/v3" ) func main() { cfg := config.Load() // Initialize database config.InitDB(cfg) // Setup Gin gin.SetMode(gin.ReleaseMode) r := gin.Default() // CORS for Vue frontend r.Use(cors.New(cors.Config{ AllowOrigins: []string{"*"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"}, AllowCredentials: true, })) // Serve static files for frontend r.Static("/assets", "./dist/assets") r.StaticFile("/favicon.ico", "./dist/favicon.ico") r.StaticFile("/", "./dist/index.html") r.NoRoute(func(c *gin.Context) { c.File("./dist/index.html") }) // API routes api := r.Group("/api") { certHandler := handlers.NewCertHandler(cfg) // Certificate management api.GET("/certificates", certHandler.ListCertificates) api.GET("/certificates/:id", certHandler.GetCertificate) api.POST("/certificates", certHandler.CreateCertificate) api.PUT("/certificates/:id", certHandler.UpdateCertificate) api.DELETE("/certificates/:id", certHandler.DeleteCertificate) api.POST("/certificates/:id/renew", certHandler.RenewCertificate) api.GET("/certificates/:id/files", certHandler.GetCertFiles) // Utility api.GET("/renewals/check", certHandler.CheckRenewals) api.GET("/stats", certHandler.Stats) } // Setup cron for auto-renewal (runs daily at 3:00 AM) c := cron.New() c.AddFunc("0 3 * * *", func() { log.Println("Running scheduled certificate renewal check...") var certs []config.Certificate config.DB.Where("auto_renew = ? AND status = ?", true, "active").Find(&certs) for _, cert := range certs { if cert.ExpiresAt != nil && time.Until(*cert.ExpiresAt).Hours() < float64(cert.RenewDays*24) { log.Printf("Auto-renewing certificate for %s (expires %s)", cert.Domain, cert.ExpiresAt.Format(time.RFC3339)) if err := services.RenewCertificate(&cert, cfg); err != nil { cert.Status = "error" cert.ErrorMessage = "auto renew: " + err.Error() log.Printf("Auto-renew failed for %s: %v", cert.Domain, err) } else { log.Printf("Auto-renew succeeded for %s", cert.Domain) } config.DB.Save(&cert) } } }) c.Start() // Setup HTTP server for ACME HTTP-01 challenges (port 80) httpPort := os.Getenv("HTTP_PORT") if httpPort == "" { httpPort = "80" } go func() { acme := gin.New() acme.Use(gin.Recovery()) // HTTP-01 challenge handler from lego log.Printf("ACME HTTP challenge server listening on :%s", httpPort) if err := acme.Run(":" + httpPort); err != nil { log.Printf("ACME HTTP server (port %s) exited: %v", httpPort, err) } }() log.Printf("AutoSSL server starting on :%s", cfg.Port) if err := r.Run(":" + cfg.Port); err != nil { log.Fatalf("Failed to start server: %v", err) } }