package api import ( "encoding/base64" "net/http" "time" "github.com/mssola/user_agent" "github.com/usefathom/fathom/pkg/counter" "github.com/usefathom/fathom/pkg/datastore" "github.com/usefathom/fathom/pkg/models" ) /* middleware */ func NewCollectHandler() http.Handler { go aggregate() return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { // abort if this is a bot. userAgent := r.UserAgent() ua := user_agent.New(userAgent) if ua.Bot() { return nil } q := r.URL.Query() now := time.Now() // get pageview details pageview := &models.Pageview{ SessionID: q.Get("sid"), Pathname: q.Get("p"), IsNewVisitor: q.Get("n") == "1", IsUnique: q.Get("u") == "1", IsBounce: q.Get("b") != "0", Referrer: q.Get("r"), Duration: 0, Timestamp: now, } // find previous pageview by same visitor previousPageview, err := datastore.GetMostRecentPageviewBySessionID(pageview.SessionID) if err != nil && err != datastore.ErrNoResults { return err } // if we have a recent pageview that is less than 30 minutes old if previousPageview != nil && previousPageview.Timestamp.After(now.Add(-30*time.Minute)) { previousPageview.Duration = (now.Unix() - previousPageview.Timestamp.Unix()) previousPageview.IsBounce = false err := datastore.UpdatePageview(previousPageview) if err != nil { return err } } // save new pageview err = datastore.SavePageview(pageview) if err != nil { return err } // don't you cache this w.Header().Set("Content-Type", "image/gif") w.Header().Set("Expires", "Mon, 01 Jan 1990 00:00:00 GMT") w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Set("Pragma", "no-cache") w.WriteHeader(http.StatusOK) // 1x1 px transparent GIF b, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") w.Write(b) return nil }) } // runs the aggregate func every 5 mins func aggregate() { counter.Aggregate() timeout := 5 * time.Minute for { select { case <-time.After(timeout): counter.Aggregate() } } }