package main import ( "encoding/json" "flag" "fmt" "net/http" "os" "os/signal" "super-frpc/config" "super-frpc/database" "super-frpc/frpLogger" "super-frpc/global" "super-frpc/postLog" "super-frpc/session" "super-frpc/sys" "super-frpc/utils" "super-frpc/watchdog" "syscall" "time" ) func main() { global.CurrentPath = utils.GetCurrentPath() postLog.Info(fmt.Sprintf("%s %s (Build %d.%s) by %s", global.Software.Name, global.Software.Version, global.Software.BuildVer, global.Software.BuildType, global.Software.Developer)) global.ConfigPath = flag.String("config", global.CurrentPath+string(os.PathSeparator)+"config.json", "path to config file") global.DBPath_data = flag.String("db", global.CurrentPath+string(os.PathSeparator)+"database.db", "path to database file") global.DBPath_log = flag.String("log", global.CurrentPath+string(os.PathSeparator)+"logs.db", "path to logs database file") flag.Parse() if _, err := os.Stat(*global.ConfigPath); os.IsNotExist(err) { // Load the default config configData, err := json.MarshalIndent(global.CurrentConfig, "", " ") if err != nil { postLog.Warning(fmt.Sprintf("Failed to marshal default config: %v", err)) } else if err := os.WriteFile(*global.ConfigPath, configData, 0644); err != nil { postLog.Warning(fmt.Sprintf("Failed to create default config file: %v", err)) } postLog.Info(fmt.Sprintf("Created default config file at %s", *global.ConfigPath)) } err := config.LoadConfig(*global.ConfigPath, sys.GetInitSystem) if err != nil { postLog.Fatal(fmt.Sprintf("Failed to load config: %v", err)) } if global.Software.BuildType == "debug" { // If debug build, set debug mode to true global.CurrentConfig.Debug = true } postLog.SetDebugMode(global.CurrentConfig.Debug) global.Is.Debug = global.CurrentConfig.Debug if err := postLog.InitLogsDatabase(*global.DBPath_log); err != nil { postLog.Fatal(fmt.Sprintf("Failed to initialize logs database: %v", err)) } postLog.Info("Logs database initialized successfully") if err := database.InitDatabase(*global.DBPath_data, *global.DBPath_log); err != nil { postLog.Fatal(fmt.Sprintf("Failed to initialize database: %v", err)) } postLog.Info("Database initialized successfully") if err := database.InitFrpcDatabase(*global.DBPath_data); err != nil { postLog.Fatal(fmt.Sprintf("Failed to initialize frpc database: %v", err)) } if global.CurrentConfig.LogsPath != "" && !global.CurrentConfig.Status.IsLogrotateConfigured { // Set up logrotate policy if LogsPath is configured if err := sys.CreateLogrotatePolicy(); err != nil { postLog.Fatal(fmt.Sprintf("Failed to create logrotate policy: %v", err)) } else { global.CurrentConfig.Status.IsLogrotateConfigured = true config.SaveConfig() postLog.Info("Logrotate policy has been configured successfully.") } } frpLogger.SetDatabase(global.ConfigDB, global.FrpcDB) frpLogger.SetDebugMode(global.CurrentConfig.Debug) postLog.InitLogBroadcaster() setupRoutes() // Initialize watchdog connection if global.CurrentConfig.Watchdog.Enabled { err = watchdog.Init() if err != nil { postLog.Error(fmt.Sprintf("Unable to initialize Watchdog: %s", err)) } else { if !watchdog.Connect() { postLog.Error("Failed to connect to Watchdog") } else { postLog.Info("Connected to Watchdog successfully") go func() { if err := watchdog.StartKeepAlive(); err != nil { postLog.Error(fmt.Sprintf("Watchdog keepalive stopped: %v", err)) global.Is.WatchdogConnected = false } }() } } } addr := fmt.Sprintf("%s:%s", global.CurrentConfig.ListenAddr, global.CurrentConfig.ListenPort) server := &http.Server{ Addr: addr, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } go func() { postLog.Info(fmt.Sprintf("Server starting on %s", addr)) global.Is.Online = true if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { postLog.Fatal(fmt.Sprintf("Failed to start server: %v", err)) } }() go func() { ticker := time.NewTicker(1 * time.Hour) defer ticker.Stop() for range ticker.C { session.CleanupExpiredTokens() session.CleanupExpiredSessions() } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit postLog.Info("Shutting down server...") if err := server.Close(); err != nil { postLog.Error(fmt.Sprintf("Server closed with error: %v", err)) } if err := database.CloseDatabase(); err != nil { postLog.Error(fmt.Sprintf("Error closing database: %v", err)) } watchdog.Close() postLog.Info("Server stopped") } func GetStatusHandler(w http.ResponseWriter, r *http.Request) { statusInfo := map[string]string{ "ServerStatus": "Offline", "WatchdogStatus": "Offline", } if global.Is.Online { statusInfo["ServerStatus"] = "Online" } if global.Is.WatchdogConnected { statusInfo["WatchdogStatus"] = "Online" } utils.SendSuccessResponse(w, "getStatus", statusInfo) } func GetSoftwareInfoHandler(w http.ResponseWriter, r *http.Request) { utils.SendSuccessResponse(w, "getSoftwareInfo", global.Software) }