feat: add license entity, repository, and database migration
Licenses table linked to tenants with JSONB features/limits, Ed25519 signed token storage, and revocation support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,5 +6,6 @@ public enum AuditAction {
|
||||
APP_CREATE, APP_DEPLOY, APP_PROMOTE, APP_ROLLBACK, APP_SCALE, APP_STOP, APP_DELETE,
|
||||
SECRET_CREATE, SECRET_READ, SECRET_UPDATE, SECRET_DELETE, SECRET_ROTATE,
|
||||
CONFIG_UPDATE,
|
||||
TEAM_INVITE, TEAM_REMOVE, TEAM_ROLE_CHANGE
|
||||
TEAM_INVITE, TEAM_REMOVE, TEAM_ROLE_CHANGE,
|
||||
LICENSE_GENERATE, LICENSE_REVOKE
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.siegeln.cameleer.saas.license;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.Table;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "licenses")
|
||||
public class LicenseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "tenant_id", nullable = false)
|
||||
private UUID tenantId;
|
||||
|
||||
@Column(name = "tier", nullable = false, length = 20)
|
||||
private String tier;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "features", nullable = false, columnDefinition = "jsonb")
|
||||
private Map<String, Object> features;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "limits", nullable = false, columnDefinition = "jsonb")
|
||||
private Map<String, Object> limits;
|
||||
|
||||
@Column(name = "issued_at", nullable = false)
|
||||
private Instant issuedAt;
|
||||
|
||||
@Column(name = "expires_at", nullable = false)
|
||||
private Instant expiresAt;
|
||||
|
||||
@Column(name = "revoked_at")
|
||||
private Instant revokedAt;
|
||||
|
||||
@Column(name = "token", nullable = false, columnDefinition = "text")
|
||||
private String token;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private Instant createdAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (createdAt == null) createdAt = Instant.now();
|
||||
if (issuedAt == null) issuedAt = Instant.now();
|
||||
}
|
||||
|
||||
public UUID getId() { return id; }
|
||||
public UUID getTenantId() { return tenantId; }
|
||||
public void setTenantId(UUID tenantId) { this.tenantId = tenantId; }
|
||||
public String getTier() { return tier; }
|
||||
public void setTier(String tier) { this.tier = tier; }
|
||||
public Map<String, Object> getFeatures() { return features; }
|
||||
public void setFeatures(Map<String, Object> features) { this.features = features; }
|
||||
public Map<String, Object> getLimits() { return limits; }
|
||||
public void setLimits(Map<String, Object> limits) { this.limits = limits; }
|
||||
public Instant getIssuedAt() { return issuedAt; }
|
||||
public void setIssuedAt(Instant issuedAt) { this.issuedAt = issuedAt; }
|
||||
public Instant getExpiresAt() { return expiresAt; }
|
||||
public void setExpiresAt(Instant expiresAt) { this.expiresAt = expiresAt; }
|
||||
public Instant getRevokedAt() { return revokedAt; }
|
||||
public void setRevokedAt(Instant revokedAt) { this.revokedAt = revokedAt; }
|
||||
public String getToken() { return token; }
|
||||
public void setToken(String token) { this.token = token; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.siegeln.cameleer.saas.license;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface LicenseRepository extends JpaRepository<LicenseEntity, UUID> {
|
||||
List<LicenseEntity> findByTenantIdOrderByCreatedAtDesc(UUID tenantId);
|
||||
Optional<LicenseEntity> findFirstByTenantIdAndRevokedAtIsNullOrderByCreatedAtDesc(UUID tenantId);
|
||||
}
|
||||
15
src/main/resources/db/migration/V006__create_licenses.sql
Normal file
15
src/main/resources/db/migration/V006__create_licenses.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE licenses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
tier VARCHAR(20) NOT NULL,
|
||||
features JSONB NOT NULL DEFAULT '{}',
|
||||
limits JSONB NOT NULL DEFAULT '{}',
|
||||
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
token TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_licenses_tenant_id ON licenses (tenant_id);
|
||||
CREATE INDEX idx_licenses_expires_at ON licenses (expires_at);
|
||||
Reference in New Issue
Block a user