package com.litoralregas.openvpn.router; import com.litoralregas.openvpn.deployment.DeploymentAction; import com.litoralregas.openvpn.deployment.DeploymentResponse; import com.litoralregas.openvpn.deployment.DeploymentService; import com.litoralregas.openvpn.openvpn.IpAllocationService; import com.litoralregas.openvpn.openvpn.OpenVpnService; import com.litoralregas.openvpn.ssh.SshService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.UUID; @RestController @RequestMapping("/api/routers") public class RouterController { private final RouterService service; private final DeploymentService deploymentService; private final SshService sshService; private final IpAllocationService ipAllocationService; private final OpenVpnService openVpnService; public RouterController(RouterService service, DeploymentService deploymentService, SshService sshService, IpAllocationService ipAllocationService, OpenVpnService openVpnService) { this.service = service; this.deploymentService = deploymentService; this.sshService = sshService; this.ipAllocationService = ipAllocationService; this.openVpnService = openVpnService; } @GetMapping public List getAll() { return service.findAll(); } @PostMapping public Router create(@Valid @RequestBody CreateRouterRequest request) { return service.create(request); } @GetMapping("/{id}") public Router getById(@PathVariable UUID id) { return service.findById(id); } @PatchMapping("/{id}/status") public Router updateStatus( @PathVariable UUID id, @Valid @RequestBody UpdateRouterStatusRequest request ) { return service.updateStatus(id, request); } @DeleteMapping("/{id}") public void delete(@PathVariable UUID id) { service.delete(id); } @PostMapping("/{id}/provision") public DeploymentResponse provision(@PathVariable UUID id) { Router router = service.findById(id); var deployment = deploymentService.startDeployment(router, DeploymentAction.PROVISION); try { service.forceStatus(id, RouterStatus.PROVISIONING); var allocation = ipAllocationService.findByRouterId(id); var preflight = openVpnService.runPreflightCheck(); if (preflight.exitCode() != 0) { throw new IllegalStateException( "Preflight failed. stdout: " + preflight.stdout() + " stderr: " + preflight.stderr() ); } var result = openVpnService.provisionClient( allocation.getClientName(), allocation.getLanSubnet(), allocation.getVpnIp() ); if (result.exitCode() != 0) { throw new IllegalStateException( "Provision failed. stdout: " + result.stdout() + " stderr: " + result.stderr() ); } var finishedDeployment = deploymentService.finishSuccess( deployment, result.stdout() ); if (openVpnService.isDryRun()) { service.forceStatus(id, RouterStatus.PENDING); } else { service.forceStatus(id, RouterStatus.PROVISIONED); } return DeploymentResponse.from(finishedDeployment); } catch (Exception exception) { var failedDeployment = deploymentService.finishFailure( deployment, exception.getMessage() ); service.forceStatus(id, RouterStatus.FAILED); return DeploymentResponse.from(failedDeployment); } } @PostMapping("/{id}/remove") public DeploymentResponse remove(@PathVariable UUID id) { Router router = service.findById(id); var deployment = deploymentService.startDeployment(router, DeploymentAction.REMOVE); try { service.forceStatus(id, RouterStatus.PROVISIONING); // or REMOVING if you want later var result = sshService.executeOnConfiguredVps( "echo 'Removing router: " + router.getName() + "' && whoami && hostname" ); if (result.exitCode() != 0) { throw new IllegalStateException(result.stderr()); } var finished = deploymentService.finishSuccess( deployment, result.stdout() ); service.forceStatus(id, RouterStatus.REMOVED); return DeploymentResponse.from(finished); } catch (Exception e) { var failed = deploymentService.finishFailure( deployment, e.getMessage() ); service.forceStatus(id, RouterStatus.FAILED); return DeploymentResponse.from(failed); } } }