2019-11-08 12:35:27 +00:00
|
|
|
package merp
|
2019-10-03 19:30:04 +00:00
|
|
|
|
2019-11-07 19:19:04 +00:00
|
|
|
import "encoding/json"
|
2019-10-25 15:09:31 +00:00
|
|
|
import "fmt"
|
2019-10-25 16:17:14 +00:00
|
|
|
import "net/http"
|
2019-10-25 15:09:31 +00:00
|
|
|
import "os"
|
2019-11-09 05:30:54 +00:00
|
|
|
import "regexp"
|
|
|
|
import "strconv"
|
2019-10-25 16:17:14 +00:00
|
|
|
import "time"
|
|
|
|
|
2019-11-08 12:53:57 +00:00
|
|
|
import "github.com/didip/tollbooth"
|
|
|
|
import "github.com/didip/tollbooth_gin"
|
2019-10-25 16:17:14 +00:00
|
|
|
import "github.com/gin-gonic/gin"
|
|
|
|
import "github.com/dn365/gin-zerolog"
|
2019-11-08 12:35:27 +00:00
|
|
|
import "github.com/thoas/stats"
|
2019-10-03 19:30:04 +00:00
|
|
|
|
2019-11-09 05:30:54 +00:00
|
|
|
import "github.com/astaxie/beego/orm"
|
|
|
|
import _ "github.com/lib/pq" //revive:disable-line
|
|
|
|
|
2019-11-09 06:01:59 +00:00
|
|
|
// Server is the central structure of the HTTP API server.
|
2019-11-09 05:59:16 +00:00
|
|
|
type Server struct {
|
2019-11-09 05:30:54 +00:00
|
|
|
db orm.Ormer
|
|
|
|
debug bool
|
|
|
|
gin *gin.Engine
|
|
|
|
port uint
|
|
|
|
server *http.Server
|
|
|
|
stats *stats.Stats
|
|
|
|
thingRegex *regexp.Regexp
|
|
|
|
}
|
|
|
|
|
2019-11-09 06:01:59 +00:00
|
|
|
// NewServer returns a Server, so that you can run the API.
|
2019-11-09 05:59:16 +00:00
|
|
|
func NewServer() *Server {
|
|
|
|
ms := new(Server)
|
2019-11-09 05:30:54 +00:00
|
|
|
ms.init()
|
|
|
|
return ms
|
|
|
|
}
|
|
|
|
|
2019-11-09 05:59:16 +00:00
|
|
|
func (ms *Server) init() {
|
2019-11-09 05:30:54 +00:00
|
|
|
|
|
|
|
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{
|
2019-11-09 05:35:45 +00:00
|
|
|
Addr: fmt.Sprintf(":%d", ms.port),
|
2019-11-09 05:30:54 +00:00
|
|
|
Handler: ms.gin,
|
|
|
|
ReadTimeout: 10 * time.Second,
|
|
|
|
WriteTimeout: 10 * time.Second,
|
|
|
|
MaxHeaderBytes: 1 << 20,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 05:59:16 +00:00
|
|
|
func (ms *Server) connectDB() {
|
2019-11-09 05:30:54 +00:00
|
|
|
ms.db = GetDB()
|
|
|
|
}
|
|
|
|
|
2019-11-08 13:01:57 +00:00
|
|
|
// ServeForever causes merp to serve http forever
|
2019-11-09 05:59:16 +00:00
|
|
|
func (ms *Server) ServeForever() {
|
2019-11-09 05:35:45 +00:00
|
|
|
err := ms.server.ListenAndServe()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-11-07 08:51:35 +00:00
|
|
|
}
|
|
|
|
|
2019-11-09 06:01:59 +00:00
|
|
|
func (ms *Server) healthCheckHandler() http.HandlerFunc {
|
2019-11-07 19:19:04 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 06:01:59 +00:00
|
|
|
func (ms *Server) statsHandler() http.HandlerFunc {
|
2019-11-08 12:35:27 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2019-11-09 05:30:54 +00:00
|
|
|
stats := ms.stats.Data()
|
2019-11-08 12:35:27 +00:00
|
|
|
b, _ := json.Marshal(stats)
|
|
|
|
w.Write(b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 05:59:16 +00:00
|
|
|
func (ms *Server) setupRoutes() {
|
2019-11-09 05:30:54 +00:00
|
|
|
if !ms.debug {
|
2019-10-25 16:17:14 +00:00
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
}
|
|
|
|
|
2019-11-08 12:53:57 +00:00
|
|
|
limiter := tollbooth.NewLimiter(5, nil)
|
|
|
|
|
2019-11-09 05:30:54 +00:00
|
|
|
ms.stats = stats.New()
|
2019-11-08 12:35:27 +00:00
|
|
|
|
2019-10-25 16:17:14 +00:00
|
|
|
// empty router
|
|
|
|
r := gin.New()
|
|
|
|
|
|
|
|
// wrap panics:
|
|
|
|
r.Use(gin.Recovery())
|
|
|
|
|
|
|
|
// attach logger middleware
|
|
|
|
r.Use(ginzerolog.Logger("gin"))
|
|
|
|
|
2019-11-08 12:35:27 +00:00
|
|
|
r.Use(func(c *gin.Context) {
|
2019-11-09 05:30:54 +00:00
|
|
|
beginning, recorder := ms.stats.Begin(c.Writer)
|
2019-11-08 12:35:27 +00:00
|
|
|
c.Next()
|
2019-11-09 05:30:54 +00:00
|
|
|
ms.stats.End(beginning, stats.WithRecorder(recorder))
|
2019-11-08 12:35:27 +00:00
|
|
|
})
|
|
|
|
|
2019-11-09 06:01:59 +00:00
|
|
|
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()))
|
2019-11-09 05:30:54 +00:00
|
|
|
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())
|
2019-10-03 19:30:04 +00:00
|
|
|
|
2019-11-09 05:30:54 +00:00
|
|
|
ms.gin = r
|
2019-10-03 19:30:04 +00:00
|
|
|
}
|