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 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-09 21:38:03 +02:00
parent 81d570fd63
commit ebba021448
5 changed files with 39 additions and 10 deletions

View File

@@ -52,6 +52,17 @@ public class LicenseService {
return licenseRepository.findFirstByTenantIdAndRevokedAtIsNullOrderByCreatedAtDesc(tenantId); 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. * 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, * Returns the license entity's metadata as a map if found and not expired/revoked,

View File

@@ -70,14 +70,6 @@ public class TenantController {
} }
private TenantResponse toResponse(TenantEntity entity) { private TenantResponse toResponse(TenantEntity entity) {
return new TenantResponse( return TenantResponse.from(entity);
entity.getId(),
entity.getName(),
entity.getSlug(),
entity.getTier().name(),
entity.getStatus().name(),
entity.getCreatedAt(),
entity.getUpdatedAt()
);
} }
} }

View File

@@ -52,6 +52,12 @@ public class TenantEntity {
@Column(name = "settings", columnDefinition = "jsonb") @Column(name = "settings", columnDefinition = "jsonb")
private Map<String, Object> settings = Map.of(); private Map<String, Object> 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) @Column(name = "created_at", nullable = false, updatable = false)
private Instant createdAt; private Instant createdAt;
@@ -87,6 +93,10 @@ public class TenantEntity {
public void setStripeSubscriptionId(String stripeSubscriptionId) { this.stripeSubscriptionId = stripeSubscriptionId; } public void setStripeSubscriptionId(String stripeSubscriptionId) { this.stripeSubscriptionId = stripeSubscriptionId; }
public Map<String, Object> getSettings() { return settings; } public Map<String, Object> getSettings() { return settings; }
public void setSettings(Map<String, Object> settings) { this.settings = settings; } public void setSettings(Map<String, Object> 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 getCreatedAt() { return createdAt; }
public Instant getUpdatedAt() { return updatedAt; } public Instant getUpdatedAt() { return updatedAt; }
} }

View File

@@ -1,5 +1,7 @@
package net.siegeln.cameleer.saas.tenant.dto; package net.siegeln.cameleer.saas.tenant.dto;
import net.siegeln.cameleer.saas.tenant.TenantEntity;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
@@ -9,6 +11,17 @@ public record TenantResponse(
String slug, String slug,
String tier, String tier,
String status, String status,
String serverEndpoint,
String provisionError,
Instant createdAt, Instant createdAt,
Instant updatedAt 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()
);
}
}

View File

@@ -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;