package merp import "encoding/json" import "fmt" import "net/http" import "os" import "regexp" import "strconv" import "time" import "github.com/didip/tollbooth" import "github.com/didip/tollbooth_gin" import "github.com/gin-gonic/gin" import "github.com/dn365/gin-zerolog" import "github.com/thoas/stats" import "github.com/astaxie/beego/orm" import _ "github.com/lib/pq" //revive:disable-line type MerpServer struct { db orm.Ormer debug bool gin *gin.Engine port uint server *http.Server stats *stats.Stats thingRegex *regexp.Regexp } func NewMerpServer() *MerpServer { ms := new(MerpServer) ms.init() return ms } func (ms *MerpServer) init() { ms.thingRegex = regexp.MustCompile(`^[a-zA-Z0-9\_\-]+$`) if os.Getenv("DEBUG") != "" { ms.debug = true } ms.port = 8080 var s uint64 var err error if os.Getenv("PORT") != "" { if s, err = strconv.ParseUint(os.Getenv("PORT"), 10, 64); err == nil { } else { panic("invalid PORT in environment") } ms.port = uint(s) } ms.connectDB() ms.setupRoutes() ms.server = &http.Server{ Addr: fmt.Sprintf(":%s", ms.port), Handler: ms.gin, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } } func (ms *MerpServer) connectDB() { ms.db = GetDB() } // ServeForever causes merp to serve http forever func (ms *MerpServer) ServeForever() { ms.server.ListenAndServe() } func (ms *MerpServer) HealthCheckHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { result := gin.H{ "status": "ok", "now": time.Now().UTC().Format(time.RFC3339), } json, err := json.Marshal(result) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(json) } } func (ms *MerpServer) StatsHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") stats := ms.stats.Data() b, _ := json.Marshal(stats) w.Write(b) } } func (ms *MerpServer) setupRoutes() { if !ms.debug { gin.SetMode(gin.ReleaseMode) } limiter := tollbooth.NewLimiter(5, nil) ms.stats = stats.New() // empty router r := gin.New() // wrap panics: r.Use(gin.Recovery()) // attach logger middleware r.Use(ginzerolog.Logger("gin")) r.Use(func(c *gin.Context) { beginning, recorder := ms.stats.Begin(c.Writer) c.Next() ms.stats.End(beginning, stats.WithRecorder(recorder)) }) r.GET("/.well-known/healthcheck.json", gin.WrapF(ms.HealthCheckHandler())) r.GET("/admin/healthcheck.json", gin.WrapF(ms.HealthCheckHandler())) r.GET("/admin/stats.json", gin.WrapF(ms.StatsHandler())) r.GET("/admin/other.json", gin.WrapF(ms.StatsHandler())) r.GET("/merp/for/:thing", tollbooth_gin.LimitHandler(limiter), ms.handleNewMerp()) r.GET("/get/latest/merp/for/:thing", tollbooth_gin.LimitHandler(limiter), ms.getLatestMerps()) r.GET("/get/latest/merps", tollbooth_gin.LimitHandler(limiter), ms.getLatestMerps()) r.GET("/get/merps/for/:thing", tollbooth_gin.LimitHandler(limiter), ms.getLatestMerps()) ms.gin = r }