chore: initial project scaffold with admin web, backend, desktop client, and deployment setup
Add monorepo structure for NexaVPN WireGuard control plane including: - .gitignore for node_modules, build artifacts, and environment files - README with project overview, monorepo layout, and quick start guide - Admin web UI with React, Vite, TypeScript, and nginx reverse proxy - API client with type definitions for users, devices, policies, gateways, and audit logs - Admin pages for dashboard, users, devices, policies, g
This commit is contained in:
130
backend/internal/device/service.go
Normal file
130
backend/internal/device/service.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/nexavpn/nexavpn/backend/internal/gateway"
|
||||
"github.com/nexavpn/nexavpn/backend/internal/ipam"
|
||||
"github.com/nexavpn/nexavpn/backend/internal/policy"
|
||||
"github.com/nexavpn/nexavpn/backend/internal/profile"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
repo Repository
|
||||
policyService *policy.Service
|
||||
gatewayService *gateway.Service
|
||||
ipamService *ipam.Service
|
||||
}
|
||||
|
||||
func NewService(repo Repository, policyService *policy.Service, gatewayService *gateway.Service, ipamService *ipam.Service) *Service {
|
||||
return &Service{
|
||||
repo: repo,
|
||||
policyService: policyService,
|
||||
gatewayService: gatewayService,
|
||||
ipamService: ipamService,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Enroll(ctx context.Context, userID uuid.UUID, input EnrollRequest, privateKeyPlaceholder string) (EnrollmentResponse, error) {
|
||||
selectedGateway, err := s.gatewayService.SelectActive(ctx)
|
||||
if err != nil {
|
||||
return EnrollmentResponse{}, err
|
||||
}
|
||||
|
||||
assignedIP, err := s.ipamService.Allocate(selectedGateway.VPNCIDR, 10)
|
||||
if err != nil {
|
||||
return EnrollmentResponse{}, err
|
||||
}
|
||||
|
||||
enrollment, err := s.repo.Enroll(ctx, userID, selectedGateway.ID, input, assignedIP, selectedGateway.DNSServers, nil)
|
||||
if err != nil {
|
||||
return EnrollmentResponse{}, err
|
||||
}
|
||||
|
||||
destinations, err := s.policyService.ResolveDestinations(ctx, userID, &enrollment.Device.ID)
|
||||
if err != nil {
|
||||
return EnrollmentResponse{}, err
|
||||
}
|
||||
if len(destinations) == 0 {
|
||||
destinations = []string{"172.16.10.0/24"}
|
||||
}
|
||||
|
||||
enrollment.Peer = PeerView{
|
||||
AssignedIP: assignedIP,
|
||||
DNSServers: selectedGateway.DNSServers,
|
||||
AllowedIPs: destinations,
|
||||
Gateway: GatewayView{
|
||||
ID: selectedGateway.ID,
|
||||
Name: selectedGateway.Name,
|
||||
Endpoint: selectedGateway.Endpoint,
|
||||
PublicKey: selectedGateway.PublicKey,
|
||||
},
|
||||
ProfileRevision: 1,
|
||||
}
|
||||
for _, destination := range destinations {
|
||||
enrollment.Resources = append(enrollment.Resources, Resource{
|
||||
Type: "cidr",
|
||||
Value: destination,
|
||||
Label: destination,
|
||||
})
|
||||
}
|
||||
|
||||
enrollment.Profile = ProfileView{
|
||||
Format: "wireguard",
|
||||
Content: profile.BuildWireGuardConfig(profile.BuildInput{
|
||||
PrivateKey: privateKeyPlaceholder,
|
||||
Address: assignedIP,
|
||||
DNSServers: selectedGateway.DNSServers,
|
||||
ServerPublicKey: selectedGateway.PublicKey,
|
||||
ServerEndpoint: selectedGateway.Endpoint,
|
||||
AllowedIPs: destinations,
|
||||
PersistentKeepal: 25,
|
||||
}),
|
||||
}
|
||||
|
||||
return enrollment, nil
|
||||
}
|
||||
|
||||
func (s *Service) ListByUser(ctx context.Context, userID uuid.UUID) ([]Device, error) {
|
||||
return s.repo.ListByUser(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *Service) ListAll(ctx context.Context) ([]Device, error) {
|
||||
return s.repo.ListAll(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) GetLatestEnrollmentByUser(ctx context.Context, userID uuid.UUID) (EnrollmentResponse, error) {
|
||||
return s.repo.GetLatestEnrollmentByUser(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *Service) GetEnrollmentByDeviceID(ctx context.Context, deviceID uuid.UUID) (EnrollmentResponse, error) {
|
||||
return s.repo.GetEnrollmentByDeviceID(ctx, deviceID)
|
||||
}
|
||||
|
||||
func (s *Service) GetConnectionStatus(ctx context.Context, userID uuid.UUID) (ConnectionStatus, error) {
|
||||
enrollment, err := s.repo.GetLatestEnrollmentByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return ConnectionStatus{
|
||||
Status: "disconnected",
|
||||
Resources: []Resource{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
lastSync := "just now"
|
||||
return ConnectionStatus{
|
||||
Status: "provisioned",
|
||||
AssignedIP: enrollment.Peer.AssignedIP,
|
||||
LastSyncTime: &lastSync,
|
||||
Resources: enrollment.Resources,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Revoke(ctx context.Context, deviceID uuid.UUID) error {
|
||||
return s.repo.Revoke(ctx, deviceID)
|
||||
}
|
||||
|
||||
func (s *Service) Rotate(ctx context.Context, deviceID uuid.UUID) error {
|
||||
return s.repo.Rotate(ctx, deviceID)
|
||||
}
|
||||
Reference in New Issue
Block a user