From ebba021448d08b88bf491abf8e04ee039099c628 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:38:03 +0200 Subject: [PATCH] feat: add provisioning fields to tenants + license revoke Adds server_endpoint and provision_error columns to tenants table (V011 migration), updates TenantEntity and TenantResponse with new fields and a from() factory, adds revokeLicense() to LicenseService, and updates TenantController to use the factory. Co-Authored-By: Claude Sonnet 4.6 --- .../cameleer/saas/license/LicenseService.java | 11 +++++++++++ .../cameleer/saas/tenant/TenantController.java | 10 +--------- .../cameleer/saas/tenant/TenantEntity.java | 10 ++++++++++ .../cameleer/saas/tenant/dto/TenantResponse.java | 15 ++++++++++++++- .../migration/V011__add_provisioning_fields.sql | 3 +++ 5 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 src/main/resources/db/migration/V011__add_provisioning_fields.sql diff --git a/src/main/java/net/siegeln/cameleer/saas/license/LicenseService.java b/src/main/java/net/siegeln/cameleer/saas/license/LicenseService.java index 9030eb9..8bb32a8 100644 --- a/src/main/java/net/siegeln/cameleer/saas/license/LicenseService.java +++ b/src/main/java/net/siegeln/cameleer/saas/license/LicenseService.java @@ -52,6 +52,17 @@ public class LicenseService { return licenseRepository.findFirstByTenantIdAndRevokedAtIsNullOrderByCreatedAtDesc(tenantId); } + public void revokeLicense(UUID tenantId, UUID actorId) { + licenseRepository.findFirstByTenantIdAndRevokedAtIsNullOrderByCreatedAtDesc(tenantId) + .ifPresent(license -> { + license.setRevokedAt(Instant.now()); + licenseRepository.save(license); + auditService.log(actorId, null, tenantId, + AuditAction.LICENSE_REVOKE, "license", + null, null, null, Map.of("licenseId", license.getId().toString())); + }); + } + /** * Verifies a license token by checking its existence and validity in the database. * Returns the license entity's metadata as a map if found and not expired/revoked, diff --git a/src/main/java/net/siegeln/cameleer/saas/tenant/TenantController.java b/src/main/java/net/siegeln/cameleer/saas/tenant/TenantController.java index 903fd0f..0598f06 100644 --- a/src/main/java/net/siegeln/cameleer/saas/tenant/TenantController.java +++ b/src/main/java/net/siegeln/cameleer/saas/tenant/TenantController.java @@ -70,14 +70,6 @@ public class TenantController { } private TenantResponse toResponse(TenantEntity entity) { - return new TenantResponse( - entity.getId(), - entity.getName(), - entity.getSlug(), - entity.getTier().name(), - entity.getStatus().name(), - entity.getCreatedAt(), - entity.getUpdatedAt() - ); + return TenantResponse.from(entity); } } diff --git a/src/main/java/net/siegeln/cameleer/saas/tenant/TenantEntity.java b/src/main/java/net/siegeln/cameleer/saas/tenant/TenantEntity.java index 351a786..71f0a25 100644 --- a/src/main/java/net/siegeln/cameleer/saas/tenant/TenantEntity.java +++ b/src/main/java/net/siegeln/cameleer/saas/tenant/TenantEntity.java @@ -52,6 +52,12 @@ public class TenantEntity { @Column(name = "settings", columnDefinition = "jsonb") private Map settings = Map.of(); + @Column(name = "server_endpoint", length = 512) + private String serverEndpoint; + + @Column(name = "provision_error", columnDefinition = "TEXT") + private String provisionError; + @Column(name = "created_at", nullable = false, updatable = false) private Instant createdAt; @@ -87,6 +93,10 @@ public class TenantEntity { public void setStripeSubscriptionId(String stripeSubscriptionId) { this.stripeSubscriptionId = stripeSubscriptionId; } public Map getSettings() { return settings; } public void setSettings(Map settings) { this.settings = settings; } + public String getServerEndpoint() { return serverEndpoint; } + public void setServerEndpoint(String serverEndpoint) { this.serverEndpoint = serverEndpoint; } + public String getProvisionError() { return provisionError; } + public void setProvisionError(String provisionError) { this.provisionError = provisionError; } public Instant getCreatedAt() { return createdAt; } public Instant getUpdatedAt() { return updatedAt; } } diff --git a/src/main/java/net/siegeln/cameleer/saas/tenant/dto/TenantResponse.java b/src/main/java/net/siegeln/cameleer/saas/tenant/dto/TenantResponse.java index b008a09..611502d 100644 --- a/src/main/java/net/siegeln/cameleer/saas/tenant/dto/TenantResponse.java +++ b/src/main/java/net/siegeln/cameleer/saas/tenant/dto/TenantResponse.java @@ -1,5 +1,7 @@ package net.siegeln.cameleer.saas.tenant.dto; +import net.siegeln.cameleer.saas.tenant.TenantEntity; + import java.time.Instant; import java.util.UUID; @@ -9,6 +11,17 @@ public record TenantResponse( String slug, String tier, String status, + String serverEndpoint, + String provisionError, Instant createdAt, Instant updatedAt -) {} +) { + public static TenantResponse from(TenantEntity e) { + return new TenantResponse( + e.getId(), e.getName(), e.getSlug(), + e.getTier().name(), e.getStatus().name(), + e.getServerEndpoint(), e.getProvisionError(), + e.getCreatedAt(), e.getUpdatedAt() + ); + } +} diff --git a/src/main/resources/db/migration/V011__add_provisioning_fields.sql b/src/main/resources/db/migration/V011__add_provisioning_fields.sql new file mode 100644 index 0000000..9623f1b --- /dev/null +++ b/src/main/resources/db/migration/V011__add_provisioning_fields.sql @@ -0,0 +1,3 @@ +-- V011__add_provisioning_fields.sql +ALTER TABLE tenants ADD COLUMN server_endpoint VARCHAR(512); +ALTER TABLE tenants ADD COLUMN provision_error TEXT;