diff --git a/.gitignore b/.gitignore index e8a8b2c..59694d7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ *.dll *.so *.dylib -server -dyn +/server +/dyn # Test binary *.test diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..bbd71a7 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "git.dws.rip/DWS/dyn/internal/config" + "git.dws.rip/DWS/dyn/internal/database" + "git.dws.rip/DWS/dyn/internal/dns" + "git.dws.rip/DWS/dyn/internal/handlers" + "git.dws.rip/DWS/dyn/internal/middleware" + "github.com/gin-gonic/gin" +) + +func main() { + cfg := config.Load() + + if errs := cfg.Validate(); len(errs) > 0 { + for _, err := range errs { + log.Printf("Configuration error: %s", err) + } + os.Exit(1) + } + + db, err := database.New(cfg.DatabasePath) + if err != nil { + log.Fatalf("Failed to initialize database: %v", err) + } + defer db.Close() + + dnsClient := dns.NewClient( + cfg.TechnitiumURL, + cfg.TechnitiumToken, + cfg.TechnitiumUsername, + cfg.TechnitiumPassword, + ) + + rateLimiter := middleware.NewRateLimiter(cfg.RateLimitPerIP, cfg.RateLimitPerToken) + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.Use(gin.Recovery()) + + if len(cfg.TrustedProxies) > 0 { + router.SetTrustedProxies(cfg.TrustedProxies) + } + + router.Static("/static", "./web/static") + router.LoadHTMLGlob("web/templates/*") + + webHandler := handlers.NewWebHandler(db, cfg) + dynHandler := handlers.NewDynDNSHandler(db, dnsClient, cfg) + + router.GET("/", webHandler.Index) + + api := router.Group("/api") + api.Use(rateLimiter.RateLimitByIP()) + { + api.GET("/check", webHandler.CheckSubdomain) + api.POST("/claim", webHandler.ClaimSpace) + + nic := api.Group("/nic") + nic.Use(rateLimiter.RateLimitByToken()) + nic.GET("/update", dynHandler.Update) + } + + port := cfg.ServerPort + if port == "" { + port = "8080" + } + + srv := &http.Server{ + Addr: ":" + port, + Handler: router, + } + + go func() { + log.Printf("Server starting on port %s", port) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("Failed to start server: %v", err) + } + }() + + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + + log.Println("Shutting down server...") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := srv.Shutdown(ctx); err != nil { + log.Printf("Server forced to shutdown: %v", err) + } + + log.Println("Server exited") +}