diff --git a/src/main/java/com/litoralregas/vpnprovisioner/ipam/controller/VpnIpAllocationController.java b/src/main/java/com/litoralregas/vpnprovisioner/ipam/controller/VpnIpAllocationController.java deleted file mode 100644 index 1c4a13e..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/ipam/controller/VpnIpAllocationController.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.litoralregas.vpnprovisioner.ipam.controller; - -import com.litoralregas.vpnprovisioner.ipam.dto.VpnIpAllocationResponse; -import com.litoralregas.vpnprovisioner.ipam.service.VpnIpAllocationService; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -import java.util.UUID; - -@RestController -@RequestMapping("/api/ipam") -public class VpnIpAllocationController { - - private final VpnIpAllocationService service; - - public VpnIpAllocationController(VpnIpAllocationService service) { - this.service = service; - } - - @PostMapping("/routers/{routerId}/allocate") - public VpnIpAllocationResponse allocate(@PathVariable UUID routerId) { - return service.allocateForRouter(routerId); - } - - @PostMapping("/routers/{routerId}/release") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void release(@PathVariable UUID routerId) { - service.releaseForRouter(routerId); - } -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/ipam/dto/VpnIpAllocationResponse.java b/src/main/java/com/litoralregas/vpnprovisioner/ipam/dto/VpnIpAllocationResponse.java deleted file mode 100644 index 0632c22..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/ipam/dto/VpnIpAllocationResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.litoralregas.vpnprovisioner.ipam.dto; - -import com.litoralregas.vpnprovisioner.ipam.entity.VpnIpAllocationStatus; - -import java.time.Instant; -import java.util.UUID; - -public record VpnIpAllocationResponse( - UUID id, - String ipAddress, - VpnIpAllocationStatus status, - UUID routerId, - Instant allocatedAt, - Instant releasedAt -) { -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/ipam/entity/VpnIpAllocation.java b/src/main/java/com/litoralregas/vpnprovisioner/ipam/entity/VpnIpAllocation.java deleted file mode 100644 index 1121eab..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/ipam/entity/VpnIpAllocation.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.litoralregas.vpnprovisioner.ipam.entity; - -import jakarta.persistence.*; - -import java.time.Instant; -import java.util.UUID; - -@Entity -@Table(name = "vpn_ip_allocations") -public class VpnIpAllocation { - - @Id - private UUID id; - - @Column(nullable = false, unique = true) - private String ipAddress; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private VpnIpAllocationStatus status; - - private UUID routerId; - - private Instant allocatedAt; - - private Instant releasedAt; - - protected VpnIpAllocation() { - } - - public VpnIpAllocation(String ipAddress) { - this.id = UUID.randomUUID(); - this.ipAddress = ipAddress; - this.status = VpnIpAllocationStatus.ALLOCATED_EXTERNAL; - this.allocatedAt = Instant.now(); - } - - public static VpnIpAllocation backendAllocated(String ipAddress, UUID routerId) { - VpnIpAllocation allocation = new VpnIpAllocation(ipAddress); - allocation.allocateTo(routerId); - return allocation; - } - - public void allocateTo(UUID routerId) { - this.status = VpnIpAllocationStatus.ALLOCATED; - this.routerId = routerId; - this.allocatedAt = Instant.now(); - this.releasedAt = null; - } - - public void markExternal() { - if (this.status != VpnIpAllocationStatus.ALLOCATED) { - this.status = VpnIpAllocationStatus.ALLOCATED_EXTERNAL; - this.allocatedAt = this.allocatedAt == null ? Instant.now() : this.allocatedAt; - } - } - - public void release() { - this.status = VpnIpAllocationStatus.RELEASED; - this.routerId = null; - this.releasedAt = Instant.now(); - } - - public UUID getId() { - return id; - } - - public String getIpAddress() { - return ipAddress; - } - - public VpnIpAllocationStatus getStatus() { - return status; - } - - public UUID getRouterId() { - return routerId; - } - - public Instant getAllocatedAt() { - return allocatedAt; - } - - public Instant getReleasedAt() { - return releasedAt; - } -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/ipam/entity/VpnIpAllocationStatus.java b/src/main/java/com/litoralregas/vpnprovisioner/ipam/entity/VpnIpAllocationStatus.java deleted file mode 100644 index 6545340..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/ipam/entity/VpnIpAllocationStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.litoralregas.vpnprovisioner.ipam.entity; - -public enum VpnIpAllocationStatus { - ALLOCATED, - ALLOCATED_EXTERNAL, - RELEASED, - RETIRED -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/ipam/repository/VpnIpAllocationRepository.java b/src/main/java/com/litoralregas/vpnprovisioner/ipam/repository/VpnIpAllocationRepository.java deleted file mode 100644 index 746a455..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/ipam/repository/VpnIpAllocationRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.litoralregas.vpnprovisioner.ipam.repository; - -import com.litoralregas.vpnprovisioner.ipam.entity.VpnIpAllocation; -import org.springframework.data.jpa.repository.*; -import org.springframework.data.repository.query.Param; - -import java.util.Optional; -import java.util.UUID; - -public interface VpnIpAllocationRepository extends JpaRepository { - - Optional findByRouterId(UUID routerId); - - Optional findByIpAddress(String ipAddress); - - @Modifying - @Query(""" - UPDATE VpnIpAllocation ip - SET ip.status = 'RELEASED', - ip.routerId = null, - ip.releasedAt = CURRENT_TIMESTAMP - WHERE ip.routerId = :routerId - """) - void releaseByRouterId(@Param("routerId") UUID routerId); -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/ipam/service/VpnIpAllocationService.java b/src/main/java/com/litoralregas/vpnprovisioner/ipam/service/VpnIpAllocationService.java deleted file mode 100644 index 979ad08..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/ipam/service/VpnIpAllocationService.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.litoralregas.vpnprovisioner.ipam.service; - -import com.litoralregas.vpnprovisioner.common.exception.ResourceNotFoundException; -import com.litoralregas.vpnprovisioner.ipam.dto.VpnIpAllocationResponse; -import com.litoralregas.vpnprovisioner.ipam.entity.VpnIpAllocation; -import com.litoralregas.vpnprovisioner.ipam.repository.VpnIpAllocationRepository; -import com.litoralregas.vpnprovisioner.router.entity.Router; -import com.litoralregas.vpnprovisioner.router.repository.RouterRepository; -import com.litoralregas.vpnprovisioner.vps.WireGuardVpsService; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -@Service -public class VpnIpAllocationService { - - private static final int START_THIRD_OCTET = 1; - private static final int END_THIRD_OCTET = 255; - private static final int START_FOURTH_OCTET = 1; - private static final int END_FOURTH_OCTET = 254; - - private final VpnIpAllocationRepository vpnIpAllocationRepository; - private final RouterRepository routerRepository; - private final WireGuardVpsService wireGuardVpsService; - - public VpnIpAllocationService( - VpnIpAllocationRepository vpnIpAllocationRepository, - RouterRepository routerRepository, - WireGuardVpsService wireGuardVpsService - ) { - this.vpnIpAllocationRepository = vpnIpAllocationRepository; - this.routerRepository = routerRepository; - this.wireGuardVpsService = wireGuardVpsService; - } - - @Transactional - public VpnIpAllocationResponse allocateForRouter(UUID routerId) { - Router router = routerRepository.findById(routerId) - .orElseThrow(() -> new ResourceNotFoundException("Router not found: " + routerId)); - - return vpnIpAllocationRepository.findByRouterId(routerId) - .map(this::toResponse) - .orElseGet(() -> allocateNewIp(router)); - } - - @Transactional - public void releaseForRouter(UUID routerId) { - vpnIpAllocationRepository.releaseByRouterId(routerId); - } - - private VpnIpAllocationResponse allocateNewIp(Router router) { - Set usedOnVps = refreshUsedIpsFromVps(); - - Set knownInDb = vpnIpAllocationRepository.findAll() - .stream() - .map(VpnIpAllocation::getIpAddress) - .collect(Collectors.toSet()); - - String nextIp = findNextAvailableIp(usedOnVps, knownInDb); - - VpnIpAllocation allocation = VpnIpAllocation.backendAllocated(nextIp, router.getId()); - - VpnIpAllocation saved = vpnIpAllocationRepository.save(allocation); - - router.assignVpnIp(saved.getIpAddress()); - - return toResponse(saved); - } - - private Set refreshUsedIpsFromVps() { - Set usedIps = wireGuardVpsService.findUsedVpnIps(); - - for (String ip : usedIps) { - vpnIpAllocationRepository.findByIpAddress(ip) - .ifPresentOrElse( - VpnIpAllocation::markExternal, - () -> vpnIpAllocationRepository.save(new VpnIpAllocation(ip)) - ); - } - - return usedIps; - } - - private String findNextAvailableIp(Set usedOnVps, Set knownInDb) { - for (int thirdOctet = START_THIRD_OCTET; thirdOctet <= END_THIRD_OCTET; thirdOctet++) { - for (int fourthOctet = START_FOURTH_OCTET; fourthOctet <= END_FOURTH_OCTET; fourthOctet++) { - String ip = "198.19." + thirdOctet + "." + fourthOctet; - - if (!usedOnVps.contains(ip) && !knownInDb.contains(ip)) { - return ip; - } - } - } - - throw new IllegalStateException("No free VPN IP addresses available"); - } - - private VpnIpAllocationResponse toResponse(VpnIpAllocation allocation) { - return new VpnIpAllocationResponse( - allocation.getId(), - allocation.getIpAddress(), - allocation.getStatus(), - allocation.getRouterId(), - allocation.getAllocatedAt(), - allocation.getReleasedAt() - ); - } -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/router/controller/RouterController.java b/src/main/java/com/litoralregas/vpnprovisioner/router/controller/RouterController.java index c02c481..734fc41 100644 --- a/src/main/java/com/litoralregas/vpnprovisioner/router/controller/RouterController.java +++ b/src/main/java/com/litoralregas/vpnprovisioner/router/controller/RouterController.java @@ -1,10 +1,8 @@ package com.litoralregas.vpnprovisioner.router.controller; -import com.litoralregas.vpnprovisioner.router.dto.CreateRouterRequest; -import com.litoralregas.vpnprovisioner.router.dto.ProvisionRouterVpnRequest; -import com.litoralregas.vpnprovisioner.router.dto.RouterResponse; -import com.litoralregas.vpnprovisioner.router.dto.UpdateRouterRequest; +import com.litoralregas.vpnprovisioner.router.dto.*; import com.litoralregas.vpnprovisioner.router.service.RouterService; +import com.litoralregas.vpnprovisioner.router.service.RouterSyncService; import com.litoralregas.vpnprovisioner.router.service.RouterVpnProvisioningService; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; @@ -19,10 +17,12 @@ public class RouterController { private final RouterService routerService; private final RouterVpnProvisioningService routerVpnProvisioningService; + private final RouterSyncService routerSyncService; - public RouterController(RouterService routerService, RouterVpnProvisioningService routerVpnProvisioningService) { + public RouterController(RouterService routerService, RouterVpnProvisioningService routerVpnProvisioningService, RouterSyncService routerSyncService) { this.routerService = routerService; this.routerVpnProvisioningService = routerVpnProvisioningService; + this.routerSyncService = routerSyncService; } @PostMapping @@ -62,4 +62,9 @@ public class RouterController { ) { return routerVpnProvisioningService.provision(id, request); } + + @PostMapping("/sync-from-vps") + public SyncRoutersFromVpsResponse syncFromVps() { + return routerSyncService.syncFromVps(); + } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/router/dto/SyncRoutersFromVpsResponse.java b/src/main/java/com/litoralregas/vpnprovisioner/router/dto/SyncRoutersFromVpsResponse.java new file mode 100644 index 0000000..9f58f16 --- /dev/null +++ b/src/main/java/com/litoralregas/vpnprovisioner/router/dto/SyncRoutersFromVpsResponse.java @@ -0,0 +1,8 @@ +package com.litoralregas.vpnprovisioner.router.dto; + +public record SyncRoutersFromVpsResponse( + int seenOnVps, + int created, + int updated +) { +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/router/entity/Router.java b/src/main/java/com/litoralregas/vpnprovisioner/router/entity/Router.java index 2dfaa4f..9744b4a 100644 --- a/src/main/java/com/litoralregas/vpnprovisioner/router/entity/Router.java +++ b/src/main/java/com/litoralregas/vpnprovisioner/router/entity/Router.java @@ -138,4 +138,31 @@ public class Router { public Instant getUpdatedAt() { return updatedAt; } + + public static Router importedVpnRouter(String publicKey, String vpnIp) { + Router router = new Router( + "Imported Router " + vpnIp, + null, + null + ); + + router.wireguardPublicKey = publicKey; + router.vpnIp = vpnIp; + router.endpointMode = EndpointMode.UDP2RAW; + router.vpnStatus = RouterVpnStatus.APPLIED; + router.vpnProvisionedAt = Instant.now(); + + return router; + } + + public void syncVpnPeer(String publicKey, String vpnIp) { + this.wireguardPublicKey = publicKey; + this.vpnIp = vpnIp; + this.endpointMode = EndpointMode.UDP2RAW; + this.vpnStatus = RouterVpnStatus.APPLIED; + + if (this.vpnProvisionedAt == null) { + this.vpnProvisionedAt = Instant.now(); + } + } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/router/repository/RouterRepository.java b/src/main/java/com/litoralregas/vpnprovisioner/router/repository/RouterRepository.java index f4bab5e..1452edb 100644 --- a/src/main/java/com/litoralregas/vpnprovisioner/router/repository/RouterRepository.java +++ b/src/main/java/com/litoralregas/vpnprovisioner/router/repository/RouterRepository.java @@ -3,7 +3,9 @@ package com.litoralregas.vpnprovisioner.router.repository; import com.litoralregas.vpnprovisioner.router.entity.Router; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; import java.util.UUID; public interface RouterRepository extends JpaRepository { + Optional findByWireguardPublicKey(String wireguardPublicKey); } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/router/service/RouterSyncService.java b/src/main/java/com/litoralregas/vpnprovisioner/router/service/RouterSyncService.java new file mode 100644 index 0000000..c545a04 --- /dev/null +++ b/src/main/java/com/litoralregas/vpnprovisioner/router/service/RouterSyncService.java @@ -0,0 +1,87 @@ +package com.litoralregas.vpnprovisioner.router.service; + +import com.litoralregas.vpnprovisioner.router.dto.SyncRoutersFromVpsResponse; +import com.litoralregas.vpnprovisioner.router.entity.Router; +import com.litoralregas.vpnprovisioner.router.repository.RouterRepository; +import com.litoralregas.vpnprovisioner.vps.WireGuardVpsService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class RouterSyncService { + + private static final Pattern LINE_PATTERN = + Pattern.compile("^(.+?)\\s+([0-9.]+/32)$"); + + private final WireGuardVpsService wireGuardVpsService; + private final RouterRepository routerRepository; + + public RouterSyncService( + WireGuardVpsService wireGuardVpsService, + RouterRepository routerRepository + ) { + this.wireGuardVpsService = wireGuardVpsService; + this.routerRepository = routerRepository; + } + + @Transactional + public SyncRoutersFromVpsResponse syncFromVps() { + + String output = wireGuardVpsService.showAllowedIps(); + + List lines = output.lines() + .filter(line -> !line.isBlank()) + .toList(); + + int created = 0; + int updated = 0; + + for (String line : lines) { + + Matcher matcher = LINE_PATTERN.matcher(line.trim()); + + if (!matcher.matches()) { + continue; + } + + String publicKey = matcher.group(1).trim(); + String allowedIp = matcher.group(2).trim(); + + String vpnIp = allowedIp.replace("/32", ""); + + Router router = routerRepository + .findByWireguardPublicKey(publicKey) + .orElse(null); + + if (router == null) { + + Router imported = Router.importedVpnRouter( + publicKey, + vpnIp + ); + + routerRepository.save(imported); + + created++; + + } else { + + router.syncVpnPeer(publicKey, vpnIp); + + routerRepository.save(router); + + updated++; + } + } + + return new SyncRoutersFromVpsResponse( + lines.size(), + created, + updated + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/router/startup/RouterStartupSync.java b/src/main/java/com/litoralregas/vpnprovisioner/router/startup/RouterStartupSync.java new file mode 100644 index 0000000..63f169c --- /dev/null +++ b/src/main/java/com/litoralregas/vpnprovisioner/router/startup/RouterStartupSync.java @@ -0,0 +1,24 @@ +package com.litoralregas.vpnprovisioner.router.startup; + +import com.litoralregas.vpnprovisioner.router.service.RouterSyncService; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +@Component +public class RouterStartupSync implements ApplicationRunner { + + private final RouterSyncService routerSyncService; + + public RouterStartupSync(RouterSyncService routerSyncService) { + this.routerSyncService = routerSyncService; + } + + @Override + public void run(ApplicationArguments args) { + + routerSyncService.syncFromVps(); + + System.out.println("Router VPS sync completed."); + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/vps/WireGuardVpsService.java b/src/main/java/com/litoralregas/vpnprovisioner/vps/WireGuardVpsService.java index e363836..73ea5bc 100644 --- a/src/main/java/com/litoralregas/vpnprovisioner/vps/WireGuardVpsService.java +++ b/src/main/java/com/litoralregas/vpnprovisioner/vps/WireGuardVpsService.java @@ -93,4 +93,18 @@ public class WireGuardVpsService { return result.stdout(); } + + public String showAllowedIps() { + SshCommandResult result = sshService.executeOnConfiguredVps( + "sudo /usr/local/sbin/lr-wg-used-ips" + ); + + if (result.exitCode() != 0) { + throw new SshCommandException( + "Failed to query WireGuard allowed IPs: " + result.stderr() + ); + } + + return result.stdout(); + } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/controller/WireGuardPeerController.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/controller/WireGuardPeerController.java deleted file mode 100644 index e9f7420..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/controller/WireGuardPeerController.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.controller; - -import com.litoralregas.vpnprovisioner.wireguard.dto.CreateWireGuardPeerRequest; -import com.litoralregas.vpnprovisioner.wireguard.dto.WireGuardPeerResponse; -import com.litoralregas.vpnprovisioner.wireguard.service.WireGuardPeerService; -import jakarta.validation.Valid; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -import java.util.UUID; - -@RestController -@RequestMapping("/api/routers/{routerId}/wireguard-peer") -public class WireGuardPeerController { - - private final WireGuardPeerService wireGuardPeerService; - - public WireGuardPeerController(WireGuardPeerService wireGuardPeerService) { - this.wireGuardPeerService = wireGuardPeerService; - } - - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public WireGuardPeerResponse create( - @PathVariable UUID routerId, - @Valid @RequestBody CreateWireGuardPeerRequest request - ) { - return wireGuardPeerService.createForRouter(routerId, request); - } -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/dto/CreateWireGuardPeerRequest.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/dto/CreateWireGuardPeerRequest.java deleted file mode 100644 index 9002b44..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/dto/CreateWireGuardPeerRequest.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.dto; - -import com.litoralregas.vpnprovisioner.wireguard.entity.EndpointMode; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; - -public record CreateWireGuardPeerRequest( - @NotBlank String publicKey, - @NotNull EndpointMode endpointMode -) { -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/dto/WireGuardPeerResponse.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/dto/WireGuardPeerResponse.java deleted file mode 100644 index d86ad72..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/dto/WireGuardPeerResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.dto; - -import com.litoralregas.vpnprovisioner.wireguard.entity.EndpointMode; -import com.litoralregas.vpnprovisioner.wireguard.entity.WireGuardPeerStatus; - -import java.time.Instant; -import java.util.UUID; - -public record WireGuardPeerResponse( - UUID id, - UUID routerId, - String publicKey, - String vpnIp, - String allowedIps, - EndpointMode endpointMode, - WireGuardPeerStatus status, - Instant createdAt, - Instant updatedAt -) { -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/EndpointMode.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/EndpointMode.java deleted file mode 100644 index 9ba87b3..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/EndpointMode.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.entity; - -public enum EndpointMode { - NORMAL_WIREGUARD, - UDP2RAW -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/WireGuardPeer.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/WireGuardPeer.java deleted file mode 100644 index 708604e..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/WireGuardPeer.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.entity; - -import jakarta.persistence.*; - -import java.time.Instant; -import java.util.UUID; - -@Entity -@Table(name = "wireguard_peers") -public class WireGuardPeer { - - @Id - private UUID id; - - @Column(nullable = false, unique = true) - private UUID routerId; - - @Column(nullable = false, unique = true) - private String publicKey; - - @Column(nullable = false) - private String vpnIp; - - @Column(nullable = false) - private String allowedIps; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private EndpointMode endpointMode; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private WireGuardPeerStatus status; - - @Column(nullable = false) - private Instant createdAt; - - @Column(nullable = false) - private Instant updatedAt; - - protected WireGuardPeer() { - } - - public WireGuardPeer(UUID routerId, String publicKey, String vpnIp, EndpointMode endpointMode) { - this.id = UUID.randomUUID(); - this.routerId = routerId; - this.publicKey = publicKey; - this.vpnIp = vpnIp; - this.allowedIps = vpnIp + "/32"; - this.endpointMode = endpointMode; - this.status = WireGuardPeerStatus.PENDING_APPLY; - this.createdAt = Instant.now(); - this.updatedAt = Instant.now(); - } - - @PreUpdate - public void preUpdate() { - this.updatedAt = Instant.now(); - } - - public UUID getId() { return id; } - public UUID getRouterId() { return routerId; } - public String getPublicKey() { return publicKey; } - public String getVpnIp() { return vpnIp; } - public String getAllowedIps() { return allowedIps; } - public EndpointMode getEndpointMode() { return endpointMode; } - public WireGuardPeerStatus getStatus() { return status; } - public Instant getCreatedAt() { return createdAt; } - public Instant getUpdatedAt() { return updatedAt; } - - public void markApplied() { - this.status = WireGuardPeerStatus.APPLIED; - this.updatedAt = Instant.now(); - } - - public void markFailed() { - this.status = WireGuardPeerStatus.FAILED; - this.updatedAt = Instant.now(); - } -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/WireGuardPeerStatus.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/WireGuardPeerStatus.java deleted file mode 100644 index 6a09250..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/entity/WireGuardPeerStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.entity; - -public enum WireGuardPeerStatus { - PENDING_APPLY, - APPLIED, - FAILED, - REVOKED -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/repository/WireGuardPeerRepository.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/repository/WireGuardPeerRepository.java deleted file mode 100644 index cd52787..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/repository/WireGuardPeerRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.repository; - -import com.litoralregas.vpnprovisioner.wireguard.entity.WireGuardPeer; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; -import java.util.UUID; - -public interface WireGuardPeerRepository extends JpaRepository { - - Optional findByRouterId(UUID routerId); - - boolean existsByPublicKey(String publicKey); -} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/service/WireGuardPeerService.java b/src/main/java/com/litoralregas/vpnprovisioner/wireguard/service/WireGuardPeerService.java deleted file mode 100644 index e9e1910..0000000 --- a/src/main/java/com/litoralregas/vpnprovisioner/wireguard/service/WireGuardPeerService.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.litoralregas.vpnprovisioner.wireguard.service; - -import com.litoralregas.vpnprovisioner.common.exception.ResourceNotFoundException; -import com.litoralregas.vpnprovisioner.ipam.dto.VpnIpAllocationResponse; -import com.litoralregas.vpnprovisioner.ipam.service.VpnIpAllocationService; -import com.litoralregas.vpnprovisioner.router.repository.RouterRepository; -import com.litoralregas.vpnprovisioner.vps.WireGuardVpsService; -import com.litoralregas.vpnprovisioner.wireguard.dto.CreateWireGuardPeerRequest; -import com.litoralregas.vpnprovisioner.wireguard.dto.WireGuardPeerResponse; -import com.litoralregas.vpnprovisioner.wireguard.entity.WireGuardPeer; -import com.litoralregas.vpnprovisioner.wireguard.repository.WireGuardPeerRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.UUID; - -@Service -public class WireGuardPeerService { - - private final WireGuardPeerRepository wireGuardPeerRepository; - private final RouterRepository routerRepository; - private final VpnIpAllocationService vpnIpAllocationService; - private final WireGuardVpsService wireGuardVpsService; - - public WireGuardPeerService( - WireGuardPeerRepository wireGuardPeerRepository, - RouterRepository routerRepository, - VpnIpAllocationService vpnIpAllocationService, - WireGuardVpsService wireGuardVpsService) { - this.wireGuardPeerRepository = wireGuardPeerRepository; - this.routerRepository = routerRepository; - this.vpnIpAllocationService = vpnIpAllocationService; - this.wireGuardVpsService = wireGuardVpsService; - } - - @Transactional - public WireGuardPeerResponse createForRouter(UUID routerId, CreateWireGuardPeerRequest request) { - routerRepository.findById(routerId) - .orElseThrow(() -> new ResourceNotFoundException("Router not found: " + routerId)); - - return wireGuardPeerRepository.findByRouterId(routerId) - .map(this::toResponse) - .orElseGet(() -> createNewPeer(routerId, request)); - } - - private WireGuardPeerResponse createNewPeer(UUID routerId, CreateWireGuardPeerRequest request) { - if (wireGuardPeerRepository.existsByPublicKey(request.publicKey())) { - throw new IllegalArgumentException("WireGuard public key already exists"); - } - - VpnIpAllocationResponse allocation = - vpnIpAllocationService.allocateForRouter(routerId); - - WireGuardPeer peer = new WireGuardPeer( - routerId, - request.publicKey(), - allocation.ipAddress(), - request.endpointMode() - ); - - WireGuardPeer saved = wireGuardPeerRepository.save(peer); - - - try { - wireGuardVpsService.applyPeer( - saved.getPublicKey(), - saved.getAllowedIps() - ); - - saved.markApplied(); - - } catch (RuntimeException exception) { - saved.markFailed(); - throw exception; - } - - return toResponse(saved); - } - - private WireGuardPeerResponse toResponse(WireGuardPeer peer) { - return new WireGuardPeerResponse( - peer.getId(), - peer.getRouterId(), - peer.getPublicKey(), - peer.getVpnIp(), - peer.getAllowedIps(), - peer.getEndpointMode(), - peer.getStatus(), - peer.getCreatedAt(), - peer.getUpdatedAt() - ); - } -} \ No newline at end of file diff --git a/src/main/resources/db/migration/V6__drop_deprecated_ipam_and_wireguard_tables.sql b/src/main/resources/db/migration/V6__drop_deprecated_ipam_and_wireguard_tables.sql new file mode 100644 index 0000000..4c58e3e --- /dev/null +++ b/src/main/resources/db/migration/V6__drop_deprecated_ipam_and_wireguard_tables.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS wireguard_peers; +DROP TABLE IF EXISTS vpn_ip_allocations; \ No newline at end of file