package main import ( "context" "log" "net/http" "os" "os/signal" "strconv" "syscall" "time" "gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/api" "gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/auth" "gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/config" "gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/database" ) func main() { ctx := context.Background() cfg, err := config.Load() if err != nil { log.Fatalf("Failed to load config: %v", err) } db, err := database.Open(ctx, cfg.DatabaseURL) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer db.Close() mux := http.NewServeMux() // Health check. mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) }) // Yggdrasil API. authHandler := auth.NewHandler(db, cfg) authHandler.RegisterRoutes(mux) // Public API. apiHandler := api.NewHandler(db, cfg) apiHandler.RegisterRoutes(mux) // TODO: register Admin, CAS routes. addr := ":" + itoa(cfg.Port) srv := &http.Server{ Addr: addr, Handler: mux, } // Graceful shutdown. done := make(chan os.Signal, 1) signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) go func() { log.Printf("MrixsCraft Server starting on %s", addr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Server error: %v", err) } }() <-done log.Println("Shutting down…") shutdownCtx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := srv.Shutdown(shutdownCtx); err != nil { log.Printf("Shutdown error: %v", err) } log.Println("Stopped.") } // itoa converts int to string (stdlib alias to avoid fmt import just for this). func itoa(n int) string { return strconv.Itoa(n) }