package config import ( "encoding/json" "fmt" "goseg/structs" "io/ioutil" "log/slog" "net" "net/http" "os" "path/filepath" "sync" "time" ) var ( globalConfig structs.SysConfig logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) BasePath string Version = "v2.0.0" Ready = false VersionServerReady = false VersionInfo structs.Version Ram int Cpu int CoreTemp int Disk int WifiEnabled = false ActiveNetwork string WifiNetworks []string HttpOpen = false UploadSecret string checkInterval = 5 * time.Minute confMutex sync.Mutex versMutex sync.Mutex ) // try initializing from system.json on disk func init() { // try loading existing config BasePath, err := os.Getwd() if err != nil { errmsg := fmt.Sprintf("Couldn't get cwd: %v", err) logger.Error(errmsg) } confPath := filepath.Join(BasePath, "settings", "system.json") file, err := os.Open(confPath) if err != nil { // create a default if it doesn't exist err = createDefaultConf() if err != nil { errmsg := fmt.Sprintf("Unable to create config! %v", err) logger.Error(errmsg) } } defer file.Close() decoder := json.NewDecoder(file) err = decoder.Decode(&globalConfig) if err != nil { errmsg := fmt.Sprintf("Error decoding JSON: %v", err) logger.Error(errmsg) } } // return the global conf var func Conf() structs.SysConfig { confMutex.Lock() defer confMutex.Unlock() return globalConfig } // update by passing in a map of key:values you want to modify func UpdateConf(values map[string]interface{}) error { // mutex lock to avoid race conditions confMutex.Lock() defer confMutex.Unlock() confPath := filepath.Join(BasePath, "settings", "system.json") file, err := ioutil.ReadFile(confPath) if err != nil { errmsg := fmt.Sprintf("Unable to load config: %v", err) logger.Error(errmsg) return err } // unmarshal the config to struct var configMap map[string]interface{} if err := json.Unmarshal(file, &configMap); err != nil { errmsg := fmt.Sprintf("Error decoding JSON: %v", err) logger.Error(errmsg) return err } // update our unmarshaled struct for key, value := range values { configMap[key] = value } // marshal and persist it updatedJSON, err := json.MarshalIndent(configMap, "", " ") if err != nil { errmsg := fmt.Sprintf("Error encoding JSON: %v", err) logger.Error(errmsg) return err } // update the globalConfig var if err := json.Unmarshal(updatedJSON, &globalConfig); err != nil { errmsg := fmt.Sprintf("Error updating global config: %v", err) logger.Error(errmsg) return err } if err := ioutil.WriteFile(confPath, updatedJSON, 0644); err != nil { errmsg := fmt.Sprintf("Error writing to file: %v", err) logger.Error(errmsg) return err } return nil } // write a default conf to disk func createDefaultConf() error { defaultConfig := structs.SysConfig{ Setup: "start", EndpointUrl: "api.startram.io", ApiVersion: "v1", Piers: []string{}, NetCheck: "1.1.1.1:53", UpdateMode: "auto", UpdateUrl: "https://version.groundseg.app", UpdateBranch: "latest", SwapVal: 16, SwapFile: filepath.Join(BasePath, "settings", "swapfile"), KeyFile: filepath.Join(BasePath, "settings", "session.key"), Sessions: struct { Authorized map[string]structs.SessionInfo `json:"authorized"` Unauthorized map[string]structs.SessionInfo `json:"unauthorized"` }{ Authorized: make(map[string]structs.SessionInfo), Unauthorized: make(map[string]structs.SessionInfo), }, LinuxUpdates: struct { Value int `json:"value"` Interval string `json:"interval"` Previous bool `json:"previous"` }{ Value: 1, Interval: "week", Previous: false, }, DockerData: "/var/lib/docker", WgOn: false, WgRegistered: false, PwHash: "", C2cInterval: 0, FirstBoot: false, WgRegisterd: false, GsVersion: Version, CfgDir: "", UpdateInterval: 0, BinHash: "", Pubkey: "", Privkey: "", Salt: "", } path := filepath.Join(BasePath, "settings", "system.json") if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { return err } file, err := os.Create(path) if err != nil { return err } defer file.Close() encoder := json.NewEncoder(file) encoder.SetIndent("", " ") if err := encoder.Encode(&defaultConfig); err != nil { return err } return nil } // check outbound tcp connectivity // takes ip:port func NetCheck(netCheck string) bool { logger.Info("Checking internet access") internet := false timeout := 3 * time.Second conn, err := net.DialTimeout("tcp", netCheck, timeout) if err != nil { errmsg := fmt.Sprintf("Check internet access error: %v", err) logger.Error(errmsg) } else { internet = true _ = conn.Close() } return internet } // check the version server and return unmarshaled result func CheckVersion() (structs.Version, bool) { versMutex.Lock() defer versMutex.Unlock() const retries = 10 const delay = time.Second url := globalConfig.UpdateUrl for i := 0; i < retries; i++ { resp, err := http.Get(url) if err != nil { errmsg := fmt.Sprintf("Unable to connect to update server: %v", err) logger.Warn(errmsg) if i < retries-1 { time.Sleep(delay) continue } else { return VersionInfo, false } } // read the body bytes body, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { errmsg := fmt.Sprintf("Error reading version info: %v", err) logger.Warn(errmsg) if i < retries-1 { time.Sleep(delay) continue } else { return VersionInfo, false } } // unmarshal values into Version struct err = json.Unmarshal(body, &VersionInfo) if err != nil { errmsg := fmt.Sprintf("Error unmarshalling JSON: %v", err) logger.Warn(errmsg) if i < retries-1 { time.Sleep(delay) continue } else { return VersionInfo, false } } // debug: re-marshal and write to disk confPath := filepath.Join(BasePath, "settings", "version_info.json") file, err := os.Create(confPath) if err != nil { errmsg := fmt.Sprintf("Failed to create file: %v", err) logger.Error(errmsg) return VersionInfo, false } defer file.Close() encoder := json.NewEncoder(file) encoder.SetIndent("", " ") if err := encoder.Encode(&VersionInfo); err != nil { errmsg := fmt.Sprintf("Failed to write JSON: %v", err) logger.Error(errmsg) } return VersionInfo, true } return VersionInfo, false } func CheckVersionLoop() { ticker := time.NewTicker(checkInterval) for { select { case <-ticker.C: latestVersion, _ := CheckVersion() currentVersion := VersionInfo if latestVersion != currentVersion { fmt.Printf("New version available! Current: %s, Latest: %s\n", currentVersion, latestVersion) // Handle the update logic here } } } }