chore: use interfaces for web related services

This commit is contained in:
Christoph Haas 2025-03-09 21:48:38 +01:00
parent 678b6c6456
commit 02ed7b19df
8 changed files with 214 additions and 87 deletions

View File

@ -109,10 +109,10 @@ func main() {
apiV0Session := handlersV0.NewSessionWrapper(cfg)
apiV0Auth := handlersV0.NewAuthenticationHandler(authenticator, apiV0Session)
apiV0EndpointAuth := handlersV0.NewAuthEndpoint(backend, apiV0Auth, apiV0Session, validatorManager)
apiV0EndpointUsers := handlersV0.NewUserEndpoint(backend, apiV0Auth, validatorManager)
apiV0EndpointInterfaces := handlersV0.NewInterfaceEndpoint(backend, apiV0Auth, validatorManager)
apiV0EndpointPeers := handlersV0.NewPeerEndpoint(backend, apiV0Auth, validatorManager)
apiV0EndpointAuth := handlersV0.NewAuthEndpoint(cfg, apiV0Auth, apiV0Session, validatorManager, backend)
apiV0EndpointUsers := handlersV0.NewUserEndpoint(cfg, apiV0Auth, validatorManager, backend)
apiV0EndpointInterfaces := handlersV0.NewInterfaceEndpoint(cfg, apiV0Auth, validatorManager, backend)
apiV0EndpointPeers := handlersV0.NewPeerEndpoint(cfg, apiV0Auth, validatorManager, backend)
apiV0EndpointConfig := handlersV0.NewConfigEndpoint(cfg, apiV0Auth)
apiV0EndpointTest := handlersV0.NewTestEndpoint(apiV0Auth)

View File

@ -36,13 +36,6 @@ type Handler interface {
RegisterRoutes(g *routegroup.Bundle)
}
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
}
// To compile the API documentation use the
// api_build_tool
// command that can be found in the $PROJECT_ROOT/cmd/api_build_tool directory.
@ -97,6 +90,27 @@ func handleCsrfGet() http.HandlerFunc {
}
}
// region session wrapper
// region handler-interfaces
// endregion session wrapper
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

View File

