package main import ( "embed" "encoding/json" "html/template" "log" "net/http" "os" "strconv" "github.com/gorilla/mux" ) //go:embed *.js *.in robots.txt var content embed.FS func handleEntryOptions(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST, GET") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.WriteHeader(http.StatusNoContent) } func handleEntryPost(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST, GET") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") var entry Entry err := json.NewDecoder(r.Body).Decode(&entry) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } err = InsertEntry(&entry) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Println("New entry for", entry.Location) } type EntriesPage struct { Total uint `json:"total"` Offset uint `json:"offset"` Size uint `json:"size"` Entries []Entry `json:"entries"` } func handleEntryGet(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST, GET") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") from64, _ := strconv.ParseUint(r.FormValue("from"), 10, 32) from := uint(from64) count, err := CountEntries() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } entries, err := ListEntries(from, 1000) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if entries == nil { entries = []Entry{} } page := EntriesPage{Total: count, Offset: from, Size: uint(len(entries)), Entries: entries} err = json.NewEncoder(w).Encode(&page) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } func handleHome(pathname string) func(http.ResponseWriter, *http.Request) { t := template.Must(template.ParseFS(content, pathname)) return func(w http.ResponseWriter, r *http.Request) { count, err := CountEntries() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } t.Execute(w, count) } } func handleRequests() { router := mux.NewRouter() router.HandleFunc("/entries", handleEntryOptions).Methods("OPTIONS") router.HandleFunc("/entries", handleEntryPost).Methods("POST") router.HandleFunc("/entries", handleEntryGet).Methods("GET") router.HandleFunc("/", handleHome("index.html.in")).Methods("GET") router.PathPrefix("/").Handler(http.FileServer(http.FS(content))) log.Fatal(http.ListenAndServe(configure(), router)) } func configure() string { at := os.Getenv("STATSAT") if at == "" { log.Println("Defaulting to STATSAT=:8080") at = ":8080" } return at } func main() { log.Println("Starting up") InitEntries() defer CloseEntries() handleRequests() }