mirror of
https://github.com/h44z/wg-portal.git
synced 2025-09-15 07:11:15 +00:00
chore: use interfaces for all other services
This commit is contained in:
@@ -1,87 +0,0 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type InterfaceAndPeerDatabaseRepo interface {
|
||||
GetInterface(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, error)
|
||||
GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, []domain.Peer, error)
|
||||
GetPeersStats(ctx context.Context, ids ...domain.PeerIdentifier) ([]domain.PeerStatus, error)
|
||||
GetAllInterfaces(ctx context.Context) ([]domain.Interface, error)
|
||||
FindInterfaces(ctx context.Context, search string) ([]domain.Interface, error)
|
||||
GetInterfaceIps(ctx context.Context) (map[domain.InterfaceIdentifier][]domain.Cidr, error)
|
||||
SaveInterface(
|
||||
ctx context.Context,
|
||||
id domain.InterfaceIdentifier,
|
||||
updateFunc func(in *domain.Interface) (*domain.Interface, error),
|
||||
) error
|
||||
DeleteInterface(ctx context.Context, id domain.InterfaceIdentifier) error
|
||||
GetInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error)
|
||||
FindInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier, search string) ([]domain.Peer, error)
|
||||
GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]domain.Peer, error)
|
||||
FindUserPeers(ctx context.Context, id domain.UserIdentifier, search string) ([]domain.Peer, error)
|
||||
SavePeer(
|
||||
ctx context.Context,
|
||||
id domain.PeerIdentifier,
|
||||
updateFunc func(in *domain.Peer) (*domain.Peer, error),
|
||||
) error
|
||||
DeletePeer(ctx context.Context, id domain.PeerIdentifier) error
|
||||
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
|
||||
GetUsedIpsPerSubnet(ctx context.Context, subnets []domain.Cidr) (map[domain.Cidr][]domain.Cidr, error)
|
||||
}
|
||||
|
||||
type StatisticsDatabaseRepo interface {
|
||||
GetAllInterfaces(ctx context.Context) ([]domain.Interface, error)
|
||||
GetInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error)
|
||||
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
|
||||
|
||||
UpdatePeerStatus(
|
||||
ctx context.Context,
|
||||
id domain.PeerIdentifier,
|
||||
updateFunc func(in *domain.PeerStatus) (*domain.PeerStatus, error),
|
||||
) error
|
||||
UpdateInterfaceStatus(
|
||||
ctx context.Context,
|
||||
id domain.InterfaceIdentifier,
|
||||
updateFunc func(in *domain.InterfaceStatus) (*domain.InterfaceStatus, error),
|
||||
) error
|
||||
|
||||
DeletePeerStatus(ctx context.Context, id domain.PeerIdentifier) error
|
||||
}
|
||||
|
||||
type InterfaceController interface {
|
||||
GetInterfaces(_ context.Context) ([]domain.PhysicalInterface, error)
|
||||
GetInterface(_ context.Context, id domain.InterfaceIdentifier) (*domain.PhysicalInterface, error)
|
||||
GetPeers(_ context.Context, deviceId domain.InterfaceIdentifier) ([]domain.PhysicalPeer, error)
|
||||
GetPeer(_ context.Context, deviceId domain.InterfaceIdentifier, id domain.PeerIdentifier) (
|
||||
*domain.PhysicalPeer,
|
||||
error,
|
||||
)
|
||||
SaveInterface(
|
||||
_ context.Context,
|
||||
id domain.InterfaceIdentifier,
|
||||
updateFunc func(pi *domain.PhysicalInterface) (*domain.PhysicalInterface, error),
|
||||
) error
|
||||
DeleteInterface(_ context.Context, id domain.InterfaceIdentifier) error
|
||||
SavePeer(
|
||||
_ context.Context,
|
||||
deviceId domain.InterfaceIdentifier,
|
||||
id domain.PeerIdentifier,
|
||||
updateFunc func(pp *domain.PhysicalPeer) (*domain.PhysicalPeer, error),
|
||||
) error
|
||||
DeletePeer(_ context.Context, deviceId domain.InterfaceIdentifier, id domain.PeerIdentifier) error
|
||||
}
|
||||
|
||||
type WgQuickController interface {
|
||||
ExecuteInterfaceHook(id domain.InterfaceIdentifier, hookCmd string) error
|
||||
SetDNS(id domain.InterfaceIdentifier, dnsStr, dnsSearchStr string) error
|
||||
UnsetDNS(id domain.InterfaceIdentifier) error
|
||||
}
|
||||
|
||||
type MetricsServer interface {
|
||||
UpdateInterfaceMetrics(status domain.InterfaceStatus)
|
||||
UpdatePeerMetrics(peer *domain.Peer, status domain.PeerStatus)
|
||||
}
|
@@ -7,31 +7,63 @@ import (
|
||||
"time"
|
||||
|
||||
probing "github.com/prometheus-community/pro-bing"
|
||||
evbus "github.com/vardius/message-bus"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app"
|
||||
"github.com/h44z/wg-portal/internal/config"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type StatisticsDatabaseRepo interface {
|
||||
GetAllInterfaces(ctx context.Context) ([]domain.Interface, error)
|
||||
GetInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error)
|
||||
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
|
||||
UpdatePeerStatus(
|
||||
ctx context.Context,
|
||||
id domain.PeerIdentifier,
|
||||
updateFunc func(in *domain.PeerStatus) (*domain.PeerStatus, error),
|
||||
) error
|
||||
UpdateInterfaceStatus(
|
||||
ctx context.Context,
|
||||
id domain.InterfaceIdentifier,
|
||||
updateFunc func(in *domain.InterfaceStatus) (*domain.InterfaceStatus, error),
|
||||
) error
|
||||
DeletePeerStatus(ctx context.Context, id domain.PeerIdentifier) error
|
||||
}
|
||||
|
||||
type StatisticsInterfaceController interface {
|
||||
GetInterface(_ context.Context, id domain.InterfaceIdentifier) (*domain.PhysicalInterface, error)
|
||||
GetPeers(_ context.Context, deviceId domain.InterfaceIdentifier) ([]domain.PhysicalPeer, error)
|
||||
}
|
||||
|
||||
type StatisticsMetricsServer interface {
|
||||
UpdateInterfaceMetrics(status domain.InterfaceStatus)
|
||||
UpdatePeerMetrics(peer *domain.Peer, status domain.PeerStatus)
|
||||
}
|
||||
|
||||
type StatisticsEventBus interface {
|
||||
// Subscribe subscribes to a topic
|
||||
Subscribe(topic string, fn interface{}) error
|
||||
}
|
||||
|
||||
type StatisticsCollector struct {
|
||||
cfg *config.Config
|
||||
bus evbus.MessageBus
|
||||
bus StatisticsEventBus
|
||||
|
||||
pingWaitGroup sync.WaitGroup
|
||||
pingJobs chan domain.Peer
|
||||
|
||||
db StatisticsDatabaseRepo
|
||||
wg InterfaceController
|
||||
ms MetricsServer
|
||||
wg StatisticsInterfaceController
|
||||
ms StatisticsMetricsServer
|
||||
}
|
||||
|
||||
// NewStatisticsCollector creates a new statistics collector.
|
||||
func NewStatisticsCollector(
|
||||
cfg *config.Config,
|
||||
bus evbus.MessageBus,
|
||||
bus StatisticsEventBus,
|
||||
db StatisticsDatabaseRepo,
|
||||
wg InterfaceController,
|
||||
ms MetricsServer,
|
||||
wg StatisticsInterfaceController,
|
||||
ms StatisticsMetricsServer,
|
||||
) (*StatisticsCollector, error) {
|
||||
c := &StatisticsCollector{
|
||||
cfg: cfg,
|
||||
@@ -47,6 +79,8 @@ func NewStatisticsCollector(
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// StartBackgroundJobs starts the background jobs for the statistics collector.
|
||||
// This method is non-blocking and returns immediately.
|
||||
func (c *StatisticsCollector) StartBackgroundJobs(ctx context.Context) {
|
||||
c.startPingWorkers(ctx)
|
||||
c.startInterfaceDataFetcher(ctx)
|
||||
|
@@ -5,17 +5,74 @@ import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
evbus "github.com/vardius/message-bus"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app"
|
||||
"github.com/h44z/wg-portal/internal/config"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
cfg *config.Config
|
||||
bus evbus.MessageBus
|
||||
// region dependencies
|
||||
|
||||
type InterfaceAndPeerDatabaseRepo interface {
|
||||
GetInterface(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, error)
|
||||
GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, []domain.Peer, error)
|
||||
GetPeersStats(ctx context.Context, ids ...domain.PeerIdentifier) ([]domain.PeerStatus, error)
|
||||
GetAllInterfaces(ctx context.Context) ([]domain.Interface, error)
|
||||
GetInterfaceIps(ctx context.Context) (map[domain.InterfaceIdentifier][]domain.Cidr, error)
|
||||
SaveInterface(
|
||||
ctx context.Context,
|
||||
id domain.InterfaceIdentifier,
|
||||
updateFunc func(in *domain.Interface) (*domain.Interface, error),
|
||||
) error
|
||||
DeleteInterface(ctx context.Context, id domain.InterfaceIdentifier) error
|
||||
GetInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error)
|
||||
GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]domain.Peer, error)
|
||||
SavePeer(
|
||||
ctx context.Context,
|
||||
id domain.PeerIdentifier,
|
||||
updateFunc func(in *domain.Peer) (*domain.Peer, error),
|
||||
) error
|
||||
DeletePeer(ctx context.Context, id domain.PeerIdentifier) error
|
||||
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
|
||||
GetUsedIpsPerSubnet(ctx context.Context, subnets []domain.Cidr) (map[domain.Cidr][]domain.Cidr, error)
|
||||
}
|
||||
|
||||
type InterfaceController interface {
|
||||
GetInterfaces(_ context.Context) ([]domain.PhysicalInterface, error)
|
||||
GetInterface(_ context.Context, id domain.InterfaceIdentifier) (*domain.PhysicalInterface, error)
|
||||
GetPeers(_ context.Context, deviceId domain.InterfaceIdentifier) ([]domain.PhysicalPeer, error)
|
||||
SaveInterface(
|
||||
_ context.Context,
|
||||
id domain.InterfaceIdentifier,
|
||||
updateFunc func(pi *domain.PhysicalInterface) (*domain.PhysicalInterface, error),
|
||||
) error
|
||||
DeleteInterface(_ context.Context, id domain.InterfaceIdentifier) error
|
||||
SavePeer(
|
||||
_ context.Context,
|
||||
deviceId domain.InterfaceIdentifier,
|
||||
id domain.PeerIdentifier,
|
||||
updateFunc func(pp *domain.PhysicalPeer) (*domain.PhysicalPeer, error),
|
||||
) error
|
||||
DeletePeer(_ context.Context, deviceId domain.InterfaceIdentifier, id domain.PeerIdentifier) error
|
||||
}
|
||||
|
||||
type WgQuickController interface {
|
||||
ExecuteInterfaceHook(id domain.InterfaceIdentifier, hookCmd string) error
|
||||
SetDNS(id domain.InterfaceIdentifier, dnsStr, dnsSearchStr string) error
|
||||
UnsetDNS(id domain.InterfaceIdentifier) error
|
||||
}
|
||||
|
||||
type EventBus interface {
|
||||
// Publish sends a message to the message bus.
|
||||
Publish(topic string, args ...any)
|
||||
// Subscribe subscribes to a topic
|
||||
Subscribe(topic string, fn interface{}) error
|
||||
}
|
||||
|
||||
// endregion dependencies
|
||||
|
||||
type Manager struct {
|
||||
cfg *config.Config
|
||||
bus EventBus
|
||||
db InterfaceAndPeerDatabaseRepo
|
||||
wg InterfaceController
|
||||
quick WgQuickController
|
||||
@@ -23,7 +80,7 @@ type Manager struct {
|
||||
|
||||
func NewWireGuardManager(
|
||||
cfg *config.Config,
|
||||
bus evbus.MessageBus,
|
||||
bus EventBus,
|
||||
wg InterfaceController,
|
||||
quick WgQuickController,
|
||||
db InterfaceAndPeerDatabaseRepo,
|
||||
@@ -41,6 +98,8 @@ func NewWireGuardManager(
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// StartBackgroundJobs starts background jobs like the expired peers check.
|
||||
// This method is non-blocking.
|
||||
func (m Manager) StartBackgroundJobs(ctx context.Context) {
|
||||
go m.runExpiredPeersCheck(ctx)
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
// GetImportableInterfaces returns all physical interfaces that are available on the system.
|
||||
// This function also returns interfaces that are already available in the database.
|
||||
func (m Manager) GetImportableInterfaces(ctx context.Context) ([]domain.PhysicalInterface, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -26,6 +28,7 @@ func (m Manager) GetImportableInterfaces(ctx context.Context) ([]domain.Physical
|
||||
return physicalInterfaces, nil
|
||||
}
|
||||
|
||||
// GetInterfaceAndPeers returns the interface and all peers for the given interface identifier.
|
||||
func (m Manager) GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceIdentifier) (
|
||||
*domain.Interface,
|
||||
[]domain.Peer,
|
||||
@@ -38,6 +41,7 @@ func (m Manager) GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceId
|
||||
return m.db.GetInterfaceAndPeers(ctx, id)
|
||||
}
|
||||
|
||||
// GetAllInterfaces returns all interfaces that are available in the database.
|
||||
func (m Manager) GetAllInterfaces(ctx context.Context) ([]domain.Interface, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -46,6 +50,7 @@ func (m Manager) GetAllInterfaces(ctx context.Context) ([]domain.Interface, erro
|
||||
return m.db.GetAllInterfaces(ctx)
|
||||
}
|
||||
|
||||
// GetAllInterfacesAndPeers returns all interfaces and their peers.
|
||||
func (m Manager) GetAllInterfacesAndPeers(ctx context.Context) ([]domain.Interface, [][]domain.Peer, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -97,6 +102,7 @@ func (m Manager) GetUserInterfaces(ctx context.Context, _ domain.UserIdentifier)
|
||||
return userInterfaces, nil
|
||||
}
|
||||
|
||||
// ImportNewInterfaces imports all new physical interfaces that are available on the system.
|
||||
func (m Manager) ImportNewInterfaces(ctx context.Context, filter ...domain.InterfaceIdentifier) (int, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return 0, err
|
||||
@@ -148,6 +154,7 @@ func (m Manager) ImportNewInterfaces(ctx context.Context, filter ...domain.Inter
|
||||
return imported, nil
|
||||
}
|
||||
|
||||
// ApplyPeerDefaults applies the interface defaults to all peers of the given interface.
|
||||
func (m Manager) ApplyPeerDefaults(ctx context.Context, in *domain.Interface) error {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return err
|
||||
@@ -179,6 +186,8 @@ func (m Manager) ApplyPeerDefaults(ctx context.Context, in *domain.Interface) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreInterfaceState restores the state of all physical interfaces and their peers.
|
||||
// The final state of the interfaces and peers will be the same as stored in the database.
|
||||
func (m Manager) RestoreInterfaceState(
|
||||
ctx context.Context,
|
||||
updateDbOnError bool,
|
||||
@@ -296,6 +305,7 @@ func (m Manager) RestoreInterfaceState(
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrepareInterface generates a new interface with fresh keys, ip addresses and a listen port.
|
||||
func (m Manager) PrepareInterface(ctx context.Context) (*domain.Interface, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -376,6 +386,7 @@ func (m Manager) PrepareInterface(ctx context.Context) (*domain.Interface, error
|
||||
return freshInterface, nil
|
||||
}
|
||||
|
||||
// CreateInterface creates a new interface with the given configuration.
|
||||
func (m Manager) CreateInterface(ctx context.Context, in *domain.Interface) (*domain.Interface, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -401,6 +412,7 @@ func (m Manager) CreateInterface(ctx context.Context, in *domain.Interface) (*do
|
||||
return in, nil
|
||||
}
|
||||
|
||||
// UpdateInterface updates the given interface with the new configuration.
|
||||
func (m Manager) UpdateInterface(ctx context.Context, in *domain.Interface) (*domain.Interface, []domain.Peer, error) {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -423,6 +435,7 @@ func (m Manager) UpdateInterface(ctx context.Context, in *domain.Interface) (*do
|
||||
return in, existingPeers, nil
|
||||
}
|
||||
|
||||
// DeleteInterface deletes the given interface.
|
||||
func (m Manager) DeleteInterface(ctx context.Context, id domain.InterfaceIdentifier) error {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return err
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
// CreateDefaultPeer creates a default peer for the given user on all server interfaces.
|
||||
func (m Manager) CreateDefaultPeer(ctx context.Context, userId domain.UserIdentifier) error {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
return err
|
||||
@@ -55,6 +56,7 @@ func (m Manager) CreateDefaultPeer(ctx context.Context, userId domain.UserIdenti
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserPeers returns all peers for the given user.
|
||||
func (m Manager) GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]domain.Peer, error) {
|
||||
if err := domain.ValidateUserAccessRights(ctx, id); err != nil {
|
||||
return nil, err
|
||||
@@ -63,6 +65,7 @@ func (m Manager) GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]
|
||||
return m.db.GetUserPeers(ctx, id)
|
||||
}
|
||||
|
||||
// PreparePeer prepares a new peer for the given interface with fresh keys and ip addresses.
|
||||
func (m Manager) PreparePeer(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Peer, error) {
|
||||
if !m.cfg.Core.SelfProvisioningAllowed {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
@@ -143,6 +146,7 @@ func (m Manager) PreparePeer(ctx context.Context, id domain.InterfaceIdentifier)
|
||||
return freshPeer, nil
|
||||
}
|
||||
|
||||
// GetPeer returns the peer with the given identifier.
|
||||
func (m Manager) GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error) {
|
||||
peer, err := m.db.GetPeer(ctx, id)
|
||||
if err != nil {
|
||||
@@ -156,6 +160,7 @@ func (m Manager) GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
// CreatePeer creates a new peer.
|
||||
func (m Manager) CreatePeer(ctx context.Context, peer *domain.Peer) (*domain.Peer, error) {
|
||||
if !m.cfg.Core.SelfProvisioningAllowed {
|
||||
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||
@@ -201,6 +206,8 @@ func (m Manager) CreatePeer(ctx context.Context, peer *domain.Peer) (*domain.Pee
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
// CreateMultiplePeers creates multiple new peers for the given user identifiers.
|
||||
// It calls PreparePeer for each user identifier in the request.
|
||||
func (m Manager) CreateMultiplePeers(
|
||||
ctx context.Context,
|
||||
interfaceId domain.InterfaceIdentifier,
|
||||
@@ -243,6 +250,7 @@ func (m Manager) CreateMultiplePeers(
|
||||
return createdPeers, nil
|
||||
}
|
||||
|
||||
// UpdatePeer updates the given peer.
|
||||
func (m Manager) UpdatePeer(ctx context.Context, peer *domain.Peer) (*domain.Peer, error) {
|
||||
existingPeer, err := m.db.GetPeer(ctx, peer.Identifier)
|
||||
if err != nil {
|
||||
@@ -309,6 +317,7 @@ func (m Manager) UpdatePeer(ctx context.Context, peer *domain.Peer) (*domain.Pee
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
// DeletePeer deletes the peer with the given identifier.
|
||||
func (m Manager) DeletePeer(ctx context.Context, id domain.PeerIdentifier) error {
|
||||
peer, err := m.db.GetPeer(ctx, id)
|
||||
if err != nil {
|
||||
@@ -341,6 +350,7 @@ func (m Manager) DeletePeer(ctx context.Context, id domain.PeerIdentifier) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPeerStats returns the status of the peer with the given identifier.
|
||||
func (m Manager) GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error) {
|
||||
_, peers, err := m.db.GetInterfaceAndPeers(ctx, id)
|
||||
if err != nil {
|
||||
@@ -359,6 +369,7 @@ func (m Manager) GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier
|
||||
return m.db.GetPeersStats(ctx, peerIds...)
|
||||
}
|
||||
|
||||
// GetUserPeerStats returns the status of all peers for the given user.
|
||||
func (m Manager) GetUserPeerStats(ctx context.Context, id domain.UserIdentifier) ([]domain.PeerStatus, error) {
|
||||
if err := domain.ValidateUserAccessRights(ctx, id); err != nil {
|
||||
return nil, err
|
||||
|
Reference in New Issue
Block a user