diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bc18dc4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +trim_trailing_whitespace = true +insert_final_newline = true + +[*.json] +indent_style = tab +tab_width = 4 diff --git a/README.md b/README.md index 4cace42..da23ea6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ $ curl http://localhost:8080 | jq { "api_compatibility": [ - "14" + "14", + "15" ], "space": "CCCHH", ... @@ -15,16 +16,16 @@ $ curl http://localhost:8080 | jq ## Configuring -spaceapid has to be configured via one or multiple json files. +`spaceapid` has to be configured via one or multiple json files. A sample configuration is provided as [config-template.json](./config-template.json). The config consists of three parts: - `"credentials"` - List of Username/Password credentials for HTTP BasicAuth - `"dynamic"` - - The configuration for the dynamic parts of the message -- `"response"` - - The static (pre-filled) parts of the response + - The dynamic parts of the message +- `"static"` + - The static (pre-filled) parts of the message See [Running](#Running) for details. diff --git a/config-template.json b/config-template.json index 8b2b039..aac6046 100644 --- a/config-template.json +++ b/config-template.json @@ -66,7 +66,7 @@ } } }, - "response": { + "static": { "api_compatibility": [ "14", "15" diff --git a/config/config.go b/config/config.go index b57d615..7ad632c 100644 --- a/config/config.go +++ b/config/config.go @@ -69,7 +69,7 @@ func ParseConfiguration() (conf SpaceapidConfig) { log.Println("Parsing configuration files") paths := getConfigPaths() - log.Println(paths) + log.Println("Config files:", paths) for _, path := range paths { // Read file file, err := os.ReadFile(path) @@ -86,9 +86,9 @@ func ParseConfiguration() (conf SpaceapidConfig) { } } - // Check if compatible with v14 - if !slices.Contains(conf.Response.APICompatibility, "14") { - log.Fatalln("Provided file doesn't specify compatibility with API version 14") + // Sanity check: Config should declare compatibility with current SpaceAPI message version + if !slices.Contains(conf.Response.APICompatibility, types.CurrentMessageVersion) { + log.Fatalln("Provided config doesn't specify compatibility with current API version", types.CurrentMessageVersion) } // Initialize fields for environment sensors diff --git a/config/types.go b/config/types.go index 9361579..315307f 100644 --- a/config/types.go +++ b/config/types.go @@ -34,5 +34,5 @@ type SpaceapidConfig struct { // The dynamic part of the spaceapi JSON Dynamic DynamicStateConfig `json:"dynamic"` // The static part of the spaceapi JSON - Response types.SpaceAPIResponse `json:"response"` + Response types.SpaceAPIResponse `json:"static"` } diff --git a/handlers/sensors.go b/handlers/sensors.go index 187620e..e3aa51e 100644 --- a/handlers/sensors.go +++ b/handlers/sensors.go @@ -11,7 +11,7 @@ import ( "git.hamburg.ccc.de/ccchh/spaceapid/types" ) -func EnvironmentSensor( +func EnvironmentSensorPUT( authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID, resp *types.EnvironmentSensor, ) func(http.ResponseWriter, *http.Request) { @@ -35,3 +35,20 @@ func EnvironmentSensor( resp.LastChange = time.Now().Unix() } } + +func EnvironmentSensorDELETE( + authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID, + resp *types.EnvironmentSensor, +) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + _, err := updateEndpointValidator(authDB, validCredentials, w, r) + if err != nil { + log.Println(err) + return + } + + // Set SpaceAPI response values + resp.Value = 0 + resp.LastChange = time.Now().Unix() + } +} diff --git a/handlers/util.go b/handlers/util.go index d7e6dd5..82af6fa 100644 --- a/handlers/util.go +++ b/handlers/util.go @@ -1,7 +1,6 @@ package handlers import ( - "errors" "fmt" "io" "net/http" @@ -20,14 +19,14 @@ func updateEndpointValidator( if !ok || !util.CheckCredentials(authDB, validCredentials, username, password) { w.Header().Set("WWW-Authenticate", "Basic realm=\"spaceapid\"") http.Error(w, "", http.StatusUnauthorized) - return []byte{}, errors.New(fmt.Sprintf("Unauthorized request from %s Username: %s Password: %s", r.RemoteAddr, username, password)) + return []byte{}, fmt.Errorf("Unauthorized request from %s Username: %s Password: %s", r.RemoteAddr, username, password) } // Read request body body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "", http.StatusInternalServerError) - return []byte{}, errors.New(fmt.Sprintf("Failed to read request body from %s with error: %s", r.RemoteAddr, err)) + return []byte{}, fmt.Errorf("Failed to read request body from %s with error: %s", r.RemoteAddr, err) } return body, nil diff --git a/main.go b/main.go index b67c43f..4b01eee 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,9 @@ func init() { } func main() { + if version == "" { + version = "unknown" + } log.Println("SpaceAPId version", version) flag.Parse() @@ -68,12 +71,18 @@ func main() { // Register handlers for environmental sensors for sensorType, envSensorConfigs := range conf.Dynamic.Sensors { for i, envSensorConfig := range envSensorConfigs { - urlPattern := "PUT " + util.GetSensorURLPath( + urlPattern := util.GetSensorURLPath( sensorType, envSensorConfig.SensorData.Location, envSensorConfig.SensorData.Name, ) http.HandleFunc( - urlPattern, - handlers.EnvironmentSensor( + "PUT "+urlPattern, + handlers.EnvironmentSensorPUT( + conf.Credentials, envSensorConfig.AllowedCredentials, &conf.Response.Sensors[sensorType][i], + ), + ) + http.HandleFunc( + "DELETE "+urlPattern, + handlers.EnvironmentSensorDELETE( conf.Credentials, envSensorConfig.AllowedCredentials, &conf.Response.Sensors[sensorType][i], ), ) diff --git a/types/v15.go b/types/v15.go index 0362fab..5180ba6 100644 --- a/types/v15.go +++ b/types/v15.go @@ -1,5 +1,10 @@ package types +const CurrentMessageVersion = "15" + +// SpaceAPIResponse represents the contents of the SpaceAPI message +// +// Currently implemented version: 15 type SpaceAPIResponse struct { APICompatibility []string `json:"api_compatibility"` // REQUIRED Space string `json:"space"` // REQUIRED @@ -120,10 +125,10 @@ type SpaceAPIState struct { } `json:"icon,omitzero"` } -func (s *SpaceAPIState) FromPersistentSpaceState(state PersistentSpaceState) { - s.Open = state.State.Open - s.LastChange = state.State.LastChange - s.Message = state.State.Message +func (s *SpaceAPIState) FromPersistentSpaceState(ps PersistentSpaceState) { + s.Open = ps.State.Open + s.LastChange = ps.State.LastChange + s.Message = ps.State.Message } type SpaceAPIEvents struct {