package main import ( "embed" "flag" "fmt" "log" "net/http" "text/template" ) //go:embed simple.css/simple.css templates/index.html var static embed.FS var templates *template.Template func main() { configPath := parseFlags() parseConfig(configPath) var err error templates, err = template.ParseFS(static, "templates/index.html") if err != nil { log.Fatal(err) } http.HandleFunc("/", index(config.Devices)) http.HandleFunc("/api", api) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(static)))) fmt.Printf("Serving at http://%s\n", config.Web.Listen) log.Fatal(http.ListenAndServe(config.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) { return func(w http.ResponseWriter, r *http.Request) { err := templates.Execute(w, devices) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } } } func api(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] }