package main import ( "embed" "flag" "fmt" "log" "net/http" "text/template" ) //go:embed simple.css/simple.css templates/index.html var static embed.FS func main() { configPath := parseFlags() c := parseConfig(configPath) http.HandleFunc("/", index(c.Devices)) http.HandleFunc("/api", api()) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(static)))) fmt.Printf("Serving at http://%s\n", c.Web.Listen) log.Fatal(http.ListenAndServe(c.Web.Listen.String(), nil)) } // parseFlags() handles command line interface (CLI) flags. func parseFlags() string { var r string flag.StringVar(&r, "c", "/etc/webiot/config.json", "path to configuration file") flag.Parse() return r } // index() returns a HTTP handler for the index page. func index(devices DevicesConfig) func(http.ResponseWriter, *http.Request) { tmpl, err := template.ParseFS(static, "templates/index.html") if err != nil { log.Fatal(err) } return func(w http.ResponseWriter, r *http.Request) { err = tmpl.Execute(w, devices) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } } } // api() returns the HTTP handler for the API endpoint. func api() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // TODO assert correct HTTP method errHost, host := assertSingleParam(r, "host") errState, state := assertSingleParam(r, "state") if (errHost != nil) || (errState != nil) { http.Error(w, "Provide exactly one host and one state parameter!", 400) return } err := set(host, state) if err != nil { http.Error(w, "Could not set WiFi plug.", 500) } else { fmt.Fprint(w, "ok") } } } // assertSingleParam() returns the value of given URL key and panics if there // is not exactly one match for this key. func assertSingleParam(r *http.Request, key string) (error, string) { values := r.URL.Query()[key] if len(values) != 1 { return fmt.Errorf("Provide exactly one '%s' parameter!", key), "" } return nil, values[0] }