package util import ( "bytes" "encoding/json" "errors" "log" "os" "path" "slices" "gitlab.hamburg.ccc.de/ccchh/spaceapid/types" ) const savedStateJSONPath = "/var/lib/spaceapid/spaceapid-state.json" // ParseTemplate parses the given file and func ParseTemplate(file string) (resp types.SpaceAPIResponseV14) { // Read template file template, err := os.ReadFile(file) if err != nil { log.Fatalln("Failed reading file:", err) } // Parse JSON dec := json.NewDecoder(bytes.NewReader(template)) dec.DisallowUnknownFields() err = dec.Decode(&resp) if err != nil { log.Fatalln("Could not parse SpaceAPI response template:", err) } // Check if compatible with v14 if !slices.Contains(resp.APICompatibility, "14") { log.Fatalln("Provided template doesn't specify compatibility with API version 14") } return } // MergeOldState merges a given SpaceAPIResponse with the state saved at the time of last program exit and then deletes // the file containing the old state. func MergeOldState(response *types.SpaceAPIResponseV14) { var ( err error oldState []byte ) // Check if state.json is present _, err = os.Stat(savedStateJSONPath) if err != nil { log.Println("Old state json not accessible at", savedStateJSONPath, ", skipping merge... error:", err) if errors.Is(err, os.ErrNotExist) { return } goto removeOld } // Read file and merge oldState, err = os.ReadFile(savedStateJSONPath) if err != nil { log.Println("Error reading old state from", savedStateJSONPath, ", skipping merge... error:", err) goto removeOld } err = json.Unmarshal(oldState, response) if err != nil { log.Println(savedStateJSONPath, "doesn't seem to contain valid data... error:", err) goto removeOld } // Delete old state json removeOld: err = os.RemoveAll(savedStateJSONPath) if err != nil { log.Println("Failed to remove", savedStateJSONPath, ", continuing... error:", err) } } func SaveCurrentState(response types.SpaceAPIResponseV14) { // Create state directory if not present err := os.MkdirAll(path.Dir(savedStateJSONPath), 0750) if err != nil { log.Fatalln("Failed creating", savedStateJSONPath, ", aborting... error:", err) } // Open persistent state file for reading file, err := os.OpenFile(savedStateJSONPath, os.O_RDWR|os.O_CREATE, 0644) if err != nil { log.Fatalln("Failed opening", savedStateJSONPath, "while trying to save current state... error:", err) } defer func(file *os.File) { _ = file.Close() }(file) // Create persistent state persistentStateV14 := types.PersistentStateV14{ State: struct { Open bool `json:"open"` LastChange int64 `json:"lastchange"` }{Open: response.State.Open, LastChange: response.State.LastChange}, } // Serialize persistent state marshal, err := json.MarshalIndent(persistentStateV14, "", "\t") if err != nil { log.Fatalln("Failed serializing persistent state... error:", err) } // Write to file _, err = file.Write(marshal) if err != nil { log.Fatalln("Failed writing persistent state to file", file.Name(), "... error:", err) } }