From 121ac0b2457d5f5617916aad38f5c05d94570260 Mon Sep 17 00:00:00 2001 From: Aki Date: Sun, 31 Dec 2023 06:18:38 +0100 Subject: Added GET /entries?[from=N] Endpoint name changed to plural --- entry.go | 21 +++++++++++++++++---- main.go | 45 +++++++++++++++++++++++++++++++++++++++++---- track.js | 2 +- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/entry.go b/entry.go index ed22141..2a55130 100644 --- a/entry.go +++ b/entry.go @@ -20,9 +20,9 @@ CREATE TABLE IF NOT EXISTS entries ( );` type Entry struct { - Id int `db:"id"` + Id int `json:"-" db:"id"` Location string `json:"location" db:"location"` - Referrer string `json:"referrer" db:"referrer"` + Referrer string `json:"referrer,omitempty" db:"referrer"` StartedAt time.Time `json:"startedAt" db:"started_at"` TimeSpent int `json:"timeSpent" db:"time_spent"` ScrolledTo float32 `json:"scrolledTo" db:"scrolled_to"` @@ -36,13 +36,11 @@ func InitEntries() { log.Println("Defaulting to STATSDB=./stats.db") file = "./stats.db" } - var err error db, err = sqlx.Connect("sqlite3", file) if err != nil { log.Fatalln(err) } - _, err = db.Exec(schema) if err != nil { log.Fatalln("Could not initialize schema") @@ -61,3 +59,18 @@ func InsertEntry(entry *Entry) error { _, err := db.NamedExec(query, entry) return err } + +func CountEntries() (count uint, err error) { + err = db.Get(&count, "SELECT COUNT(*) FROM entries;") + return +} + +func AllEntries() (entries []Entry, err error) { + err = db.Select(&entries, "SELECT * FROM entries;") + return +} + +func ListEntries(from, count uint) (entries []Entry, err error) { + err = db.Select(&entries, "SELECT * FROM entries LIMIT ? OFFSET ?;", count, from) + return +} diff --git a/main.go b/main.go index 036b47c..e28f72b 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "log" "net/http" "os" + "strconv" "github.com/gorilla/mux" ) @@ -16,14 +17,14 @@ 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", "POST, OPTIONS") + 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", "POST, OPTIONS") + 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) @@ -39,10 +40,46 @@ func handleEntryPost(w http.ResponseWriter, r *http.Request) { 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 handleRequests() { router := mux.NewRouter() - router.HandleFunc("/entry", handleEntryPost).Methods("POST") - router.HandleFunc("/entry", handleEntryOptions).Methods("OPTIONS") + router.HandleFunc("/entries", handleEntryOptions).Methods("OPTIONS") + router.HandleFunc("/entries", handleEntryPost).Methods("POST") + router.HandleFunc("/entries", handleEntryGet).Methods("GET") router.PathPrefix("/").Handler(http.FileServer(http.FS(content))) log.Fatal(http.ListenAndServe(configure(), router)) } diff --git a/track.js b/track.js index cc2df6f..0a281e6 100644 --- a/track.js +++ b/track.js @@ -12,7 +12,7 @@ scrolledTo = Math.max(scrolledTo, window.scrollY + window.innerHeight) }, false) window.addEventListener("beforeunload", ()=>{ - navigator.sendBeacon("https://stats.ignore.pl/entry", JSON.stringify({ + navigator.sendBeacon("http://stats.ignore.pl/entries", JSON.stringify({ "location": document.location.href, "referrer": document.referrer, "startedAt": startedAt, -- cgit v1.1