mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-11 07:59:08 +02:00
move to reverse proxy and update api
This commit is contained in:
+1
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
"github.com/netbirdio/netbird/shared/management/http/util"
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services/domains"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domains"
|
||||
)
|
||||
|
||||
type resolver struct {
|
||||
@@ -0,0 +1,13 @@
|
||||
package reverseproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetAllReverseProxies(ctx context.Context, accountID, userID string) ([]*ReverseProxy, error)
|
||||
GetReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) (*ReverseProxy, error)
|
||||
CreateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *ReverseProxy) (*ReverseProxy, error)
|
||||
UpdateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *ReverseProxy) (*ReverseProxy, error)
|
||||
DeleteReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) error
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
"github.com/netbirdio/netbird/shared/management/http/util"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
manager reverseproxy.Manager
|
||||
}
|
||||
|
||||
func RegisterEndpoints(router *mux.Router, manager reverseproxy.Manager) {
|
||||
h := &handler{
|
||||
manager: manager,
|
||||
}
|
||||
|
||||
router.HandleFunc("/reverse-proxies", h.getAllReverseProxies).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/reverse-proxies", h.createReverseProxy).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/reverse-proxies/{reverseProxyId}", h.getReverseProxy).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/reverse-proxies/{reverseProxyId}", h.updateReverseProxy).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/reverse-proxies/{reverseProxyId}", h.deleteReverseProxy).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (h *handler) getAllReverseProxies(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
allReverseProxies, err := h.manager.GetAllReverseProxies(r.Context(), userAuth.AccountId, userAuth.UserId)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
apiReverseProxies := make([]*api.ReverseProxy, 0, len(allReverseProxies))
|
||||
for _, reverseProxy := range allReverseProxies {
|
||||
apiReverseProxies = append(apiReverseProxies, reverseProxy.ToAPIResponse())
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, apiReverseProxies)
|
||||
}
|
||||
|
||||
func (h *handler) createReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.PostApiReverseProxyJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := new(reverseproxy.ReverseProxy)
|
||||
reverseProxy.FromAPIRequest(&req)
|
||||
|
||||
if err = reverseProxy.Validate(); err != nil {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
|
||||
return
|
||||
}
|
||||
|
||||
createdReverseProxy, err := h.manager.CreateReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxy)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, createdReverseProxy.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *handler) getReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxyID := mux.Vars(r)["proxyId"]
|
||||
if reverseProxyID == "" {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy, err := h.manager.GetReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxyID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, reverseProxy.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *handler) updateReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxyID := mux.Vars(r)["proxyId"]
|
||||
if reverseProxyID == "" {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.PutApiReverseProxyProxyIdJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := new(reverseproxy.ReverseProxy)
|
||||
reverseProxy.ID = reverseProxyID
|
||||
reverseProxy.FromAPIRequest(&req)
|
||||
|
||||
if err = reverseProxy.Validate(); err != nil {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
|
||||
return
|
||||
}
|
||||
|
||||
updatedReverseProxy, err := h.manager.UpdateReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxy)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, updatedReverseProxy.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *handler) deleteReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxyID := mux.Vars(r)["proxyId"]
|
||||
if reverseProxyID == "" {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.manager.DeleteReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxyID); err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
)
|
||||
|
||||
type managerImpl struct {
|
||||
store store.Store
|
||||
accountManager account.Manager
|
||||
permissionsManager permissions.Manager
|
||||
}
|
||||
|
||||
func NewManager(store store.Store, accountManager account.Manager, permissionsManager permissions.Manager) reverseproxy.Manager {
|
||||
return &managerImpl{
|
||||
store: store,
|
||||
accountManager: accountManager,
|
||||
permissionsManager: permissionsManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllReverseProxies(ctx context.Context, accountID, userID string) ([]*reverseproxy.ReverseProxy, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return m.store.GetAccountReverseProxies(ctx, store.LockingStrengthNone, accountID)
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) (*reverseproxy.ReverseProxy, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return m.store.GetReverseProxyByID(ctx, store.LockingStrengthNone, accountID, reverseProxyID)
|
||||
}
|
||||
|
||||
func (m *managerImpl) CreateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *reverseproxy.ReverseProxy) (*reverseproxy.ReverseProxy, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Create)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
authConfig := reverseProxy.Auth
|
||||
|
||||
reverseProxy = reverseproxy.NewReverseProxy(accountID, reverseProxy.Name, reverseProxy.Domain, reverseProxy.Targets, reverseProxy.Enabled)
|
||||
|
||||
reverseProxy.Auth = authConfig
|
||||
|
||||
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||
// Check for duplicate domain
|
||||
existingReverseProxy, err := transaction.GetReverseProxyByDomain(ctx, accountID, reverseProxy.Domain)
|
||||
if err != nil {
|
||||
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
|
||||
return fmt.Errorf("failed to check existing reverse proxy: %w", err)
|
||||
}
|
||||
}
|
||||
if existingReverseProxy != nil {
|
||||
return status.Errorf(status.AlreadyExists, "reverse proxy with domain %s already exists", reverseProxy.Domain)
|
||||
}
|
||||
|
||||
if err = transaction.CreateReverseProxy(ctx, reverseProxy); err != nil {
|
||||
return fmt.Errorf("failed to create reverse proxy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.accountManager.StoreEvent(ctx, userID, reverseProxy.ID, accountID, activity.ReverseProxyCreated, reverseProxy.EventMeta())
|
||||
|
||||
return reverseProxy, nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) UpdateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *reverseproxy.ReverseProxy) (*reverseproxy.ReverseProxy, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Update)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||
// Get existing reverse proxy
|
||||
existingReverseProxy, err := transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxy.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if domain changed and if it conflicts
|
||||
if existingReverseProxy.Domain != reverseProxy.Domain {
|
||||
conflictReverseProxy, err := transaction.GetReverseProxyByDomain(ctx, accountID, reverseProxy.Domain)
|
||||
if err != nil {
|
||||
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
|
||||
return fmt.Errorf("failed to check existing reverse proxy: %w", err)
|
||||
}
|
||||
}
|
||||
if conflictReverseProxy != nil && conflictReverseProxy.ID != reverseProxy.ID {
|
||||
return status.Errorf(status.AlreadyExists, "reverse proxy with domain %s already exists", reverseProxy.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if err = transaction.UpdateReverseProxy(ctx, reverseProxy); err != nil {
|
||||
return fmt.Errorf("failed to update reverse proxy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.accountManager.StoreEvent(ctx, userID, reverseProxy.ID, accountID, activity.ReverseProxyUpdated, reverseProxy.EventMeta())
|
||||
|
||||
return reverseProxy, nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) DeleteReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) error {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Delete)
|
||||
if err != nil {
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var reverseProxy *reverseproxy.ReverseProxy
|
||||
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||
var err error
|
||||
reverseProxy, err = transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = transaction.DeleteReverseProxy(ctx, accountID, reverseProxyID); err != nil {
|
||||
return fmt.Errorf("failed to delete reverse proxy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.accountManager.StoreEvent(ctx, userID, reverseProxyID, accountID, activity.ReverseProxyDeleted, reverseProxy.EventMeta())
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package reverseproxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rs/xid"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
Path *string `json:"path,omitempty"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Protocol string `json:"protocol"`
|
||||
TargetId string `json:"target_id"`
|
||||
TargetType string `json:"target_type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type PasswordAuthConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type PINAuthConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Pin string `json:"pin"`
|
||||
}
|
||||
|
||||
type BearerAuthConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DistributionGroups []string `json:"distribution_groups,omitempty" gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
type LinkAuthConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
PasswordAuth *PasswordAuthConfig `json:"password_auth,omitempty" gorm:"serializer:json"`
|
||||
PinAuth *PINAuthConfig `json:"pin_auth,omitempty" gorm:"serializer:json"`
|
||||
BearerAuth *BearerAuthConfig `json:"bearer_auth,omitempty" gorm:"serializer:json"`
|
||||
LinkAuth *LinkAuthConfig `json:"link_auth,omitempty" gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
type ReverseProxy struct {
|
||||
ID string `gorm:"primaryKey"`
|
||||
AccountID string `gorm:"index"`
|
||||
Name string
|
||||
Domain string `gorm:"index"`
|
||||
Targets []Target `gorm:"serializer:json"`
|
||||
Enabled bool
|
||||
Auth AuthConfig `gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
func NewReverseProxy(accountID, name, domain string, targets []Target, enabled bool) *ReverseProxy {
|
||||
return &ReverseProxy{
|
||||
ID: xid.New().String(),
|
||||
AccountID: accountID,
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
Targets: targets,
|
||||
Enabled: enabled,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) ToAPIResponse() *api.ReverseProxy {
|
||||
authConfig := api.ReverseProxyAuthConfig{}
|
||||
|
||||
if r.Auth.PasswordAuth != nil {
|
||||
authConfig.PasswordAuth = &api.PasswordAuthConfig{
|
||||
Enabled: r.Auth.PasswordAuth.Enabled,
|
||||
Password: r.Auth.PasswordAuth.Password,
|
||||
}
|
||||
}
|
||||
|
||||
if r.Auth.PinAuth != nil {
|
||||
authConfig.PinAuth = &api.PINAuthConfig{
|
||||
Enabled: r.Auth.PinAuth.Enabled,
|
||||
Pin: r.Auth.PinAuth.Pin,
|
||||
}
|
||||
}
|
||||
|
||||
if r.Auth.BearerAuth != nil {
|
||||
authConfig.BearerAuth = &api.BearerAuthConfig{
|
||||
Enabled: r.Auth.BearerAuth.Enabled,
|
||||
DistributionGroups: &r.Auth.BearerAuth.DistributionGroups,
|
||||
}
|
||||
}
|
||||
|
||||
if r.Auth.LinkAuth != nil {
|
||||
authConfig.LinkAuth = &api.LinkAuthConfig{
|
||||
Enabled: r.Auth.LinkAuth.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert internal targets to API targets
|
||||
apiTargets := make([]api.ReverseProxyTarget, 0, len(r.Targets))
|
||||
for _, target := range r.Targets {
|
||||
apiTargets = append(apiTargets, api.ReverseProxyTarget{
|
||||
Path: target.Path,
|
||||
Host: target.Host,
|
||||
Port: target.Port,
|
||||
Protocol: api.ReverseProxyTargetProtocol(target.Protocol),
|
||||
TargetId: target.TargetId,
|
||||
TargetType: api.ReverseProxyTargetTargetType(target.TargetType),
|
||||
Enabled: target.Enabled,
|
||||
})
|
||||
}
|
||||
|
||||
return &api.ReverseProxy{
|
||||
Id: r.ID,
|
||||
Name: r.Name,
|
||||
Domain: r.Domain,
|
||||
Targets: apiTargets,
|
||||
Enabled: r.Enabled,
|
||||
Auth: authConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) FromAPIRequest(req *api.ReverseProxyRequest) {
|
||||
r.Name = req.Name
|
||||
r.Domain = req.Domain
|
||||
|
||||
// Convert API targets to internal targets
|
||||
targets := make([]Target, 0, len(req.Targets))
|
||||
for _, apiTarget := range req.Targets {
|
||||
targets = append(targets, Target{
|
||||
Path: apiTarget.Path,
|
||||
Host: apiTarget.Host,
|
||||
Port: apiTarget.Port,
|
||||
Protocol: string(apiTarget.Protocol),
|
||||
TargetId: apiTarget.TargetId,
|
||||
TargetType: string(apiTarget.TargetType),
|
||||
Enabled: apiTarget.Enabled,
|
||||
})
|
||||
}
|
||||
r.Targets = targets
|
||||
|
||||
r.Enabled = req.Enabled
|
||||
|
||||
if req.Auth.PasswordAuth != nil {
|
||||
r.Auth.PasswordAuth = &PasswordAuthConfig{
|
||||
Enabled: req.Auth.PasswordAuth.Enabled,
|
||||
Password: req.Auth.PasswordAuth.Password,
|
||||
}
|
||||
}
|
||||
|
||||
if req.Auth.PinAuth != nil {
|
||||
r.Auth.PinAuth = &PINAuthConfig{
|
||||
Enabled: req.Auth.PinAuth.Enabled,
|
||||
Pin: req.Auth.PinAuth.Pin,
|
||||
}
|
||||
}
|
||||
|
||||
if req.Auth.BearerAuth != nil {
|
||||
bearerAuth := &BearerAuthConfig{
|
||||
Enabled: req.Auth.BearerAuth.Enabled,
|
||||
}
|
||||
if req.Auth.BearerAuth.DistributionGroups != nil {
|
||||
bearerAuth.DistributionGroups = *req.Auth.BearerAuth.DistributionGroups
|
||||
}
|
||||
r.Auth.BearerAuth = bearerAuth
|
||||
}
|
||||
|
||||
if req.Auth.LinkAuth != nil {
|
||||
r.Auth.LinkAuth = &LinkAuthConfig{
|
||||
Enabled: req.Auth.LinkAuth.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) Validate() error {
|
||||
if r.Name == "" {
|
||||
return errors.New("reverse proxy name is required")
|
||||
}
|
||||
if len(r.Name) > 255 {
|
||||
return errors.New("reverse proxy name exceeds maximum length of 255 characters")
|
||||
}
|
||||
|
||||
if r.Domain == "" {
|
||||
return errors.New("reverse proxy domain is required")
|
||||
}
|
||||
|
||||
if len(r.Targets) == 0 {
|
||||
return errors.New("at least one target is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) EventMeta() map[string]any {
|
||||
return map[string]any{"name": r.Name, "domain": r.Domain}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetAllServices(ctx context.Context, accountID, userID string) ([]*Service, error)
|
||||
GetService(ctx context.Context, accountID, userID, serviceID string) (*Service, error)
|
||||
CreateService(ctx context.Context, accountID, userID string, service *Service) (*Service, error)
|
||||
UpdateService(ctx context.Context, accountID, userID string, service *Service) (*Service, error)
|
||||
DeleteService(ctx context.Context, accountID, userID, serviceID string) error
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
"github.com/netbirdio/netbird/shared/management/http/util"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
manager services.Manager
|
||||
}
|
||||
|
||||
func RegisterEndpoints(router *mux.Router, manager services.Manager) {
|
||||
h := &handler{
|
||||
manager: manager,
|
||||
}
|
||||
|
||||
router.HandleFunc("/services", h.getAllServices).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/services", h.createService).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/services/{serviceId}", h.getService).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/services/{serviceId}", h.updateService).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/services/{serviceId}", h.deleteService).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (h *handler) getAllServices(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
allServices, err := h.manager.GetAllServices(r.Context(), userAuth.AccountId, userAuth.UserId)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
apiServices := make([]*api.Service, 0, len(allServices))
|
||||
for _, service := range allServices {
|
||||
apiServices = append(apiServices, service.ToAPIResponse())
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, apiServices)
|
||||
}
|
||||
|
||||
func (h *handler) createService(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.PostApiServicesJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
service := new(services.Service)
|
||||
service.FromAPIRequest(&req)
|
||||
|
||||
if err = service.Validate(); err != nil {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
|
||||
return
|
||||
}
|
||||
|
||||
createdService, err := h.manager.CreateService(r.Context(), userAuth.AccountId, userAuth.UserId, service)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, createdService.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *handler) getService(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
serviceID := mux.Vars(r)["serviceId"]
|
||||
if serviceID == "" {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
|
||||
return
|
||||
}
|
||||
|
||||
service, err := h.manager.GetService(r.Context(), userAuth.AccountId, userAuth.UserId, serviceID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, service.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *handler) updateService(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
serviceID := mux.Vars(r)["serviceId"]
|
||||
if serviceID == "" {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.PutApiServicesServiceIdJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
service := new(services.Service)
|
||||
service.ID = serviceID
|
||||
service.FromAPIRequest(&req)
|
||||
|
||||
if err = service.Validate(); err != nil {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
|
||||
return
|
||||
}
|
||||
|
||||
updatedService, err := h.manager.UpdateService(r.Context(), userAuth.AccountId, userAuth.UserId, service)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, updatedService.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *handler) deleteService(w http.ResponseWriter, r *http.Request) {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
serviceID := mux.Vars(r)["serviceId"]
|
||||
if serviceID == "" {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.manager.DeleteService(r.Context(), userAuth.AccountId, userAuth.UserId, serviceID); err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
)
|
||||
|
||||
type managerImpl struct {
|
||||
store store.Store
|
||||
accountManager account.Manager
|
||||
permissionsManager permissions.Manager
|
||||
}
|
||||
|
||||
func NewManager(store store.Store, accountManager account.Manager, permissionsManager permissions.Manager) services.Manager {
|
||||
return &managerImpl{
|
||||
store: store,
|
||||
accountManager: accountManager,
|
||||
permissionsManager: permissionsManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllServices(ctx context.Context, accountID, userID string) ([]*services.Service, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return m.store.GetAccountServices(ctx, store.LockingStrengthNone, accountID)
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetService(ctx context.Context, accountID, userID, serviceID string) (*services.Service, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return m.store.GetServiceByID(ctx, store.LockingStrengthNone, accountID, serviceID)
|
||||
}
|
||||
|
||||
func (m *managerImpl) CreateService(ctx context.Context, accountID, userID string, service *services.Service) (*services.Service, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Create)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
// Store auth config before creating new service
|
||||
authType := service.AuthType
|
||||
authBasicUsername := service.AuthBasicUsername
|
||||
authBasicPassword := service.AuthBasicPassword
|
||||
authPINValue := service.AuthPINValue
|
||||
authPINHeader := service.AuthPINHeader
|
||||
authBearerEnabled := service.AuthBearerEnabled
|
||||
|
||||
service = services.NewService(accountID, service.Name, service.Description, service.Domain, service.Targets, service.DistributionGroups, service.Enabled, service.Exposed)
|
||||
|
||||
// Restore auth config
|
||||
service.AuthType = authType
|
||||
service.AuthBasicUsername = authBasicUsername
|
||||
service.AuthBasicPassword = authBasicPassword
|
||||
service.AuthPINValue = authPINValue
|
||||
service.AuthPINHeader = authPINHeader
|
||||
service.AuthBearerEnabled = authBearerEnabled
|
||||
|
||||
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||
// Check for duplicate domain
|
||||
existingService, err := transaction.GetServiceByDomain(ctx, accountID, service.Domain)
|
||||
if err != nil {
|
||||
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
|
||||
return fmt.Errorf("failed to check existing service: %w", err)
|
||||
}
|
||||
}
|
||||
if existingService != nil {
|
||||
return status.Errorf(status.AlreadyExists, "service with domain %s already exists", service.Domain)
|
||||
}
|
||||
|
||||
// Validate distribution groups exist
|
||||
for _, groupID := range service.DistributionGroups {
|
||||
_, err = transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, groupID)
|
||||
if err != nil {
|
||||
return status.Errorf(status.InvalidArgument, "%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err = transaction.CreateService(ctx, service); err != nil {
|
||||
return fmt.Errorf("failed to create service: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.accountManager.StoreEvent(ctx, userID, service.ID, accountID, activity.ServiceCreated, service.EventMeta())
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) UpdateService(ctx context.Context, accountID, userID string, service *services.Service) (*services.Service, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Update)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||
// Get existing service
|
||||
existingService, err := transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, service.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if domain changed and if it conflicts
|
||||
if existingService.Domain != service.Domain {
|
||||
conflictService, err := transaction.GetServiceByDomain(ctx, accountID, service.Domain)
|
||||
if err != nil {
|
||||
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
|
||||
return fmt.Errorf("failed to check existing service: %w", err)
|
||||
}
|
||||
}
|
||||
if conflictService != nil && conflictService.ID != service.ID {
|
||||
return status.Errorf(status.AlreadyExists, "service with domain %s already exists", service.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate distribution groups exist
|
||||
for _, groupID := range service.DistributionGroups {
|
||||
_, err = transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, groupID)
|
||||
if err != nil {
|
||||
return status.Errorf(status.InvalidArgument, "%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err = transaction.UpdateService(ctx, service); err != nil {
|
||||
return fmt.Errorf("failed to update service: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.accountManager.StoreEvent(ctx, userID, service.ID, accountID, activity.ServiceUpdated, service.EventMeta())
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) DeleteService(ctx context.Context, accountID, userID, serviceID string) error {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Delete)
|
||||
if err != nil {
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !ok {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var service *services.Service
|
||||
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||
var err error
|
||||
service, err = transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, serviceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = transaction.DeleteService(ctx, accountID, serviceID); err != nil {
|
||||
return fmt.Errorf("failed to delete service: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.accountManager.StoreEvent(ctx, userID, serviceID, accountID, activity.ServiceDeleted, service.EventMeta())
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rs/xid"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
Path string `json:"path"`
|
||||
Host string `json:"host"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
ID string `gorm:"primaryKey"`
|
||||
AccountID string `gorm:"index"`
|
||||
Name string
|
||||
Description string
|
||||
Domain string `gorm:"index"`
|
||||
Targets []Target `gorm:"serializer:json"`
|
||||
DistributionGroups []string `gorm:"serializer:json"`
|
||||
Enabled bool
|
||||
Exposed bool
|
||||
|
||||
// Authentication configuration
|
||||
AuthType string
|
||||
AuthBasicUsername string
|
||||
AuthBasicPassword string
|
||||
AuthPINValue string
|
||||
AuthPINHeader string
|
||||
AuthBearerEnabled bool
|
||||
}
|
||||
|
||||
func NewService(accountID, name, description, domain string, targets []Target, distributionGroups []string, enabled, exposed bool) *Service {
|
||||
return &Service{
|
||||
ID: xid.New().String(),
|
||||
AccountID: accountID,
|
||||
Name: name,
|
||||
Description: description,
|
||||
Domain: domain,
|
||||
Targets: targets,
|
||||
DistributionGroups: distributionGroups,
|
||||
Enabled: enabled,
|
||||
Exposed: exposed,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) ToAPIResponse() *api.Service {
|
||||
var authConfig *api.ServiceAuthConfig
|
||||
|
||||
switch s.AuthType {
|
||||
case "basic":
|
||||
authConfig = &api.ServiceAuthConfig{
|
||||
Type: "basic",
|
||||
BasicAuth: &api.BasicAuthConfig{
|
||||
Username: s.AuthBasicUsername,
|
||||
Password: s.AuthBasicPassword,
|
||||
},
|
||||
}
|
||||
case "pin":
|
||||
authConfig = &api.ServiceAuthConfig{
|
||||
Type: "pin",
|
||||
PinAuth: &api.PINAuthConfig{
|
||||
Pin: s.AuthPINValue,
|
||||
Header: s.AuthPINHeader,
|
||||
},
|
||||
}
|
||||
case "bearer":
|
||||
authConfig = &api.ServiceAuthConfig{
|
||||
Type: "bearer",
|
||||
BearerAuth: &api.BearerAuthConfig{
|
||||
Enabled: s.AuthBearerEnabled,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Convert internal targets to API targets
|
||||
apiTargets := make([]api.ServiceTarget, 0, len(s.Targets))
|
||||
for _, target := range s.Targets {
|
||||
apiTargets = append(apiTargets, api.ServiceTarget{
|
||||
Path: target.Path,
|
||||
Host: target.Host,
|
||||
Enabled: target.Enabled,
|
||||
})
|
||||
}
|
||||
|
||||
return &api.Service{
|
||||
Id: s.ID,
|
||||
Name: s.Name,
|
||||
Description: &s.Description,
|
||||
Domain: s.Domain,
|
||||
Targets: apiTargets,
|
||||
DistributionGroups: s.DistributionGroups,
|
||||
Enabled: s.Enabled,
|
||||
Exposed: s.Exposed,
|
||||
Auth: authConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) FromAPIRequest(req *api.ServiceRequest) {
|
||||
s.Name = req.Name
|
||||
s.Domain = req.Domain
|
||||
|
||||
// Convert API targets to internal targets
|
||||
targets := make([]Target, 0, len(req.Targets))
|
||||
for _, apiTarget := range req.Targets {
|
||||
targets = append(targets, Target{
|
||||
Path: apiTarget.Path,
|
||||
Host: apiTarget.Host,
|
||||
Enabled: apiTarget.Enabled,
|
||||
})
|
||||
}
|
||||
s.Targets = targets
|
||||
|
||||
s.DistributionGroups = req.DistributionGroups
|
||||
|
||||
if req.Description != nil {
|
||||
s.Description = *req.Description
|
||||
}
|
||||
|
||||
enabled := true
|
||||
if req.Enabled != nil {
|
||||
enabled = *req.Enabled
|
||||
}
|
||||
s.Enabled = enabled
|
||||
|
||||
exposed := false
|
||||
if req.Exposed != nil {
|
||||
exposed = *req.Exposed
|
||||
}
|
||||
s.Exposed = exposed
|
||||
|
||||
// Handle auth config
|
||||
if req.Auth != nil {
|
||||
s.AuthType = string(req.Auth.Type)
|
||||
|
||||
switch req.Auth.Type {
|
||||
case "basic":
|
||||
if req.Auth.BasicAuth != nil {
|
||||
s.AuthBasicUsername = req.Auth.BasicAuth.Username
|
||||
s.AuthBasicPassword = req.Auth.BasicAuth.Password
|
||||
}
|
||||
case "pin":
|
||||
if req.Auth.PinAuth != nil {
|
||||
s.AuthPINValue = req.Auth.PinAuth.Pin
|
||||
s.AuthPINHeader = req.Auth.PinAuth.Header
|
||||
}
|
||||
case "bearer":
|
||||
if req.Auth.BearerAuth != nil {
|
||||
s.AuthBearerEnabled = req.Auth.BearerAuth.Enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Validate() error {
|
||||
if s.Name == "" {
|
||||
return errors.New("service name is required")
|
||||
}
|
||||
if len(s.Name) > 255 {
|
||||
return errors.New("service name exceeds maximum length of 255 characters")
|
||||
}
|
||||
|
||||
if s.Domain == "" {
|
||||
return errors.New("service domain is required")
|
||||
}
|
||||
|
||||
if len(s.Targets) == 0 {
|
||||
return errors.New("at least one target is required")
|
||||
}
|
||||
|
||||
if len(s.DistributionGroups) == 0 {
|
||||
return errors.New("at least one distribution group is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) EventMeta() map[string]any {
|
||||
return map[string]any{"name": s.Name, "domain": s.Domain}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func (s *BaseServer) EventStore() activity.Store {
|
||||
|
||||
func (s *BaseServer) APIHandler() http.Handler {
|
||||
return Create(s, func() http.Handler {
|
||||
httpAPIHandler, err := nbhttp.NewAPIHandler(context.Background(), s.AccountManager(), s.NetworksManager(), s.ResourcesManager(), s.RoutesManager(), s.GroupsManager(), s.GeoLocationManager(), s.AuthManager(), s.Metrics(), s.IntegratedValidator(), s.ProxyController(), s.PermissionsManager(), s.PeersManager(), s.SettingsManager(), s.ZonesManager(), s.RecordsManager(), s.NetworkMapController(), s.IdpManager(), s.ServiceManager())
|
||||
httpAPIHandler, err := nbhttp.NewAPIHandler(context.Background(), s.AccountManager(), s.NetworksManager(), s.ResourcesManager(), s.RoutesManager(), s.GroupsManager(), s.GeoLocationManager(), s.AuthManager(), s.Metrics(), s.IntegratedValidator(), s.ProxyController(), s.PermissionsManager(), s.PeersManager(), s.SettingsManager(), s.ZonesManager(), s.RecordsManager(), s.NetworkMapController(), s.IdpManager(), s.ReverseProxyManager())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create API handler: %v", err)
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func (s *BaseServer) GRPCServer() *grpc.Server {
|
||||
}
|
||||
mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv)
|
||||
|
||||
proxyService := nbgrpc.NewProxyServiceServer(s.Store(), s.AccountManager())
|
||||
proxyService := nbgrpc.NewProxyServiceServer(s.Store())
|
||||
mgmtProto.RegisterProxyServiceServer(gRPCAPIHandler, proxyService)
|
||||
log.Info("ProxyService registered on gRPC server")
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/peers"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
nbservices "github.com/netbirdio/netbird/management/internals/modules/services/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
nbreverseproxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
zonesManager "github.com/netbirdio/netbird/management/internals/modules/zones/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
@@ -177,8 +177,8 @@ func (s *BaseServer) RecordsManager() records.Manager {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseServer) ServiceManager() services.Manager {
|
||||
return Create(s, func() services.Manager {
|
||||
return nbservices.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager())
|
||||
func (s *BaseServer) ReverseProxyManager() reverseproxy.Manager {
|
||||
return Create(s, func() reverseproxy.Manager {
|
||||
return nbreverseproxy.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,18 +6,19 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
type serviceStore interface {
|
||||
GetAccountServices(ctx context.Context, lockStrength store.LockingStrength, accountID string) ([]*services.Service, error)
|
||||
type reverseProxyStore interface {
|
||||
GetAccountReverseProxies(ctx context.Context, lockStrength store.LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error)
|
||||
}
|
||||
|
||||
type keyStore interface {
|
||||
@@ -31,11 +32,11 @@ type ProxyServiceServer struct {
|
||||
// Map of connected proxies: proxy_id -> proxy connection
|
||||
connectedProxies sync.Map
|
||||
|
||||
// Channel for broadcasting service updates to all proxies
|
||||
// Channel for broadcasting reverse proxy updates to all proxies
|
||||
updatesChan chan *proto.ProxyMapping
|
||||
|
||||
// Store of services
|
||||
serviceStore serviceStore
|
||||
// Store of reverse proxies
|
||||
reverseProxyStore reverseProxyStore
|
||||
|
||||
// Store for client setup keys
|
||||
keyStore keyStore
|
||||
@@ -52,10 +53,10 @@ type proxyConnection struct {
|
||||
}
|
||||
|
||||
// NewProxyServiceServer creates a new proxy service server
|
||||
func NewProxyServiceServer(store serviceStore) *ProxyServiceServer {
|
||||
func NewProxyServiceServer(store reverseProxyStore) *ProxyServiceServer {
|
||||
return &ProxyServiceServer{
|
||||
updatesChan: make(chan *proto.ProxyMapping, 100),
|
||||
serviceStore: store,
|
||||
updatesChan: make(chan *proto.ProxyMapping, 100),
|
||||
reverseProxyStore: store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,51 +111,51 @@ func (s *ProxyServiceServer) GetMappingUpdate(req *proto.GetMappingUpdateRequest
|
||||
}
|
||||
}
|
||||
|
||||
// sendSnapshot sends the initial snapshot of all services to proxy
|
||||
// sendSnapshot sends the initial snapshot of all reverse proxies to proxy
|
||||
func (s *ProxyServiceServer) sendSnapshot(ctx context.Context, conn *proxyConnection) error {
|
||||
svcs, err := s.serviceStore.GetAccountServices(ctx, store.LockingStrengthNone, conn.proxyID) // TODO: check locking strength and accountID.
|
||||
reverseProxies, err := s.reverseProxyStore.GetAccountReverseProxies(ctx, store.LockingStrengthNone, conn.proxyID) // TODO: check locking strength and accountID.
|
||||
if err != nil {
|
||||
// TODO: something
|
||||
return fmt.Errorf("get account services from store: %w", err)
|
||||
return fmt.Errorf("get account reverse proxies from store: %w", err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if !svc.Enabled || !svc.Exposed {
|
||||
// We don't care about disabled services for snapshots.
|
||||
for _, rp := range reverseProxies {
|
||||
if !rp.Enabled {
|
||||
// We don't care about disabled reverse proxies for snapshots.
|
||||
continue
|
||||
}
|
||||
|
||||
// Fill auth values.
|
||||
// TODO: This will be removed soon as the management server should be handling authentication rather than the proxy, so probably not much use in fleshing this out too much.
|
||||
auth := &proto.Authentication{}
|
||||
if svc.AuthBearerEnabled {
|
||||
if rp.Auth.BearerAuth != nil && rp.Auth.BearerAuth.Enabled {
|
||||
auth.Oidc = &proto.OIDC{
|
||||
Enabled: true,
|
||||
// TODO: fill other OIDC fields from account OIDC settings.
|
||||
}
|
||||
}
|
||||
if svc.AuthBasicPassword != "" {
|
||||
if rp.Auth.PasswordAuth != nil && rp.Auth.PasswordAuth.Password != "" {
|
||||
auth.Basic = &proto.HTTPBasic{
|
||||
Enabled: true,
|
||||
Username: svc.AuthBasicUsername,
|
||||
Password: svc.AuthBasicPassword,
|
||||
Username: "",
|
||||
Password: rp.Auth.PasswordAuth.Password,
|
||||
}
|
||||
}
|
||||
if svc.AuthPINValue != "" {
|
||||
if rp.Auth.PinAuth != nil && rp.Auth.PinAuth.Pin != "" {
|
||||
auth.Pin = &proto.Pin{
|
||||
Enabled: true,
|
||||
Pin: svc.AuthPINValue,
|
||||
Pin: rp.Auth.PinAuth.Pin,
|
||||
}
|
||||
}
|
||||
|
||||
var paths []*proto.PathMapping
|
||||
for _, t := range svc.Targets {
|
||||
for _, t := range rp.Targets {
|
||||
if !t.Enabled {
|
||||
// We don't care about disabled service targets for snapshots.
|
||||
// We don't care about disabled reverse proxy targets for snapshots.
|
||||
continue
|
||||
}
|
||||
paths = append(paths, &proto.PathMapping{
|
||||
Path: t.Path,
|
||||
Path: *t.Path,
|
||||
Target: t.Host,
|
||||
})
|
||||
}
|
||||
@@ -179,8 +180,8 @@ func (s *ProxyServiceServer) sendSnapshot(ctx context.Context, conn *proxyConnec
|
||||
Mapping: []*proto.ProxyMapping{
|
||||
{
|
||||
Type: proto.ProxyMappingUpdateType_UPDATE_TYPE_CREATED, // Initial snapshot, all records are "new" for the proxy.
|
||||
Id: svc.ID,
|
||||
Domain: svc.Domain,
|
||||
Id: rp.ID,
|
||||
Domain: rp.Domain,
|
||||
Path: paths,
|
||||
SetupKey: key.Key,
|
||||
Auth: auth,
|
||||
@@ -214,34 +215,34 @@ func (s *ProxyServiceServer) sender(conn *proxyConnection, errChan chan<- error)
|
||||
// SendAccessLog processes access log from proxy
|
||||
func (s *ProxyServiceServer) SendAccessLog(ctx context.Context, req *proto.SendAccessLogRequest) (*proto.SendAccessLogResponse, error) {
|
||||
log.WithFields(log.Fields{
|
||||
"proxy_id": "", // TODO: get proxy id, probably from context or maybe from request message.
|
||||
"service_id": req.GetLog().GetServiceId(),
|
||||
"host": req.GetLog().GetHost(),
|
||||
"path": req.GetLog().GetPath(),
|
||||
"method": req.GetLog().GetMethod(),
|
||||
"response_code": req.GetLog().GetResponseCode(),
|
||||
"duration_ms": req.GetLog().GetDurationMs(),
|
||||
"source_ip": req.GetLog().GetSourceIp(),
|
||||
"auth_mechanism": req.GetLog().GetAuthMechanism(),
|
||||
"user_id": req.GetLog().GetUserId(),
|
||||
"auth_success": req.GetLog().GetAuthSuccess(),
|
||||
"proxy_id": "", // TODO: get proxy id, probably from context or maybe from request message.
|
||||
"reverse_proxy_id": req.GetLog().GetServiceId(),
|
||||
"host": req.GetLog().GetHost(),
|
||||
"path": req.GetLog().GetPath(),
|
||||
"method": req.GetLog().GetMethod(),
|
||||
"response_code": req.GetLog().GetResponseCode(),
|
||||
"duration_ms": req.GetLog().GetDurationMs(),
|
||||
"source_ip": req.GetLog().GetSourceIp(),
|
||||
"auth_mechanism": req.GetLog().GetAuthMechanism(),
|
||||
"user_id": req.GetLog().GetUserId(),
|
||||
"auth_success": req.GetLog().GetAuthSuccess(),
|
||||
}).Info("Access log from proxy")
|
||||
|
||||
// TODO: Store access log in database/metrics system
|
||||
return &proto.SendAccessLogResponse{}, nil
|
||||
}
|
||||
|
||||
// SendServiceUpdate broadcasts a service update to all connected proxies.
|
||||
// Management should call this when services are created/updated/removed
|
||||
func (s *ProxyServiceServer) SendServiceUpdate(update *proto.ProxyMapping) {
|
||||
// SendReverseProxyUpdate broadcasts a reverse proxy update to all connected proxies.
|
||||
// Management should call this when reverse proxies are created/updated/removed
|
||||
func (s *ProxyServiceServer) SendReverseProxyUpdate(update *proto.ProxyMapping) {
|
||||
// Send it to all connected proxies
|
||||
s.connectedProxies.Range(func(key, value interface{}) bool {
|
||||
conn := value.(*proxyConnection)
|
||||
select {
|
||||
case conn.sendChan <- update:
|
||||
log.Debugf("Sent service update to proxy %s", conn.proxyID)
|
||||
log.Debugf("Sent reverse proxy update to proxy %s", conn.proxyID)
|
||||
default:
|
||||
log.Warnf("Failed to send service update to proxy %s (channel full)", conn.proxyID)
|
||||
log.Warnf("Failed to send reverse proxy update to proxy %s (channel full)", conn.proxyID)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -204,9 +204,9 @@ const (
|
||||
UserInviteLinkRegenerated Activity = 106
|
||||
UserInviteLinkDeleted Activity = 107
|
||||
|
||||
ServiceCreated Activity = 108
|
||||
ServiceUpdated Activity = 109
|
||||
ServiceDeleted Activity = 110
|
||||
ReverseProxyCreated Activity = 108
|
||||
ReverseProxyUpdated Activity = 109
|
||||
ReverseProxyDeleted Activity = 110
|
||||
|
||||
AccountDeleted Activity = 99999
|
||||
)
|
||||
@@ -342,9 +342,9 @@ var activityMap = map[Activity]Code{
|
||||
UserInviteLinkRegenerated: {"User invite link regenerated", "user.invite.link.regenerate"},
|
||||
UserInviteLinkDeleted: {"User invite link deleted", "user.invite.link.delete"},
|
||||
|
||||
ServiceCreated: {"Service created", "service.create"},
|
||||
ServiceUpdated: {"Service updated", "service.update"},
|
||||
ServiceDeleted: {"Service deleted", "service.delete"},
|
||||
ReverseProxyCreated: {"Reverse proxy created", "reverseproxy.create"},
|
||||
ReverseProxyUpdated: {"Reverse proxy updated", "reverseproxy.update"},
|
||||
ReverseProxyDeleted: {"Reverse proxy deleted", "reverseproxy.delete"},
|
||||
}
|
||||
|
||||
// StringCode returns a string code of the activity
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/rs/cors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
nbservices "github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
services "github.com/netbirdio/netbird/management/internals/modules/services/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
reverseproxymanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/manager"
|
||||
idpmanager "github.com/netbirdio/netbird/management/server/idp"
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
@@ -62,7 +62,7 @@ const (
|
||||
)
|
||||
|
||||
// NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
||||
func NewAPIHandler(ctx context.Context, accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, integratedValidator integrated_validator.IntegratedValidator, proxyController port_forwarding.Controller, permissionsManager permissions.Manager, peersManager nbpeers.Manager, settingsManager settings.Manager, zManager zones.Manager, rManager records.Manager, networkMapController network_map.Controller, idpManager idpmanager.Manager, serviceManager nbservices.Manager) (http.Handler, error) {
|
||||
func NewAPIHandler(ctx context.Context, accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, integratedValidator integrated_validator.IntegratedValidator, proxyController port_forwarding.Controller, permissionsManager permissions.Manager, peersManager nbpeers.Manager, settingsManager settings.Manager, zManager zones.Manager, rManager records.Manager, networkMapController network_map.Controller, idpManager idpmanager.Manager, reverseProxyManager reverseproxy.Manager) (http.Handler, error) {
|
||||
|
||||
// Register bypass paths for unauthenticated endpoints
|
||||
if err := bypass.AddBypassPath("/api/instance"); err != nil {
|
||||
@@ -158,7 +158,7 @@ func NewAPIHandler(ctx context.Context, accountManager account.Manager, networks
|
||||
idp.AddEndpoints(accountManager, router)
|
||||
instance.AddEndpoints(instanceManager, router)
|
||||
instance.AddVersionEndpoint(instanceManager, router)
|
||||
services.RegisterEndpoints(router, serviceManager)
|
||||
reverseproxymanager.RegisterEndpoints(router, reverseProxyManager)
|
||||
|
||||
// Mount embedded IdP handler at /oauth2 path if configured
|
||||
if embeddedIdpEnabled {
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
@@ -127,7 +127,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine types.Engine, met
|
||||
&types.Account{}, &types.Policy{}, &types.PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
|
||||
&installation{}, &types.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
|
||||
&networkTypes.Network{}, &routerTypes.NetworkRouter{}, &resourceTypes.NetworkResource{}, &types.AccountOnboarding{},
|
||||
&types.Job{}, &zones.Zone{}, &records.Record{}, &types.UserInviteRecord{}, &services.Service{},
|
||||
&types.Job{}, &zones.Zone{}, &records.Record{}, &types.UserInviteRecord{}, &reverseproxy.ReverseProxy{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("auto migratePreAuto: %w", err)
|
||||
@@ -4604,87 +4604,87 @@ func (s *SqlStore) GetPeerIDByKey(ctx context.Context, lockStrength LockingStren
|
||||
return peerID, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) CreateService(ctx context.Context, service *services.Service) error {
|
||||
result := s.db.Create(service)
|
||||
func (s *SqlStore) CreateReverseProxy(ctx context.Context, proxy *reverseproxy.ReverseProxy) error {
|
||||
result := s.db.Create(proxy)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to create service to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to create service to store")
|
||||
log.WithContext(ctx).Errorf("failed to create reverse proxy to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to create reverse proxy to store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) UpdateService(ctx context.Context, service *services.Service) error {
|
||||
result := s.db.Select("*").Save(service)
|
||||
func (s *SqlStore) UpdateReverseProxy(ctx context.Context, proxy *reverseproxy.ReverseProxy) error {
|
||||
result := s.db.Select("*").Save(proxy)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to update service to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to update service to store")
|
||||
log.WithContext(ctx).Errorf("failed to update reverse proxy to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to update reverse proxy to store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) DeleteService(ctx context.Context, accountID, serviceID string) error {
|
||||
result := s.db.Delete(&services.Service{}, accountAndIDQueryCondition, accountID, serviceID)
|
||||
func (s *SqlStore) DeleteReverseProxy(ctx context.Context, accountID, proxyID string) error {
|
||||
result := s.db.Delete(&reverseproxy.ReverseProxy{}, accountAndIDQueryCondition, accountID, proxyID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to delete service from store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to delete service from store")
|
||||
log.WithContext(ctx).Errorf("failed to delete reverse proxy from store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to delete reverse proxy from store")
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return status.Errorf(status.NotFound, "service %s not found", serviceID)
|
||||
return status.Errorf(status.NotFound, "reverse proxy %s not found", proxyID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetServiceByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*services.Service, error) {
|
||||
func (s *SqlStore) GetReverseProxyByID(ctx context.Context, lockStrength LockingStrength, accountID, proxyID string) (*reverseproxy.ReverseProxy, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var service *services.Service
|
||||
result := tx.Take(&service, accountAndIDQueryCondition, accountID, serviceID)
|
||||
var proxy *reverseproxy.ReverseProxy
|
||||
result := tx.Take(&proxy, accountAndIDQueryCondition, accountID, proxyID)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, status.Errorf(status.NotFound, "service %s not found", serviceID)
|
||||
return nil, status.Errorf(status.NotFound, "reverse proxy %s not found", proxyID)
|
||||
}
|
||||
|
||||
log.WithContext(ctx).Errorf("failed to get service from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get service from store")
|
||||
log.WithContext(ctx).Errorf("failed to get reverse proxy from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get reverse proxy from store")
|
||||
}
|
||||
|
||||
return service, nil
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetServiceByDomain(ctx context.Context, accountID, domain string) (*services.Service, error) {
|
||||
var service *services.Service
|
||||
result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&service)
|
||||
func (s *SqlStore) GetReverseProxyByDomain(ctx context.Context, accountID, domain string) (*reverseproxy.ReverseProxy, error) {
|
||||
var proxy *reverseproxy.ReverseProxy
|
||||
result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&proxy)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, status.Errorf(status.NotFound, "service with domain %s not found", domain)
|
||||
return nil, status.Errorf(status.NotFound, "reverse proxy with domain %s not found", domain)
|
||||
}
|
||||
|
||||
log.WithContext(ctx).Errorf("failed to get service by domain from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get service by domain from store")
|
||||
log.WithContext(ctx).Errorf("failed to get reverse proxy by domain from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get reverse proxy by domain from store")
|
||||
}
|
||||
|
||||
return service, nil
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetAccountServices(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*services.Service, error) {
|
||||
func (s *SqlStore) GetAccountReverseProxies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var servicesList []*services.Service
|
||||
result := tx.Find(&servicesList, accountIDCondition, accountID)
|
||||
var proxyList []*reverseproxy.ReverseProxy
|
||||
result := tx.Find(&proxyList, accountIDCondition, accountID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to get services from the store: %s", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get services from store")
|
||||
log.WithContext(ctx).Errorf("failed to get reverse proxy from the store: %s", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get reverse proxy from store")
|
||||
}
|
||||
|
||||
return servicesList, nil
|
||||
return proxyList, nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/services"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
@@ -242,12 +242,12 @@ type Store interface {
|
||||
MarkAllPendingJobsAsFailed(ctx context.Context, accountID, peerID, reason string) error
|
||||
GetPeerIDByKey(ctx context.Context, lockStrength LockingStrength, key string) (string, error)
|
||||
|
||||
CreateService(ctx context.Context, service *services.Service) error
|
||||
UpdateService(ctx context.Context, service *services.Service) error
|
||||
DeleteService(ctx context.Context, accountID, serviceID string) error
|
||||
GetServiceByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*services.Service, error)
|
||||
GetServiceByDomain(ctx context.Context, accountID, domain string) (*services.Service, error)
|
||||
GetAccountServices(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*services.Service, error)
|
||||
CreateReverseProxy(ctx context.Context, service *reverseproxy.ReverseProxy) error
|
||||
UpdateReverseProxy(ctx context.Context, service *reverseproxy.ReverseProxy) error
|
||||
DeleteReverseProxy(ctx context.Context, accountID, serviceID string) error
|
||||
GetReverseProxyByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*reverseproxy.ReverseProxy, error)
|
||||
GetReverseProxyByDomain(ctx context.Context, accountID, domain string) (*reverseproxy.ReverseProxy, error)
|
||||
GetAccountReverseProxies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user