156 lines
3.5 KiB
Go
156 lines
3.5 KiB
Go
package routes
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/labstack/echo/v4/middleware"
|
|
"github.com/labstack/gommon/log"
|
|
"net/http"
|
|
"vibeStonk/server/repository"
|
|
"vibeStonk/server/services"
|
|
)
|
|
|
|
type ApiServer interface {
|
|
AddRoutesBulk(routeProviders []Provider) error
|
|
AddRoute(routeProvider Provider) error
|
|
|
|
GetSystemServices() *services.SystemServices
|
|
GetCommonMiddleware() *SystemMiddleware
|
|
|
|
Start() error
|
|
}
|
|
|
|
type SystemMiddleware struct {
|
|
UserAuth echo.MiddlewareFunc
|
|
}
|
|
|
|
func NewAPIServer(config *repository.Config) (ApiServer, error) {
|
|
e := echo.New()
|
|
e.HideBanner = true
|
|
|
|
// Middleware
|
|
e.Use(middleware.Logger())
|
|
e.Use(middleware.Recover())
|
|
e.Use(middleware.CORS())
|
|
|
|
svcs, err := services.NewSystemServices(config)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to initialize server's system services: %w", err)
|
|
}
|
|
|
|
server := &apiServer{
|
|
e: e,
|
|
config: config,
|
|
Services: svcs,
|
|
}
|
|
|
|
// initialize common mware utilities
|
|
mware := &SystemMiddleware{
|
|
UserAuth: server.AuthenticationMiddleware,
|
|
}
|
|
server.Middleware = mware
|
|
|
|
server.developmentNonsense()
|
|
return server, nil
|
|
}
|
|
|
|
type apiServer struct {
|
|
e *echo.Echo
|
|
config *repository.Config
|
|
Services *services.SystemServices
|
|
Middleware *SystemMiddleware
|
|
}
|
|
|
|
func (a *apiServer) addRoute(route *Route) error {
|
|
var handler func(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
|
switch route.Method {
|
|
case http.MethodPost:
|
|
handler = a.e.POST
|
|
case http.MethodGet:
|
|
handler = a.e.GET
|
|
case http.MethodPut:
|
|
handler = a.e.PUT
|
|
case http.MethodDelete:
|
|
handler = a.e.DELETE
|
|
default:
|
|
return fmt.Errorf("failed to add Route; unknown method: %s", route.Method)
|
|
}
|
|
|
|
log.Infof("Registering route[%s]{%s}\n", route.Endpoint.String(), route.Method)
|
|
handler(route.Endpoint.String(), route.HandlerFn, route.Middleware...)
|
|
return nil
|
|
}
|
|
|
|
func (a *apiServer) AddRoute(provider Provider) error {
|
|
for _, route := range provider.Provide(a.Middleware) {
|
|
err := a.addRoute(route)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to add route[%s]: %w", route.Endpoint.String(), err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *apiServer) AddRoutesBulk(routeProviders []Provider) error {
|
|
for _, rp := range routeProviders {
|
|
err := a.AddRoute(rp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *apiServer) developmentNonsense() {
|
|
// this is here until we get a hosting | ci/cd pipeline established
|
|
a.e.Static("/", "./content/")
|
|
}
|
|
|
|
func (a *apiServer) Start() error {
|
|
return a.e.Start(a.config.ListenAddress)
|
|
}
|
|
|
|
func (a *apiServer) GetSystemServices() *services.SystemServices {
|
|
return a.Services
|
|
}
|
|
|
|
func (a *apiServer) GetCommonMiddleware() *SystemMiddleware {
|
|
return a.Middleware
|
|
}
|
|
|
|
// region system-level middleware
|
|
|
|
const authHeader = "Bearer"
|
|
|
|
var (
|
|
ErrNoAuthToken = errors.New("no 'Bearer' token header")
|
|
)
|
|
|
|
func (a *apiServer) AuthenticationMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
token := c.Request().Header.Get(authHeader)
|
|
if len(token) == 0 {
|
|
c.Response().Status = http.StatusUnauthorized
|
|
_, err := c.Response().Write([]byte(ErrNoAuthToken.Error()))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write error response: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
user, err := a.Services.AuthService.AuthenticateToken(token)
|
|
if err != nil {
|
|
c.Response().Status = http.StatusUnauthorized
|
|
return fmt.Errorf("failed to authenticate user's bearer token: %w", err)
|
|
}
|
|
|
|
newRequestContext(c, user)
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
// endregion
|