initial version; nixos module untested
This commit is contained in:
commit
10cf66ae53
9 changed files with 325 additions and 0 deletions
95
birdtown-visit-counter.go
Normal file
95
birdtown-visit-counter.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type visitsStore struct {
|
||||
m map[string]int
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *visitsStore) visit(ending string) int {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
oldVisits, has := s.m[ending]
|
||||
var newVisits int
|
||||
if has {
|
||||
newVisits = oldVisits + 1
|
||||
} else {
|
||||
newVisits = 1
|
||||
}
|
||||
s.m[ending] = newVisits
|
||||
return newVisits
|
||||
}
|
||||
|
||||
type visitsHandler struct {
|
||||
store *visitsStore
|
||||
}
|
||||
|
||||
func (h *visitsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// CORS: this is expected to be called from a different domain,
|
||||
// hoops require jumping
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Max-Age", "86400")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
|
||||
w.Header().Set("content-type", "application/json")
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
var s string
|
||||
if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("cannot parse data"))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(([]byte)(strconv.Itoa(h.store.visit(s))))
|
||||
return
|
||||
}
|
||||
if r.Method == http.MethodGet {
|
||||
jsonBytes, err := json.Marshal(h.store.m)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("cannot serialize"))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(jsonBytes)
|
||||
return
|
||||
}
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Allow", "POST, GET, OPTIONS")
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
func main() {
|
||||
addr := flag.String("addr", ":11568", "Listen address")
|
||||
initialFile := flag.String("file", "visits.json", "File with initial data in JSON format")
|
||||
flag.Parse()
|
||||
|
||||
initialVisits := make(map[string]int)
|
||||
initialVisitsJson, err := os.ReadFile(*initialFile)
|
||||
if err == nil {
|
||||
json.Unmarshal(initialVisitsJson, &initialVisits)
|
||||
} else {
|
||||
log.Println("initial file", *initialFile, "could not be read, defaulting to empty data")
|
||||
}
|
||||
|
||||
store := visitsStore{m: initialVisits, RWMutex: &sync.RWMutex{}}
|
||||
visitsH := &visitsHandler{store: &store}
|
||||
http.Handle("/visits", visitsH)
|
||||
|
||||
log.Println("Starting web server...")
|
||||
http.ListenAndServe(*addr, nil)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue