Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 98d22b9f72 | |||
| 11046cb501 |
7 changed files with 118 additions and 41 deletions
|
|
@ -2,7 +2,7 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json/v2"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
@ -78,9 +78,7 @@ func ParseConfiguration() (conf SpaceapidConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON
|
// Parse JSON
|
||||||
dec := json.NewDecoder(bytes.NewReader(file))
|
err = json.UnmarshalRead(bytes.NewReader(file), &conf, json.RejectUnknownMembers(true))
|
||||||
dec.DisallowUnknownFields()
|
|
||||||
err = dec.Decode(&conf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Could not parse spaceapid config file:", err)
|
log.Fatalln("Could not parse spaceapid config file:", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -1,3 +1,3 @@
|
||||||
module git.hamburg.ccc.de/ccchh/spaceapid
|
module git.hamburg.ccc.de/ccchh/spaceapid
|
||||||
|
|
||||||
go 1.22
|
go 1.27
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json/v2"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ func Root(resp *types.SpaceAPIResponse) func(http.ResponseWriter, *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize response
|
// Serialize response
|
||||||
response, err := json.Marshal(resp)
|
response, err := json.Marshal(resp, json.Deterministic(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to serialize JSON response:", err)
|
log.Println("Failed to serialize JSON response:", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StateOpen(
|
func StateOpenPUT(
|
||||||
authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID,
|
authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID,
|
||||||
resp *types.SpaceAPIState,
|
resp *types.SpaceAPIState,
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
|
|
@ -22,7 +22,7 @@ func StateOpen(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse request body
|
// Parse request body
|
||||||
newState, err := strconv.ParseBool(string(body))
|
doorNowOpen, err := strconv.ParseBool(string(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to parse request body from", r.RemoteAddr, "with error:", err)
|
log.Println("Failed to parse request body from", r.RemoteAddr, "with error:", err)
|
||||||
http.Error(w, "HTTP request body should either be true or false", http.StatusBadRequest)
|
http.Error(w, "HTTP request body should either be true or false", http.StatusBadRequest)
|
||||||
|
|
@ -30,7 +30,28 @@ func StateOpen(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set SpaceAPI response values
|
// Set SpaceAPI response values
|
||||||
resp.Open = newState
|
if doorNowOpen {
|
||||||
|
resp.Open = types.SpaceAPIStateOpenIsOpen
|
||||||
|
} else {
|
||||||
|
resp.Open = types.SpaceAPIStateOpenIsClosed
|
||||||
|
}
|
||||||
|
resp.LastChange = time.Now().Unix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StateOpenDELETE(
|
||||||
|
authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID,
|
||||||
|
resp *types.SpaceAPIState,
|
||||||
|
) 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.Open = types.SpaceAPIStateOpenIsUnknown
|
||||||
resp.LastChange = time.Now().Unix()
|
resp.LastChange = time.Now().Unix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
main.go
12
main.go
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -17,10 +18,14 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagHelp bool
|
flagHelp bool
|
||||||
|
listenAddress string
|
||||||
|
listenPort string
|
||||||
version string
|
version string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
flag.StringVar(&listenAddress, "i", "", "IP address the HTTP server should bind to (all available addresses by default)")
|
||||||
|
flag.StringVar(&listenPort, "p", "8080", "Port the HTTP server should listen on")
|
||||||
flag.BoolVar(&flagHelp, "h", false, "Print help output")
|
flag.BoolVar(&flagHelp, "h", false, "Print help output")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +64,10 @@ func main() {
|
||||||
)
|
)
|
||||||
// state->open
|
// state->open
|
||||||
http.HandleFunc("PUT /state/open",
|
http.HandleFunc("PUT /state/open",
|
||||||
handlers.StateOpen(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State),
|
handlers.StateOpenPUT(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State),
|
||||||
|
)
|
||||||
|
http.HandleFunc("DELETE /state/open",
|
||||||
|
handlers.StateOpenDELETE(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State),
|
||||||
)
|
)
|
||||||
// state->message
|
// state->message
|
||||||
http.HandleFunc("PUT /state/message",
|
http.HandleFunc("PUT /state/message",
|
||||||
|
|
@ -91,5 +99,5 @@ func main() {
|
||||||
|
|
||||||
// Start webserver
|
// Start webserver
|
||||||
log.Println("Starting HTTP server...")
|
log.Println("Starting HTTP server...")
|
||||||
log.Fatalln(http.ListenAndServe(":8080", nil))
|
log.Fatalln(http.ListenAndServe(fmt.Sprintf("%s:%s", listenAddress, listenPort), nil))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package persistence
|
package persistence
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json/jsontext"
|
||||||
|
"encoding/json/v2"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -20,7 +21,7 @@ func MergeOldState(response *types.SpaceAPIResponse) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
oldState []byte
|
oldState []byte
|
||||||
persistedState types.PersistentSpaceState
|
persistedState types.PersistentState
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Println("Merging old state from", persistentStatePath, "...")
|
log.Println("Merging old state from", persistentStatePath, "...")
|
||||||
|
|
@ -87,27 +88,38 @@ func SaveCurrentState(response types.SpaceAPIResponse) {
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
// Create persistent state
|
// Create persistent state
|
||||||
persistentStateV14 := types.PersistentSpaceState{
|
var (
|
||||||
State: struct {
|
isOpen bool
|
||||||
Open bool `json:"open"`
|
isUnknown bool
|
||||||
LastChange int64 `json:"lastchange"`
|
)
|
||||||
Message string `json:"message,omitempty"`
|
switch response.State.Open {
|
||||||
}{Open: response.State.Open, LastChange: response.State.LastChange, Message: response.State.Message},
|
case types.SpaceAPIStateOpenIsUnknown:
|
||||||
|
isUnknown = true
|
||||||
|
case types.SpaceAPIStateOpenIsClosed:
|
||||||
|
break
|
||||||
|
case types.SpaceAPIStateOpenIsOpen:
|
||||||
|
isOpen = true
|
||||||
|
}
|
||||||
|
persistentState := types.PersistentState{
|
||||||
|
State: types.PersistentSpaceState{
|
||||||
|
Open: isOpen, IsUnknown: isUnknown,
|
||||||
|
LastChange: response.State.LastChange, Message: response.State.Message,
|
||||||
|
},
|
||||||
|
Sensors: make(map[string][]types.PersistentEnvironmentSensor),
|
||||||
}
|
}
|
||||||
// Save sensor state
|
// Save sensor state
|
||||||
persistentStateV14.Sensors = make(map[string][]types.PersistentEnvironmentSensor)
|
|
||||||
for key, environmentSensors := range response.Sensors {
|
for key, environmentSensors := range response.Sensors {
|
||||||
persistentStateV14.Sensors[key] = make([]types.PersistentEnvironmentSensor, len(environmentSensors))
|
persistentState.Sensors[key] = make([]types.PersistentEnvironmentSensor, len(environmentSensors))
|
||||||
for i, sensor := range environmentSensors {
|
for i, sensor := range environmentSensors {
|
||||||
persistentStateV14.Sensors[key][i].Value = sensor.Value
|
persistentState.Sensors[key][i].Value = sensor.Value
|
||||||
persistentStateV14.Sensors[key][i].Location = sensor.Location
|
persistentState.Sensors[key][i].Location = sensor.Location
|
||||||
persistentStateV14.Sensors[key][i].Name = sensor.Name
|
persistentState.Sensors[key][i].Name = sensor.Name
|
||||||
persistentStateV14.Sensors[key][i].LastChange = sensor.LastChange
|
persistentState.Sensors[key][i].LastChange = sensor.LastChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize persistent state
|
// Serialize persistent state
|
||||||
marshal, err := json.MarshalIndent(persistentStateV14, "", "\t")
|
marshal, err := json.Marshal(persistentState, json.JoinOptions(jsontext.Multiline(true), jsontext.SpaceAfterComma(true)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Failed serializing persistent state... error:", err)
|
log.Fatalln("Failed serializing persistent state... error:", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
types/v15.go
50
types/v15.go
|
|
@ -1,5 +1,9 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json/v2"
|
||||||
|
)
|
||||||
|
|
||||||
const CurrentMessageVersion = "15"
|
const CurrentMessageVersion = "15"
|
||||||
|
|
||||||
// SpaceAPIResponse represents the contents of the SpaceAPI message
|
// SpaceAPIResponse represents the contents of the SpaceAPI message
|
||||||
|
|
@ -114,8 +118,31 @@ type SpaceAPILocationAreas struct {
|
||||||
SquareMeters float64 `json:"square_meters"` // REQUIRED
|
SquareMeters float64 `json:"square_meters"` // REQUIRED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SpaceAPIStateOpen int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SpaceAPIStateOpenIsUnknown = iota
|
||||||
|
SpaceAPIStateOpenIsClosed
|
||||||
|
SpaceAPIStateOpenIsOpen
|
||||||
|
)
|
||||||
|
|
||||||
|
func (state SpaceAPIStateOpen) MarshalJSON() ([]byte, error) {
|
||||||
|
switch state {
|
||||||
|
case SpaceAPIStateOpenIsClosed:
|
||||||
|
return json.Marshal(false)
|
||||||
|
case SpaceAPIStateOpenIsOpen:
|
||||||
|
return json.Marshal(true)
|
||||||
|
default:
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state SpaceAPIStateOpen) IsZero() bool {
|
||||||
|
return state == SpaceAPIStateOpenIsUnknown
|
||||||
|
}
|
||||||
|
|
||||||
type SpaceAPIState struct {
|
type SpaceAPIState struct {
|
||||||
Open bool `json:"open,omitempty"`
|
Open SpaceAPIStateOpen `json:"open,omitzero"`
|
||||||
LastChange int64 `json:"lastchange,omitempty"`
|
LastChange int64 `json:"lastchange,omitempty"`
|
||||||
TriggerPerson string `json:"trigger_person,omitempty"`
|
TriggerPerson string `json:"trigger_person,omitempty"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
|
|
@ -125,8 +152,16 @@ type SpaceAPIState struct {
|
||||||
} `json:"icon,omitzero"`
|
} `json:"icon,omitzero"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SpaceAPIState) FromPersistentSpaceState(ps PersistentSpaceState) {
|
func (s *SpaceAPIState) FromPersistentSpaceState(ps PersistentState) {
|
||||||
s.Open = ps.State.Open
|
if ps.State.IsUnknown {
|
||||||
|
s.Open = SpaceAPIStateOpenIsUnknown
|
||||||
|
} else {
|
||||||
|
if ps.State.Open {
|
||||||
|
s.Open = SpaceAPIStateOpenIsOpen
|
||||||
|
} else {
|
||||||
|
s.Open = SpaceAPIStateOpenIsClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
s.LastChange = ps.State.LastChange
|
s.LastChange = ps.State.LastChange
|
||||||
s.Message = ps.State.Message
|
s.Message = ps.State.Message
|
||||||
}
|
}
|
||||||
|
|
@ -147,13 +182,16 @@ type EnvironmentSensor struct {
|
||||||
LastChange int64 `json:"lastchange,omitempty"`
|
LastChange int64 `json:"lastchange,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PersistentState struct {
|
||||||
|
State PersistentSpaceState `json:"state"`
|
||||||
|
Sensors map[string][]PersistentEnvironmentSensor `json:"sensors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type PersistentSpaceState struct {
|
type PersistentSpaceState struct {
|
||||||
State struct {
|
|
||||||
Open bool `json:"open"`
|
Open bool `json:"open"`
|
||||||
|
IsUnknown bool `json:"is_unknown"`
|
||||||
LastChange int64 `json:"lastchange"`
|
LastChange int64 `json:"lastchange"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
} `json:"state"`
|
|
||||||
Sensors map[string][]PersistentEnvironmentSensor `json:"sensors,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersistentEnvironmentSensor struct {
|
type PersistentEnvironmentSensor struct {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue