parent
ee6b8def4a
commit
492d522d24
@ -1 +1,2 @@
|
||||
.bin
|
||||
.bin
|
||||
.idea
|
@ -0,0 +1,8 @@
|
||||
SELECT
|
||||
domain,
|
||||
ROUND(AVG(totalTimeMs), 3) as averageTotalTime,
|
||||
COUNT(*) as requests,
|
||||
strftime('%s', started)/(5*60) as "timeWindow"
|
||||
FROM log
|
||||
GROUP BY domain, strftime('%s', started) / (5 * 60)
|
||||
ORDER BY started desc;
|
@ -0,0 +1,236 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
||||
type adminHandler struct {
|
||||
Cache
|
||||
Storage
|
||||
*RuleEngine
|
||||
h http.Handler
|
||||
}
|
||||
|
||||
func NewAdminHandler(c Cache, s Storage, re *RuleEngine) http.Handler {
|
||||
handler := chi.NewRouter()
|
||||
|
||||
a := &adminHandler{
|
||||
Cache: c,
|
||||
Storage: s,
|
||||
RuleEngine: re,
|
||||
h: handler,
|
||||
}
|
||||
|
||||
handler.Use(middleware.RequestID)
|
||||
handler.Use(middleware.RealIP)
|
||||
handler.Use(middleware.Logger)
|
||||
handler.Use(middleware.AllowContentType("application/json; utf-8"))
|
||||
handler.Use(middleware.Timeout(time.Second * 10))
|
||||
|
||||
handler.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{"http://*", "https://*"},
|
||||
AllowedMethods: []string{"GET", "PUT", "DELETE", "POST", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Content-Type", "Accept"},
|
||||
AllowCredentials: false,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
|
||||
handler.Use(middleware.Recoverer)
|
||||
|
||||
handler.Route("/api/v1", func(r chi.Router) {
|
||||
r.Get("/metrics/log", RestHandler(a.getLog).ToHF())
|
||||
r.Get("/metrics/stats", RestHandler(a.getStats).ToHF())
|
||||
// r.Delete("/cache/purgeall", RestHandler(a.purgeAll).ToHF())
|
||||
// r.Delete("/cache/purge", a.purgeKey)
|
||||
// r.Get("/cache", a.getCacheContents)
|
||||
// r.Put("/rules", a.createRule)
|
||||
// r.Get("/rules", a.getRules)
|
||||
// r.Delete("/rules/{id}", a.deleteRole)
|
||||
// r.Put("/rules/lists", a.addRulelist)
|
||||
// r.Get("/rules/lists", a.getRuleLists)
|
||||
// r.Delete("/rules/lists/{id}", a.deleteRuleList)
|
||||
// r.Post("/rules/lists/reload", a.reloadRuleLists)
|
||||
})
|
||||
|
||||
return a
|
||||
|
||||
}
|
||||
|
||||
func (a *adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
a.h.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (a *adminHandler) getStats(r *http.Request) (*RestResponse, error) {
|
||||
q := r.URL.Query()
|
||||
startFilter := q.Get("start")
|
||||
endFilter := q.Get("end")
|
||||
key := q.Get("key")
|
||||
intervalSecondsStr := q.Get("interval")
|
||||
|
||||
var err error
|
||||
start := time.Now().UTC().Add(time.Hour * -86400)
|
||||
end := time.Now().UTC()
|
||||
|
||||
if startFilter != "" {
|
||||
if start, err = time.Parse(ISO8601, startFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if endFilter != "" {
|
||||
if end, err = time.Parse(ISO8601, endFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
lai := LogAggregateInput{
|
||||
Start: start,
|
||||
End: end,
|
||||
Column: key,
|
||||
}
|
||||
|
||||
if intervalSecondsStr != "" {
|
||||
if lai.IntervalSeconds, err = strconv.Atoi(intervalSecondsStr); err != nil {
|
||||
return nil, errors.New("interval query param must be a valid whole number")
|
||||
}
|
||||
}
|
||||
|
||||
la, err := a.Storage.GetLogAggregate(lai)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RestResponse{
|
||||
Status: http.StatusOK,
|
||||
Payload: struct {
|
||||
Success bool `json:"success"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}{
|
||||
Success: true,
|
||||
Payload: la,
|
||||
},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (a *adminHandler) getLog(r *http.Request) (*RestResponse, error) {
|
||||
q := r.URL.Query()
|
||||
startFilter := q.Get("start")
|
||||
endFilter := q.Get("end")
|
||||
// filter := q.Get("filter")
|
||||
pageStr := q.Get("page")
|
||||
|
||||
var err error
|
||||
var page int
|
||||
start := time.Now().UTC().Add(time.Hour * -86400)
|
||||
end := time.Now().UTC()
|
||||
|
||||
if startFilter != "" {
|
||||
if start, err = time.Parse(ISO8601, startFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if endFilter != "" {
|
||||
if end, err = time.Parse(ISO8601, endFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if pageStr != "" {
|
||||
page, _ = strconv.Atoi(pageStr)
|
||||
}
|
||||
|
||||
ql, err := a.Storage.GetLog(GetLogInput{
|
||||
Start: start,
|
||||
End: end,
|
||||
Limit: 250,
|
||||
Page: page,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RestResponse{
|
||||
Status: http.StatusOK,
|
||||
Payload: struct {
|
||||
Success bool `json:"success"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}{
|
||||
Success: true,
|
||||
Payload: ql,
|
||||
},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
type RestResponse struct {
|
||||
Status int
|
||||
Headers http.Header
|
||||
Payload struct {
|
||||
Success bool `json:"success"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *RestResponse) Write(w http.ResponseWriter) error {
|
||||
if rr.Status != 0 && rr.Status != 200 {
|
||||
w.WriteHeader(rr.Status)
|
||||
}
|
||||
|
||||
for k, v := range rr.Headers {
|
||||
for _, ve := range v {
|
||||
w.Header().Add(k, ve)
|
||||
}
|
||||
}
|
||||
|
||||
e := json.NewEncoder(w)
|
||||
e.SetIndent("\n", "\t")
|
||||
|
||||
if err := e.Encode(rr.Payload); err != nil {
|
||||
return fmt.Errorf("could not serialize struct for http response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RestHandler func(request *http.Request) (*RestResponse, error)
|
||||
|
||||
func (rh RestHandler) ToHF() http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, r *http.Request) { rh.ServeHTTP(rw, r) }
|
||||
}
|
||||
|
||||
func (rh RestHandler) Error(e error) *RestResponse {
|
||||
return &RestResponse{
|
||||
Status: http.StatusInternalServerError,
|
||||
Payload: struct {
|
||||
Success bool `json:"success"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}{
|
||||
Success: false,
|
||||
Payload: e.Error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r RestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
response, err := r(req)
|
||||
if err != nil {
|
||||
response = r.Error(err)
|
||||
}
|
||||
|
||||
if err := response.Write(w); err != nil {
|
||||
log.Printf("Error occurred handling rest response: %v", err)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue