106
api/api.go
Normal file
106
api/api.go
Normal file
@ -0,0 +1,106 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/TwiN/gatus/v5/config"
|
||||
static "github.com/TwiN/gatus/v5/web"
|
||||
"github.com/TwiN/health"
|
||||
fiber "github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/adaptor"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
fiberfs "github.com/gofiber/fiber/v2/middleware/filesystem"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
"github.com/gofiber/fiber/v2/middleware/redirect"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
router *fiber.App
|
||||
}
|
||||
|
||||
func New(cfg *config.Config) *API {
|
||||
api := &API{}
|
||||
api.router = api.createRouter(cfg)
|
||||
return api
|
||||
}
|
||||
|
||||
func (a *API) Router() *fiber.App {
|
||||
return a.router
|
||||
}
|
||||
|
||||
func (a *API) createRouter(cfg *config.Config) *fiber.App {
|
||||
app := fiber.New(fiber.Config{
|
||||
ErrorHandler: func(c *fiber.Ctx, err error) error {
|
||||
log.Printf("[api.ErrorHandler] %s", err.Error())
|
||||
return fiber.DefaultErrorHandler(c, err)
|
||||
},
|
||||
})
|
||||
if os.Getenv("ENVIRONMENT") == "dev" {
|
||||
app.Use(cors.New(cors.Config{
|
||||
AllowOrigins: "http://localhost:8081",
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
}
|
||||
apiRouter := app.Group("/api")
|
||||
protectedAPIRouter := apiRouter.Group("/")
|
||||
unprotectedAPIRouter := apiRouter.Group("/")
|
||||
if cfg.Metrics {
|
||||
metricsHandler := promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
|
||||
DisableCompression: true,
|
||||
}))
|
||||
app.Get("/metrics", adaptor.HTTPHandler(metricsHandler))
|
||||
}
|
||||
// Security (ORDER IS IMPORTANT: middlewares must be applied before the routes are registered)
|
||||
if cfg.Security != nil {
|
||||
if err := cfg.Security.RegisterHandlers(app); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := cfg.Security.ApplySecurityMiddleware(protectedAPIRouter); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Middlewares
|
||||
app.Use(recover.New())
|
||||
app.Use(compress.New())
|
||||
// Routes
|
||||
handler := ConfigHandler{securityConfig: cfg.Security}
|
||||
unprotectedAPIRouter.Get("/v1/config", handler.GetConfig)
|
||||
protectedAPIRouter.Get("/v1/endpoints/statuses", EndpointStatuses(cfg))
|
||||
protectedAPIRouter.Get("/v1/endpoints/:key/statuses", EndpointStatus)
|
||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/health/badge.svg", HealthBadge)
|
||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/uptimes/:duration/badge.svg", UptimeBadge)
|
||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/badge.svg", ResponseTimeBadge(cfg))
|
||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/chart.svg", ResponseTimeChart)
|
||||
// SPA
|
||||
app.Get("/", SinglePageApplication(cfg.UI))
|
||||
app.Get("/endpoints/:name", SinglePageApplication(cfg.UI))
|
||||
// Health endpoint
|
||||
healthHandler := health.Handler().WithJSON(true)
|
||||
app.Get("/health", func(c *fiber.Ctx) error {
|
||||
statusCode, body := healthHandler.GetResponseStatusCodeAndBody()
|
||||
return c.Status(statusCode).Send(body)
|
||||
})
|
||||
// Everything else falls back on static content
|
||||
app.Use(redirect.New(redirect.Config{
|
||||
Rules: map[string]string{
|
||||
"/index.html": "/",
|
||||
},
|
||||
StatusCode: 301,
|
||||
}))
|
||||
staticFileSystem, err := fs.Sub(static.FileSystem, static.RootPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.Use("/", fiberfs.New(fiberfs.Config{
|
||||
Root: http.FS(staticFileSystem),
|
||||
Index: "index.html",
|
||||
Browse: true,
|
||||
}))
|
||||
return app
|
||||
}
|
Reference in New Issue
Block a user