package main import ( "context" "log" "net/http" "os" "os/signal" "path/filepath" "syscall" "time" "arma3-web-server/internal/api" "arma3-web-server/internal/services" "github.com/gin-gonic/gin" ) func main() { dataDir := mustAbs(getEnv("DATA_DIR", filepath.Join("data"))) serverfileDir := mustAbs(getEnv("SERVERFILE_DIR", "serverfiles")) modsDir := mustAbs(getEnv("MODS_DIR", "serverfiles/mods")) cfgDir := mustAbs(getEnv("CFG_DIR", "serverfiles/cfg")) profilesDir := mustAbs(getEnv("PROFILES_DIR", "serverfiles/profiles")) listen := getEnv("LISTEN", ":8080") frontendDir := mustAbs(getEnv("FRONTEND_DIR", filepath.Join("..", "frontend", "dist"))) log.Printf("data dir: %s", dataDir) log.Printf("serverfile dir: %s", serverfileDir) log.Printf("mods dir: %s", modsDir) log.Printf("cfg dir: %s", cfgDir) log.Printf("profiles dir: %s", profilesDir) log.Printf("listening on %s", listen) dirs := []string{ dataDir, filepath.Join(dataDir, "modlists"), serverfileDir, modsDir, cfgDir, profilesDir, filepath.Join(serverfileDir, "steamapps", "workshop", "content", "107410"), } for _, dir := range dirs { if err := os.MkdirAll(dir, 0755); err != nil { log.Fatalf("mkdir %s: %v", dir, err) } } settings := services.NewSettingsManager(dataDir) streamer := services.NewLogStreamer() configMgr := services.NewConfigManager(cfgDir) modlistMgr := services.NewModlistManager(dataDir) process := services.NewProcessManager(serverfileDir, modsDir, cfgDir, profilesDir, settings, modlistMgr, configMgr, streamer) steamcmd := services.NewSteamCmdManager(serverfileDir, streamer) r := gin.Default() if fi, err := os.Stat(frontendDir); err == nil && fi.IsDir() { r.Static("/assets", filepath.Join(frontendDir, "assets")) r.StaticFile("/favicon.ico", filepath.Join(frontendDir, "favicon.ico")) r.NoRoute(func(c *gin.Context) { c.File(filepath.Join(frontendDir, "index.html")) }) log.Printf("serving frontend from %s", frontendDir) } handler := api.New(settings, configMgr, modlistMgr, process, steamcmd, streamer, serverfileDir, modsDir, cfgDir, profilesDir) handler.SetupRoutes(r) log.Printf("starting server on %s", listen) srv := &http.Server{Addr: listen, Handler: r} go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server: %v", err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Print("shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("server forced shutdown: %v", err) } log.Print("server exited") } func mustAbs(p string) string { a, err := filepath.Abs(p) if err != nil { log.Fatalf("abs %s: %v", p, err) } return a } func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback }