2025-03-30 23:14:49 +02:00

119 lines
3.8 KiB
Go

package handlers
import (
"context"
"net/http"
"github.com/go-pkgz/routegroup"
"github.com/h44z/wg-portal/internal/app/api/core"
"github.com/h44z/wg-portal/internal/app/api/core/middleware/cors"
"github.com/h44z/wg-portal/internal/app/api/core/middleware/csrf"
"github.com/h44z/wg-portal/internal/app/api/core/respond"
)
type SessionMiddleware interface {
// SetData sets the session data for the given context.
SetData(ctx context.Context, val SessionData)
// GetData returns the session data for the given context. If no data is found, the default session data is returned.
GetData(ctx context.Context) SessionData
// DestroyData destroys the session data for the given context.
DestroyData(ctx context.Context)
// GetString returns the string value for the given key. If no value is found, an empty string is returned.
GetString(ctx context.Context, key string) string
// Put sets the value for the given key.
Put(ctx context.Context, key string, value any)
// LoadAndSave is a middleware that loads the session data for the given request and saves it after the request is
// finished.
LoadAndSave(next http.Handler) http.Handler
}
type Handler interface {
// GetName returns the name of the handler.
GetName() string
// RegisterRoutes registers the routes for the handler. The session manager is passed to the handler.
RegisterRoutes(g *routegroup.Bundle)
}
// To compile the API documentation use the
// api_build_tool
// command that can be found in the $PROJECT_ROOT/cmd/api_build_tool directory.
// @title WireGuard Portal SPA-UI API
// @version 0.0
// @description WireGuard Portal API - UI Endpoints
// @contact.name WireGuard Portal Developers
// @contact.url https://github.com/h44z/wg-portal
// @BasePath /api/v0
// @query.collection.format multi
func NewRestApi(
session SessionMiddleware,
handlers ...Handler,
) core.ApiEndpointSetupFunc {
return func() (core.ApiVersion, core.GroupSetupFn) {
return "v0", func(group *routegroup.Bundle) {
csrfMiddleware := csrf.New(func(r *http.Request) string {
return session.GetData(r.Context()).CsrfToken
}, func(r *http.Request, token string) {
currentSession := session.GetData(r.Context())
currentSession.CsrfToken = token
session.SetData(r.Context(), currentSession)
})
group.Use(session.LoadAndSave)
group.Use(csrfMiddleware.Handler)
group.Use(cors.New().Handler)
group.With(csrfMiddleware.RefreshToken).HandleFunc("GET /csrf", handleCsrfGet())
// Handler functions
for _, h := range handlers {
h.RegisterRoutes(group)
}
}
}
}
// handleCsrfGet returns a gorm Handler function.
//
// @ID base_handleCsrfGet
// @Tags Security
// @Summary Get a CSRF token for the current session.
// @Produce json
// @Success 200 {object} string
// @Router /csrf [get]
func handleCsrfGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
respond.JSON(w, http.StatusOK, csrf.GetToken(r.Context()))
}
}
// region handler-interfaces
type Authenticator interface {
// LoggedIn checks if a user is logged in. If scopes are given, they are validated as well.
LoggedIn(scopes ...Scope) func(next http.Handler) http.Handler
// UserIdMatch checks if the user id in the session matches the user id in the request. If not, the request is aborted.
UserIdMatch(idParameter string) func(next http.Handler) http.Handler
}
type Session interface {
// SetData sets the session data for the given context.
SetData(ctx context.Context, val SessionData)
// GetData returns the session data for the given context. If no data is found, the default session data is returned.
GetData(ctx context.Context) SessionData
// DestroyData destroys the session data for the given context.
DestroyData(ctx context.Context)
}
type Validator interface {
// Struct validates the given struct.
Struct(s interface{}) error
}
// endregion handler-interfaces