package main import ( "encoding/json" "io/ioutil" "log" "net/http" "github.com/spf13/viper" "gopkg.in/yaml.v2" ) type webhook struct { Name string Path string SecretKey string `yaml:"secret_key"` NumSwitches int `yaml:"num_switches"` MomentarySwitches []int `yaml:"momentary_switches"` SwitchStates []int `json:switch_states` } func debug(msg string, args ...interface{}) { if viper.GetBool("Debug") { log.Printf(msg, args...) } } func initWebhooks(filename string) { bytes, err := ioutil.ReadFile(filename) if err != nil { log.Panicf("Couldn't read config file: %s", err) } var hooks []webhook err = yaml.Unmarshal(bytes, &hooks) if err != nil { log.Panicf("Couldn't parse webhook file: %v", err) } debug("Webhook definitions:") debug("%v", hooks) for _, v := range hooks { v.SwitchStates = make([]int, v.NumSwitches) http.HandleFunc("/"+v.Path, makeWebhookHandler(v)) } } func runServer() { http.ListenAndServe(":"+viper.GetString("ListenPort"), nil) } func makeWebhookHandler(hook webhook) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // authentication check keys, ok := r.URL.Query()["key"] if !ok || len(keys) == 0 { log.Printf("No key found for webhook: %s", hook.Name) http.NotFound(w, r) return } providedKey := keys[0] if providedKey == "" || providedKey != hook.SecretKey { log.Printf("Failed to authenticate request for webhook: %s", hook.Name) debug("Got key '%s', expected key '%s'", providedKey, hook.SecretKey) http.NotFound(w, r) return } // now actually handle the request if r.Method == "GET" { webhookRead(hook, w, r) } else if r.Method == "POST" { webhookWrite(hook, w, r) } } } func webhookRead(hook webhook, w http.ResponseWriter, r *http.Request) { err := json.NewEncoder(w).Encode(hook.SwitchStates) if err != nil { log.Printf("Failed to send response: %v", err) return } // momentary switches have been read, so make sure they're 0 // next time for _, i := range hook.MomentarySwitches { hook.SwitchStates[i] = 0 } } func webhookWrite(hook webhook, w http.ResponseWriter, r *http.Request) { err := json.NewDecoder(r.Body).Decode(&hook.SwitchStates) if err != nil { log.Printf("Failed to parse json for '%s': %v", hook.Name, err) http.Error(w, "Failed to parse json", http.StatusBadRequest) return } } func main() { // read environment config viper.BindEnv("Debug", "DEBUG") viper.BindEnv("WebhookFile", "WEBHOOK_CONFIG_FILE") viper.BindEnv("ListenPort", "PORT") viper.SetDefault("ListenPort", "7200") viper.SetDefault("WebhookFile", "./webhooks.yaml") viper.SetDefault("Debug", false) // read webhook config initWebhooks(viper.GetString("WebhookFile")) // serve app runServer() }