@ -5,6 +5,9 @@ import (
"strings"
)
// Base64UrlDecode decodes a base64 url encoded string.
// In comparison to the standard base64 encoding, the url encoding uses - instead of + and _ instead of /
// as well as . instead of =.
func Base64UrlDecode(in string) string {
in = strings.ReplaceAll(in, "-", "=")
in = strings.ReplaceAll(in, "_", "/")

View File

@ -10,36 +10,42 @@ import (
"github.com/go-pkgz/routegroup"
"github.com/h44z/wg-portal/internal/app"
"github.com/h44z/wg-portal/internal/app/api/core/request"
"github.com/h44z/wg-portal/internal/app/api/core/respond"
"github.com/h44z/wg-portal/internal/app/api/v0/model"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
)
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(s interface{}) error
type AuthenticationService interface {
// GetExternalLoginProviders returns a list of all available external login providers.
GetExternalLoginProviders(_ context.Context) []domain.LoginProviderInfo
// PlainLogin authenticates a user with a username and password.
PlainLogin(ctx context.Context, username, password string) (*domain.User, error)
// OauthLoginStep1 initiates the OAuth login flow.
OauthLoginStep1(_ context.Context, providerId string) (authCodeUrl, state, nonce string, err error)
// OauthLoginStep2 completes the OAuth login flow and logins the user in.
OauthLoginStep2(ctx context.Context, providerId, nonce, code string) (*domain.User, error)
}
type AuthEndpoint struct {
app *app.App
cfg *config.Config
authService AuthenticationService
authenticator Authenticator
session Session
validate Validator
}
func NewAuthEndpoint(app *app.App, authenticator Authenticator, session Session, validator Validator) AuthEndpoint {
func NewAuthEndpoint(
cfg *config.Config,
authenticator Authenticator,
session Session,
validator Validator,
authService AuthenticationService,
) AuthEndpoint {
return AuthEndpoint{
app: app,
cfg: cfg,
authService: authService,
authenticator: authenticator,
session: session,
validate: validator,
@ -73,7 +79,7 @@ func (e AuthEndpoint) RegisterRoutes(g *routegroup.Bundle) {
// @Router /auth/providers [get]
func (e AuthEndpoint) handleExternalLoginProvidersGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
providers := e.app.Authenticator.GetExternalLoginProviders(r.Context())
providers := e.authService.GetExternalLoginProviders(r.Context())
respond.JSON(w, http.StatusOK, model.NewLoginProviderInfos(providers))
}
@ -169,7 +175,7 @@ func (e AuthEndpoint) handleOauthInitiateGet() http.HandlerFunc {
return
}
authCodeUrl, state, nonce, err := e.app.Authenticator.OauthLoginStep1(context.Background(), provider)
authCodeUrl, state, nonce, err := e.authService.OauthLoginStep1(context.Background(), provider)
if err != nil {
if autoRedirect && e.isValidReturnUrl(returnTo) {
redirectToReturn()
@ -262,7 +268,8 @@ func (e AuthEndpoint) handleOauthCallbackGet() http.HandlerFunc {
}
loginCtx, cancel := context.WithTimeout(context.Background(), 1000*time.Second)
user, err := e.app.Authenticator.OauthLoginStep2(loginCtx, provider, currentSession.OauthNonce, oauthCode)
user, err := e.authService.OauthLoginStep2(loginCtx, provider, currentSession.OauthNonce,
oauthCode)
cancel()
if err != nil {
if returnUrl != nil && e.isValidReturnUrl(returnUrl.String()) {
@ -335,7 +342,8 @@ func (e AuthEndpoint) handleLoginPost() http.HandlerFunc {
return
}
user, err := e.app.Authenticator.PlainLogin(context.Background(), loginData.Username, loginData.Password)
user, err := e.authService.PlainLogin(context.Background(), loginData.Username,
loginData.Password)
if err != nil {
respond.JSON(w, http.StatusUnauthorized,
model.Error{Code: http.StatusUnauthorized, Message: "login failed"})
@ -372,7 +380,7 @@ func (e AuthEndpoint) handleLogoutPost() http.HandlerFunc {
// isValidReturnUrl checks if the given return URL matches the configured external URL of the application.
func (e AuthEndpoint) isValidReturnUrl(returnUrl string) bool {
if !strings.HasPrefix(returnUrl, e.app.Config.Web.ExternalUrl) {
if !strings.HasPrefix(returnUrl, e.cfg.Web.ExternalUrl) {
return false
}

View File

@ -1,29 +1,58 @@
package handlers
import (
"context"
"io"
"net/http"
"github.com/go-pkgz/routegroup"
"github.com/h44z/wg-portal/internal/app"
"github.com/h44z/wg-portal/internal/app/api/core/request"
"github.com/h44z/wg-portal/internal/app/api/core/respond"
"github.com/h44z/wg-portal/internal/app/api/v0/model"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
)
type InterfaceEndpoint struct {
app *app.App
authenticator Authenticator
validator Validator
type InterfaceService interface {
// GetInterfaceAndPeers returns the interface with the given id and all peers associated with it.
GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, []domain.Peer, error)
// PrepareInterface returns a new interface with default values.
PrepareInterface(ctx context.Context) (*domain.Interface, error)
// CreateInterface creates a new interface.
CreateInterface(ctx context.Context, in *domain.Interface) (*domain.Interface, error)
// UpdateInterface updates the interface with the given id.
UpdateInterface(ctx context.Context, in *domain.Interface) (*domain.Interface, []domain.Peer, error)
// DeleteInterface deletes the interface with the given id.
DeleteInterface(ctx context.Context, id domain.InterfaceIdentifier) error
// GetAllInterfacesAndPeers returns all interfaces and all peers associated with them.
GetAllInterfacesAndPeers(ctx context.Context) ([]domain.Interface, [][]domain.Peer, error)
// GetInterfaceConfig returns the interface configuration as string.
GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error)
// PersistInterfaceConfig persists the interface configuration to a file.
PersistInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) error
// ApplyPeerDefaults applies the peer defaults to all peers of the given interface.
ApplyPeerDefaults(ctx context.Context, in *domain.Interface) error
}
func NewInterfaceEndpoint(app *app.App, authenticator Authenticator, validator Validator) InterfaceEndpoint {
type InterfaceEndpoint struct {
cfg *config.Config
interfaceService InterfaceService
authenticator Authenticator
validator Validator
}
func NewInterfaceEndpoint(
cfg *config.Config,
authenticator Authenticator,
validator Validator,
interfaceService InterfaceService,
) InterfaceEndpoint {
return InterfaceEndpoint{
app: app,
authenticator: authenticator,
validator: validator,
cfg: cfg,
interfaceService: interfaceService,
authenticator: authenticator,
validator: validator,
}
}
@ -59,7 +88,7 @@ func (e InterfaceEndpoint) RegisterRoutes(g *routegroup.Bundle) {
// @Router /interface/prepare [get]
func (e InterfaceEndpoint) handlePrepareGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
in, err := e.app.PrepareInterface(r.Context())
in, err := e.interfaceService.PrepareInterface(r.Context())
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -82,7 +111,7 @@ func (e InterfaceEndpoint) handlePrepareGet() http.HandlerFunc {
// @Router /interface/all [get]
func (e InterfaceEndpoint) handleAllGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
interfaces, peers, err := e.app.GetAllInterfacesAndPeers(r.Context())
interfaces, peers, err := e.interfaceService.GetAllInterfacesAndPeers(r.Context())
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -114,7 +143,7 @@ func (e InterfaceEndpoint) handleSingleGet() http.HandlerFunc {
return
}
iface, peers, err := e.app.GetInterfaceAndPeers(r.Context(), domain.InterfaceIdentifier(id))
iface, peers, err := e.interfaceService.GetInterfaceAndPeers(r.Context(), domain.InterfaceIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -146,7 +175,7 @@ func (e InterfaceEndpoint) handleConfigGet() http.HandlerFunc {
return
}
config, err := e.app.GetInterfaceConfig(r.Context(), domain.InterfaceIdentifier(id))
config, err := e.interfaceService.GetInterfaceConfig(r.Context(), domain.InterfaceIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -203,7 +232,7 @@ func (e InterfaceEndpoint) handleUpdatePut() http.HandlerFunc {
return
}
updatedInterface, peers, err := e.app.UpdateInterface(r.Context(), model.NewDomainInterface(&in))
updatedInterface, peers, err := e.interfaceService.UpdateInterface(r.Context(), model.NewDomainInterface(&in))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -238,7 +267,7 @@ func (e InterfaceEndpoint) handleCreatePost() http.HandlerFunc {
return
}
newInterface, err := e.app.CreateInterface(r.Context(), model.NewDomainInterface(&in))
newInterface, err := e.interfaceService.CreateInterface(r.Context(), model.NewDomainInterface(&in))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -269,7 +298,7 @@ func (e InterfaceEndpoint) handlePeersGet() http.HandlerFunc {
return
}
_, peers, err := e.app.GetInterfaceAndPeers(r.Context(), domain.InterfaceIdentifier(id))
_, peers, err := e.interfaceService.GetInterfaceAndPeers(r.Context(), domain.InterfaceIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -301,7 +330,7 @@ func (e InterfaceEndpoint) handleDelete() http.HandlerFunc {
return
}
err := e.app.DeleteInterface(r.Context(), domain.InterfaceIdentifier(id))
err := e.interfaceService.DeleteInterface(r.Context(), domain.InterfaceIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -333,7 +362,7 @@ func (e InterfaceEndpoint) handleSaveConfigPost() http.HandlerFunc {
return
}
err := e.app.PersistInterfaceConfig(r.Context(), domain.InterfaceIdentifier(id))
err := e.interfaceService.PersistInterfaceConfig(r.Context(), domain.InterfaceIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -382,7 +411,7 @@ func (e InterfaceEndpoint) handleApplyPeerDefaultsPost() http.HandlerFunc {
return
}
if err := e.app.ApplyPeerDefaults(r.Context(), model.NewDomainInterface(&in)); err != nil {
if err := e.interfaceService.ApplyPeerDefaults(r.Context(), model.NewDomainInterface(&in)); err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
})

View File

@ -1,27 +1,64 @@
package handlers
import (
"context"
"io"
"net/http"
"github.com/go-pkgz/routegroup"
"github.com/h44z/wg-portal/internal/app"
"github.com/h44z/wg-portal/internal/app/api/core/request"
"github.com/h44z/wg-portal/internal/app/api/core/respond"
"github.com/h44z/wg-portal/internal/app/api/v0/model"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
)
type PeerService interface {
// GetInterfaceAndPeers returns the interface with the given id and all peers associated with it.
GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, []domain.Peer, error)
// PreparePeer returns a new peer with default values for the given interface.
PreparePeer(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Peer, error)
// GetPeer returns the peer with the given id.
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
// CreatePeer creates a new peer.
CreatePeer(ctx context.Context, peer *domain.Peer) (*domain.Peer, error)
// CreateMultiplePeers creates multiple new peers.
CreateMultiplePeers(
ctx context.Context,
interfaceId domain.InterfaceIdentifier,
r *domain.PeerCreationRequest,
) ([]domain.Peer, error)
// UpdatePeer updates the peer with the given id.
UpdatePeer(ctx context.Context, peer *domain.Peer) (*domain.Peer, error)
// DeletePeer deletes the peer with the given id.
DeletePeer(ctx context.Context, id domain.PeerIdentifier) error
// GetPeerConfig returns the peer configuration for the given id.
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
// GetPeerConfigQrCode returns the peer configuration as qr code for the given id.
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
// SendPeerEmail sends the peer configuration via email.
SendPeerEmail(ctx context.Context, linkOnly bool, peers ...domain.PeerIdentifier) error
// GetPeerStats returns the peer stats for the given interface.
GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error)
}
type PeerEndpoint struct {
app *app.App
cfg *config.Config
peerService PeerService
authenticator Authenticator
validator Validator
}
func NewPeerEndpoint(app *app.App, authenticator Authenticator, validator Validator) PeerEndpoint {
func NewPeerEndpoint(
cfg *config.Config,
authenticator Authenticator,
validator Validator,
peerService PeerService,
) PeerEndpoint {
return PeerEndpoint{
app: app,
cfg: cfg,
peerService: peerService,
authenticator: authenticator,
validator: validator,
}
@ -69,7 +106,7 @@ func (e PeerEndpoint) handleAllGet() http.HandlerFunc {
return
}
_, peers, err := e.app.GetInterfaceAndPeers(r.Context(), domain.InterfaceIdentifier(interfaceId))
_, peers, err := e.peerService.GetInterfaceAndPeers(r.Context(), domain.InterfaceIdentifier(interfaceId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -100,7 +137,7 @@ func (e PeerEndpoint) handleSingleGet() http.HandlerFunc {
return
}
peer, err := e.app.GetPeer(r.Context(), domain.PeerIdentifier(peerId))
peer, err := e.peerService.GetPeer(r.Context(), domain.PeerIdentifier(peerId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -131,7 +168,7 @@ func (e PeerEndpoint) handlePrepareGet() http.HandlerFunc {
return
}
peer, err := e.app.PreparePeer(r.Context(), domain.InterfaceIdentifier(interfaceId))
peer, err := e.peerService.PreparePeer(r.Context(), domain.InterfaceIdentifier(interfaceId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -179,7 +216,7 @@ func (e PeerEndpoint) handleCreatePost() http.HandlerFunc {
return
}
newPeer, err := e.app.CreatePeer(r.Context(), model.NewDomainPeer(&p))
newPeer, err := e.peerService.CreatePeer(r.Context(), model.NewDomainPeer(&p))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -221,7 +258,7 @@ func (e PeerEndpoint) handleCreateMultiplePost() http.HandlerFunc {
return
}
newPeers, err := e.app.CreateMultiplePeers(r.Context(), domain.InterfaceIdentifier(interfaceId),
newPeers, err := e.peerService.CreateMultiplePeers(r.Context(), domain.InterfaceIdentifier(interfaceId),
model.NewDomainPeerCreationRequest(&req))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
@ -270,7 +307,7 @@ func (e PeerEndpoint) handleUpdatePut() http.HandlerFunc {
return
}
updatedPeer, err := e.app.UpdatePeer(r.Context(), model.NewDomainPeer(&p))
updatedPeer, err := e.peerService.UpdatePeer(r.Context(), model.NewDomainPeer(&p))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -300,7 +337,7 @@ func (e PeerEndpoint) handleDelete() http.HandlerFunc {
return
}
err := e.app.DeletePeer(r.Context(), domain.PeerIdentifier(id))
err := e.peerService.DeletePeer(r.Context(), domain.PeerIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -332,7 +369,7 @@ func (e PeerEndpoint) handleConfigGet() http.HandlerFunc {
return
}
config, err := e.app.GetPeerConfig(r.Context(), domain.PeerIdentifier(id))
configTxt, err := e.peerService.GetPeerConfig(r.Context(), domain.PeerIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -340,7 +377,7 @@ func (e PeerEndpoint) handleConfigGet() http.HandlerFunc {
return
}
configString, err := io.ReadAll(config)
configTxtString, err := io.ReadAll(configTxt)
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -348,7 +385,7 @@ func (e PeerEndpoint) handleConfigGet() http.HandlerFunc {
return
}
respond.JSON(w, http.StatusOK, string(configString))
respond.JSON(w, http.StatusOK, string(configTxtString))
}
}
@ -374,7 +411,7 @@ func (e PeerEndpoint) handleQrCodeGet() http.HandlerFunc {
return
}
config, err := e.app.GetPeerConfigQrCode(r.Context(), domain.PeerIdentifier(id))
configQr, err := e.peerService.GetPeerConfigQrCode(r.Context(), domain.PeerIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -382,7 +419,7 @@ func (e PeerEndpoint) handleQrCodeGet() http.HandlerFunc {
return
}
configData, err := io.ReadAll(config)
configQrData, err := io.ReadAll(configQr)
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
@ -390,7 +427,7 @@ func (e PeerEndpoint) handleQrCodeGet() http.HandlerFunc {
return
}
respond.Data(w, http.StatusOK, "image/png", configData)
respond.Data(w, http.StatusOK, "image/png", configQrData)
}
}
@ -427,7 +464,7 @@ func (e PeerEndpoint) handleEmailPost() http.HandlerFunc {
for i := range req.Identifiers {
peerIds[i] = domain.PeerIdentifier(req.Identifiers[i])
}
if err := e.app.SendPeerEmail(r.Context(), req.LinkOnly, peerIds...); err != nil {
if err := e.peerService.SendPeerEmail(r.Context(), req.LinkOnly, peerIds...); err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
@ -457,13 +494,13 @@ func (e PeerEndpoint) handleStatsGet() http.HandlerFunc {
return
}
stats, err := e.app.GetPeerStats(r.Context(), domain.InterfaceIdentifier(interfaceId))
stats, err := e.peerService.GetPeerStats(r.Context(), domain.InterfaceIdentifier(interfaceId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.JSON(w, http.StatusOK, model.NewPeerStats(e.app.Config.Statistics.CollectPeerData, stats))
respond.JSON(w, http.StatusOK, model.NewPeerStats(e.cfg.Statistics.CollectPeerData, stats))
}
}

View File

@ -1,26 +1,57 @@
package handlers
import (
"context"
"net/http"
"github.com/go-pkgz/routegroup"
"github.com/h44z/wg-portal/internal/app"
"github.com/h44z/wg-portal/internal/app/api/core/request"
"github.com/h44z/wg-portal/internal/app/api/core/respond"
"github.com/h44z/wg-portal/internal/app/api/v0/model"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
)
type UserService interface {
// GetUser returns the user with the given id.
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
// GetAllUsers returns all users.
GetAllUsers(ctx context.Context) ([]domain.User, error)
// UpdateUser updates the user with the given id.
UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error)
// CreateUser creates a new user.
CreateUser(ctx context.Context, user *domain.User) (*domain.User, error)
// DeleteUser deletes the user with the given id.
DeleteUser(ctx context.Context, id domain.UserIdentifier) error
// ActivateApi enables the API for the user with the given id.
ActivateApi(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
// DeactivateApi disables the API for the user with the given id.
DeactivateApi(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
// GetUserPeers returns all peers for the given user.
GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]domain.Peer, error)
// GetUserPeerStats returns all peer stats for the given user.
GetUserPeerStats(ctx context.Context, id domain.UserIdentifier) ([]domain.PeerStatus, error)
// GetUserInterfaces returns all interfaces for the given user.
GetUserInterfaces(ctx context.Context, id domain.UserIdentifier) ([]domain.Interface, error)
}
type UserEndpoint struct {
app *app.App
cfg *config.Config
userService UserService
authenticator Authenticator
validator Validator
}
func NewUserEndpoint(app *app.App, authenticator Authenticator, validator Validator) UserEndpoint {
func NewUserEndpoint(
cfg *config.Config,
authenticator Authenticator,
validator Validator,
userService UserService,
) UserEndpoint {
return UserEndpoint{
app: app,
cfg: cfg,
userService: userService,
authenticator: authenticator,
validator: validator,
}
@ -57,7 +88,7 @@ func (e UserEndpoint) RegisterRoutes(g *routegroup.Bundle) {
// @Router /user/all [get]
func (e UserEndpoint) handleAllGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
users, err := e.app.GetAllUsers(r.Context())
users, err := e.userService.GetAllUsers(r.Context())
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -86,7 +117,7 @@ func (e UserEndpoint) handleSingleGet() http.HandlerFunc {
return
}
user, err := e.app.GetUser(r.Context(), domain.UserIdentifier(id))
user, err := e.userService.GetUser(r.Context(), domain.UserIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -133,7 +164,7 @@ func (e UserEndpoint) handleUpdatePut() http.HandlerFunc {
return
}
updateUser, err := e.app.UpdateUser(r.Context(), model.NewDomainUser(&user))
updateUser, err := e.userService.UpdateUser(r.Context(), model.NewDomainUser(&user))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -167,7 +198,7 @@ func (e UserEndpoint) handleCreatePost() http.HandlerFunc {
return
}
newUser, err := e.app.CreateUser(r.Context(), model.NewDomainUser(&user))
newUser, err := e.userService.CreateUser(r.Context(), model.NewDomainUser(&user))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -198,7 +229,7 @@ func (e UserEndpoint) handlePeersGet() http.HandlerFunc {
return
}
peers, err := e.app.GetUserPeers(r.Context(), domain.UserIdentifier(userId))
peers, err := e.userService.GetUserPeers(r.Context(), domain.UserIdentifier(userId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -229,14 +260,14 @@ func (e UserEndpoint) handleStatsGet() http.HandlerFunc {
return
}
stats, err := e.app.GetUserPeerStats(r.Context(), domain.UserIdentifier(userId))
stats, err := e.userService.GetUserPeerStats(r.Context(), domain.UserIdentifier(userId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.JSON(w, http.StatusOK, model.NewPeerStats(e.app.Config.Statistics.CollectPeerData, stats))
respond.JSON(w, http.StatusOK, model.NewPeerStats(e.cfg.Statistics.CollectPeerData, stats))
}
}
@ -260,7 +291,7 @@ func (e UserEndpoint) handleInterfacesGet() http.HandlerFunc {
return
}
peers, err := e.app.GetUserInterfaces(r.Context(), domain.UserIdentifier(userId))
peers, err := e.userService.GetUserInterfaces(r.Context(), domain.UserIdentifier(userId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -290,7 +321,7 @@ func (e UserEndpoint) handleDelete() http.HandlerFunc {
return
}
err := e.app.DeleteUser(r.Context(), domain.UserIdentifier(id))
err := e.userService.DeleteUser(r.Context(), domain.UserIdentifier(id))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -320,7 +351,7 @@ func (e UserEndpoint) handleApiEnablePost() http.HandlerFunc {
return
}
user, err := e.app.ActivateApi(r.Context(), domain.UserIdentifier(userId))
user, err := e.userService.ActivateApi(r.Context(), domain.UserIdentifier(userId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
@ -350,7 +381,7 @@ func (e UserEndpoint) handleApiDisablePost() http.HandlerFunc {
return
}
user, err := e.app.DeactivateApi(r.Context(), domain.UserIdentifier(userId))
user, err := e.userService.DeactivateApi(r.Context(), domain.UserIdentifier(userId))
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})

View File

@ -79,11 +79,16 @@ func ParseServiceError(err error) (int, models.Error) {
}
}
// 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
}
type Validator interface {
// Struct validates the given struct.
Struct(s interface{}) error
}
// endregion handler-interfaces