mirror of
https://github.com/status-im/fathom.git
synced 2025-03-01 11:30:28 +00:00
universal error handling in api pkg by returning errs from http handlers
This commit is contained in:
parent
08ce5e651d
commit
b3ee15cc9a
@ -25,7 +25,7 @@ type login struct {
|
|||||||
var store = sessions.NewCookieStore([]byte(os.Getenv("ANA_SECRET_KEY")))
|
var store = sessions.NewCookieStore([]byte(os.Getenv("ANA_SECRET_KEY")))
|
||||||
|
|
||||||
// URL: POST /api/session
|
// URL: POST /api/session
|
||||||
var LoginHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var LoginHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
||||||
// check login creds
|
// check login creds
|
||||||
var l login
|
var l login
|
||||||
@ -36,27 +36,31 @@ var LoginHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)
|
|||||||
// compare pwd
|
// compare pwd
|
||||||
if err != nil || bcrypt.CompareHashAndPassword([]byte(u.HashedPassword), []byte(l.Password)) != nil {
|
if err != nil || bcrypt.CompareHashAndPassword([]byte(u.HashedPassword), []byte(l.Password)) != nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
respond(w, envelope{Error: "invalid_credentials"})
|
return respond(w, envelope{Error: "invalid_credentials"})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
session, _ := store.Get(r, "auth")
|
session, _ := store.Get(r, "auth")
|
||||||
session.Values["user_id"] = u.ID
|
session.Values["user_id"] = u.ID
|
||||||
err = session.Save(r, w)
|
err = session.Save(r, w)
|
||||||
checkError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
respond(w, envelope{Data: true})
|
return respond(w, envelope{Data: true})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: DELETE /api/session
|
// URL: DELETE /api/session
|
||||||
var LogoutHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var LogoutHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
session, _ := store.Get(r, "auth")
|
session, _ := store.Get(r, "auth")
|
||||||
if !session.IsNew {
|
if !session.IsNew {
|
||||||
session.Options.MaxAge = -1
|
session.Options.MaxAge = -1
|
||||||
session.Save(r, w)
|
err := session.Save(r, w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
respond(w, envelope{Data: true})
|
return respond(w, envelope{Data: true})
|
||||||
})
|
})
|
||||||
|
|
||||||
/* middleware */
|
/* middleware */
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// URL: /api/browsers
|
// URL: /api/browsers
|
||||||
var GetBrowsersHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetBrowsersHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.Browsers(before, after, getRequestedLimit(r))
|
results := count.Browsers(before, after, getRequestedLimit(r))
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
@ -11,28 +11,23 @@ import (
|
|||||||
"github.com/mssola/user_agent"
|
"github.com/mssola/user_agent"
|
||||||
"github.com/usefathom/fathom/pkg/datastore"
|
"github.com/usefathom/fathom/pkg/datastore"
|
||||||
"github.com/usefathom/fathom/pkg/models"
|
"github.com/usefathom/fathom/pkg/models"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var buffer []*models.Pageview
|
var buffer []*models.Pageview
|
||||||
var bufferSize = 250
|
var bufferSize = 250
|
||||||
var timeout = 100 * time.Millisecond
|
var timeout = 100 * time.Millisecond
|
||||||
|
|
||||||
func getRequestIp(r *http.Request) string {
|
|
||||||
ipAddress := r.RemoteAddr
|
|
||||||
|
|
||||||
headerForwardedFor := r.Header.Get("X-Forwarded-For")
|
|
||||||
if headerForwardedFor != "" {
|
|
||||||
ipAddress = headerForwardedFor
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func persistPageviews() {
|
func persistPageviews() {
|
||||||
if len(buffer) > 0 {
|
if len(buffer) > 0 {
|
||||||
err := datastore.SavePageviews(buffer)
|
err := datastore.SavePageviews(buffer)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error saving pageviews: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear buffer regardless of error... this means data loss, but better than filling the buffer for now
|
||||||
buffer = buffer[:0]
|
buffer = buffer[:0]
|
||||||
checkError(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +50,12 @@ func NewCollectHandler() http.Handler {
|
|||||||
pageviews := make(chan *models.Pageview, 100)
|
pageviews := make(chan *models.Pageview, 100)
|
||||||
go processBuffer(pageviews)
|
go processBuffer(pageviews)
|
||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
ua := user_agent.New(r.UserAgent())
|
|
||||||
|
|
||||||
// abort if this is a bot.
|
// abort if this is a bot.
|
||||||
|
ua := user_agent.New(r.UserAgent())
|
||||||
if ua.Bot() {
|
if ua.Bot() {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
q := r.URL.Query()
|
q := r.URL.Query()
|
||||||
@ -75,7 +70,9 @@ func NewCollectHandler() http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = datastore.SavePage(page)
|
err = datastore.SavePage(page)
|
||||||
checkError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find or insert visitor.
|
// find or insert visitor.
|
||||||
@ -98,7 +95,9 @@ func NewCollectHandler() http.Handler {
|
|||||||
visitor.BrowserName, visitor.BrowserVersion = ua.Browser()
|
visitor.BrowserName, visitor.BrowserVersion = ua.Browser()
|
||||||
visitor.BrowserName = parseMajorMinor(visitor.BrowserName)
|
visitor.BrowserName = parseMajorMinor(visitor.BrowserName)
|
||||||
err = datastore.SaveVisitor(visitor)
|
err = datastore.SaveVisitor(visitor)
|
||||||
checkError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pageview := &models.Pageview{
|
pageview := &models.Pageview{
|
||||||
@ -127,6 +126,7 @@ func NewCollectHandler() http.Handler {
|
|||||||
// 1x1 px transparent GIF
|
// 1x1 px transparent GIF
|
||||||
b, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
|
b, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
|
||||||
w.Write(b)
|
w.Write(b)
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
43
pkg/api/http.go
Normal file
43
pkg/api/http.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is our custom HTTP handler with error returns
|
||||||
|
type Handler func(w http.ResponseWriter, r *http.Request) error
|
||||||
|
|
||||||
|
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := h(w, r); err != nil {
|
||||||
|
HandleError(w, r, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFunc takes a custom Handler func and converts it to http.HandlerFunc
|
||||||
|
func HandlerFunc(fn Handler) http.HandlerFunc {
|
||||||
|
return http.HandlerFunc(Handler(fn).ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleError handles errors
|
||||||
|
func HandleError(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"request": r.Method + " " + r.RequestURI,
|
||||||
|
"error": err,
|
||||||
|
}).Error("error handling request")
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write([]byte("false"))
|
||||||
|
}
|
||||||
|
|
||||||
|
type envelope struct {
|
||||||
|
Data interface{}
|
||||||
|
Error interface{} `json:"omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func respond(w http.ResponseWriter, d interface{}) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
err := json.NewEncoder(w).Encode(d)
|
||||||
|
return err
|
||||||
|
}
|
@ -7,8 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// URL: /api/languages
|
// URL: /api/languages
|
||||||
var GetLanguagesHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetLanguagesHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.Languages(before, after, getRequestedLimit(r))
|
results := count.Languages(before, after, getRequestedLimit(r))
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
@ -15,7 +15,7 @@ type pageviews struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// URL: /api/pageviews
|
// URL: /api/pageviews
|
||||||
var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetPageviewsHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
|
|
||||||
stmt, err := datastore.DB.Prepare(`
|
stmt, err := datastore.DB.Prepare(`
|
||||||
@ -30,38 +30,46 @@ var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.R
|
|||||||
GROUP BY p.path, p.hostname
|
GROUP BY p.path, p.hostname
|
||||||
ORDER BY count DESC
|
ORDER BY count DESC
|
||||||
LIMIT ?`)
|
LIMIT ?`)
|
||||||
|
if err != nil {
|
||||||
checkError(err)
|
return err
|
||||||
|
}
|
||||||
defer stmt.Close()
|
defer stmt.Close()
|
||||||
|
|
||||||
rows, err := stmt.Query(before, after, defaultLimit)
|
rows, err := stmt.Query(before, after, defaultLimit)
|
||||||
checkError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
results := make([]pageviews, 0)
|
results := make([]pageviews, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var p pageviews
|
var p pageviews
|
||||||
err = rows.Scan(&p.Hostname, &p.Path, &p.Count, &p.CountUnique)
|
err = rows.Scan(&p.Hostname, &p.Path, &p.Count, &p.CountUnique)
|
||||||
checkError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
results = append(results, p)
|
results = append(results, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
checkError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: /api/pageviews/count
|
// URL: /api/pageviews/count
|
||||||
var GetPageviewsCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetPageviewsCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
result := count.Pageviews(before, after)
|
result := count.Pageviews(before, after)
|
||||||
respond(w, envelope{Data: result})
|
return respond(w, envelope{Data: result})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: /api/pageviews/group/day
|
// URL: /api/pageviews/group/day
|
||||||
var GetPageviewsPeriodCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetPageviewsPeriodCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.PageviewsPerDay(before, after)
|
results := count.PageviewsPerDay(before, after)
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -12,25 +10,6 @@ import (
|
|||||||
const defaultPeriod = 7
|
const defaultPeriod = 7
|
||||||
const defaultLimit = 10
|
const defaultLimit = 10
|
||||||
|
|
||||||
type envelope struct {
|
|
||||||
Data interface{}
|
|
||||||
Error interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func respond(w http.ResponseWriter, d interface{}) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
enc := json.NewEncoder(w)
|
|
||||||
err := enc.Encode(d)
|
|
||||||
checkError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// log fatal errors
|
|
||||||
func checkError(err error) {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRequestedLimit(r *http.Request) int {
|
func getRequestedLimit(r *http.Request) int {
|
||||||
limit, err := strconv.Atoi(r.URL.Query().Get("limit"))
|
limit, err := strconv.Atoi(r.URL.Query().Get("limit"))
|
||||||
if err != nil || limit == 0 {
|
if err != nil || limit == 0 {
|
||||||
@ -64,3 +43,14 @@ func parseMajorMinor(v string) string {
|
|||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequestIp(r *http.Request) string {
|
||||||
|
ipAddress := r.RemoteAddr
|
||||||
|
|
||||||
|
headerForwardedFor := r.Header.Get("X-Forwarded-For")
|
||||||
|
if headerForwardedFor != "" {
|
||||||
|
ipAddress = headerForwardedFor
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipAddress
|
||||||
|
}
|
@ -7,8 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// URL: /api/referrers
|
// URL: /api/referrers
|
||||||
var GetReferrersHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetReferrersHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.Referrers(before, after, getRequestedLimit(r))
|
results := count.Referrers(before, after, getRequestedLimit(r))
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// URL: /api/screen-resolutions
|
// URL: /api/screen-resolutions
|
||||||
var GetScreenResolutionsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetScreenResolutionsHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.Screens(before, after, getRequestedLimit(r))
|
results := count.Screens(before, after, getRequestedLimit(r))
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
@ -7,21 +7,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// URL: /api/visitors/count
|
// URL: /api/visitors/count
|
||||||
var GetVisitorsCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetVisitorsCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
result := count.Visitors(before, after)
|
result := count.Visitors(before, after)
|
||||||
respond(w, envelope{Data: result})
|
return respond(w, envelope{Data: result})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: /api/visitors/count/realtime
|
// URL: /api/visitors/count/realtime
|
||||||
var GetVisitorsRealtimeCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetVisitorsRealtimeCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
result := count.RealtimeVisitors()
|
result := count.RealtimeVisitors()
|
||||||
respond(w, envelope{Data: result})
|
return respond(w, envelope{Data: result})
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL: /api/visitors/count/group/:period
|
// URL: /api/visitors/count/group/:period
|
||||||
var GetVisitorsPeriodCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var GetVisitorsPeriodCountHandler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
before, after := getRequestedPeriods(r)
|
before, after := getRequestedPeriods(r)
|
||||||
results := count.VisitorsPerDay(before, after)
|
results := count.VisitorsPerDay(before, after)
|
||||||
respond(w, envelope{Data: results})
|
return respond(w, envelope{Data: results})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user