diff --git a/src/main/java/net/siegeln/cameleer/saas/app/AppController.java b/src/main/java/net/siegeln/cameleer/saas/app/AppController.java index 0206658..bbe6264 100644 --- a/src/main/java/net/siegeln/cameleer/saas/app/AppController.java +++ b/src/main/java/net/siegeln/cameleer/saas/app/AppController.java @@ -3,14 +3,19 @@ package net.siegeln.cameleer.saas.app; import com.fasterxml.jackson.databind.ObjectMapper; import net.siegeln.cameleer.saas.app.dto.AppResponse; import net.siegeln.cameleer.saas.app.dto.CreateAppRequest; +import net.siegeln.cameleer.saas.environment.EnvironmentService; +import net.siegeln.cameleer.saas.runtime.RuntimeConfig; +import net.siegeln.cameleer.saas.tenant.TenantRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; @@ -26,10 +31,19 @@ public class AppController { private final AppService appService; private final ObjectMapper objectMapper; + private final EnvironmentService environmentService; + private final RuntimeConfig runtimeConfig; + private final TenantRepository tenantRepository; - public AppController(AppService appService, ObjectMapper objectMapper) { + public AppController(AppService appService, ObjectMapper objectMapper, + EnvironmentService environmentService, + RuntimeConfig runtimeConfig, + TenantRepository tenantRepository) { this.appService = appService; this.objectMapper = objectMapper; + this.environmentService = environmentService; + this.runtimeConfig = runtimeConfig; + this.tenantRepository = tenantRepository; } @PostMapping(consumes = "multipart/form-data") @@ -103,6 +117,21 @@ public class AppController { } } + @PatchMapping("/{appId}/routing") + public ResponseEntity updateRouting( + @PathVariable UUID environmentId, + @PathVariable UUID appId, + @RequestBody net.siegeln.cameleer.saas.observability.dto.UpdateRoutingRequest request, + Authentication authentication) { + try { + var actorId = resolveActorId(authentication); + var app = appService.updateRouting(appId, request.exposedPort(), actorId); + return ResponseEntity.ok(toResponse(app)); + } catch (IllegalArgumentException e) { + return ResponseEntity.notFound().build(); + } + } + private UUID resolveActorId(Authentication authentication) { String sub = authentication.getName(); try { @@ -112,19 +141,23 @@ public class AppController { } } - private AppResponse toResponse(AppEntity entity) { + private AppResponse toResponse(AppEntity app) { + String routeUrl = null; + if (app.getExposedPort() != null) { + var env = environmentService.getById(app.getEnvironmentId()).orElse(null); + if (env != null) { + var tenant = tenantRepository.findById(env.getTenantId()).orElse(null); + if (tenant != null) { + routeUrl = "http://" + app.getSlug() + "." + env.getSlug() + "." + + tenant.getSlug() + "." + runtimeConfig.getDomain(); + } + } + } return new AppResponse( - entity.getId(), - entity.getEnvironmentId(), - entity.getSlug(), - entity.getDisplayName(), - entity.getJarOriginalFilename(), - entity.getJarSizeBytes(), - entity.getJarChecksum(), - entity.getCurrentDeploymentId(), - entity.getPreviousDeploymentId(), - entity.getCreatedAt(), - entity.getUpdatedAt() - ); + app.getId(), app.getEnvironmentId(), app.getSlug(), app.getDisplayName(), + app.getJarOriginalFilename(), app.getJarSizeBytes(), app.getJarChecksum(), + app.getExposedPort(), routeUrl, + app.getCurrentDeploymentId(), app.getPreviousDeploymentId(), + app.getCreatedAt(), app.getUpdatedAt()); } } diff --git a/src/main/java/net/siegeln/cameleer/saas/app/AppService.java b/src/main/java/net/siegeln/cameleer/saas/app/AppService.java index b2dbd77..580f61b 100644 --- a/src/main/java/net/siegeln/cameleer/saas/app/AppService.java +++ b/src/main/java/net/siegeln/cameleer/saas/app/AppService.java @@ -112,6 +112,13 @@ public class AppService { null, null, "SUCCESS", null); } + public AppEntity updateRouting(UUID appId, Integer exposedPort, UUID actorId) { + var app = appRepository.findById(appId) + .orElseThrow(() -> new IllegalArgumentException("App not found")); + app.setExposedPort(exposedPort); + return appRepository.save(app); + } + public Path resolveJarPath(String relativePath) { return Path.of(runtimeConfig.getJarStoragePath()).resolve(relativePath); } diff --git a/src/main/java/net/siegeln/cameleer/saas/app/dto/AppResponse.java b/src/main/java/net/siegeln/cameleer/saas/app/dto/AppResponse.java index b0c1c94..43e1cc3 100644 --- a/src/main/java/net/siegeln/cameleer/saas/app/dto/AppResponse.java +++ b/src/main/java/net/siegeln/cameleer/saas/app/dto/AppResponse.java @@ -4,8 +4,17 @@ import java.time.Instant; import java.util.UUID; public record AppResponse( - UUID id, UUID environmentId, String slug, String displayName, - String jarOriginalFilename, Long jarSizeBytes, String jarChecksum, - UUID currentDeploymentId, UUID previousDeploymentId, - Instant createdAt, Instant updatedAt + UUID id, + UUID environmentId, + String slug, + String displayName, + String jarOriginalFilename, + Long jarSizeBytes, + String jarChecksum, + Integer exposedPort, + String routeUrl, + UUID currentDeploymentId, + UUID previousDeploymentId, + Instant createdAt, + Instant updatedAt ) {} diff --git a/src/main/java/net/siegeln/cameleer/saas/observability/dto/UpdateRoutingRequest.java b/src/main/java/net/siegeln/cameleer/saas/observability/dto/UpdateRoutingRequest.java new file mode 100644 index 0000000..385e78f --- /dev/null +++ b/src/main/java/net/siegeln/cameleer/saas/observability/dto/UpdateRoutingRequest.java @@ -0,0 +1,5 @@ +package net.siegeln.cameleer.saas.observability.dto; + +public record UpdateRoutingRequest( + Integer exposedPort +) {} diff --git a/src/main/java/net/siegeln/cameleer/saas/runtime/RuntimeConfig.java b/src/main/java/net/siegeln/cameleer/saas/runtime/RuntimeConfig.java index 5dbfe0d..dbfd972 100644 --- a/src/main/java/net/siegeln/cameleer/saas/runtime/RuntimeConfig.java +++ b/src/main/java/net/siegeln/cameleer/saas/runtime/RuntimeConfig.java @@ -39,6 +39,9 @@ public class RuntimeConfig { @Value("${cameleer.runtime.cameleer3-server-endpoint:http://cameleer3-server:8081}") private String cameleer3ServerEndpoint; + @Value("${cameleer.runtime.domain:localhost}") + private String domain; + public long getMaxJarSize() { return maxJarSize; } public String getJarStoragePath() { return jarStoragePath; } public String getBaseImage() { return baseImage; } @@ -50,6 +53,7 @@ public class RuntimeConfig { public int getContainerCpuShares() { return containerCpuShares; } public String getBootstrapToken() { return bootstrapToken; } public String getCameleer3ServerEndpoint() { return cameleer3ServerEndpoint; } + public String getDomain() { return domain; } public long parseMemoryLimitBytes() { var limit = containerMemoryLimit.trim().toLowerCase(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 04b48fc..107b0be 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -45,5 +45,6 @@ cameleer: container-cpu-shares: ${CAMELEER_CONTAINER_CPU_SHARES:512} bootstrap-token: ${CAMELEER_AUTH_TOKEN:} cameleer3-server-endpoint: ${CAMELEER3_SERVER_ENDPOINT:http://cameleer3-server:8081} + domain: ${DOMAIN:localhost} clickhouse: url: ${CLICKHOUSE_URL:jdbc:clickhouse://clickhouse:8123/cameleer}