feat: add restart server action for vendor and tenant
Vendor: POST /api/vendor/tenants/{id}/restart (platform:admin scope)
Tenant: POST /api/tenant/server/restart (tenant:manage scope)
Both call TenantProvisioner.stop() then start() on the server + UI
containers. Restart button on vendor TenantDetailPage (Actions card)
and tenant TenantDashboardPage (Server card). Allowed in any status
including PROVISIONING.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,12 @@ public class TenantPortalController {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@PostMapping("/server/restart")
|
||||
public ResponseEntity<Void> restartServer() {
|
||||
portalService.restartServer();
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping("/settings")
|
||||
public ResponseEntity<TenantPortalService.TenantSettingsData> getSettings() {
|
||||
return ResponseEntity.ok(portalService.getSettings());
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.siegeln.cameleer.saas.identity.LogtoManagementClient;
|
||||
import net.siegeln.cameleer.saas.identity.ServerApiClient;
|
||||
import net.siegeln.cameleer.saas.license.LicenseEntity;
|
||||
import net.siegeln.cameleer.saas.license.LicenseService;
|
||||
import net.siegeln.cameleer.saas.provisioning.TenantProvisioner;
|
||||
import net.siegeln.cameleer.saas.tenant.TenantEntity;
|
||||
import net.siegeln.cameleer.saas.tenant.TenantService;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -22,15 +23,18 @@ public class TenantPortalService {
|
||||
private final LicenseService licenseService;
|
||||
private final ServerApiClient serverApiClient;
|
||||
private final LogtoManagementClient logtoClient;
|
||||
private final TenantProvisioner tenantProvisioner;
|
||||
|
||||
public TenantPortalService(TenantService tenantService,
|
||||
LicenseService licenseService,
|
||||
ServerApiClient serverApiClient,
|
||||
LogtoManagementClient logtoClient) {
|
||||
LogtoManagementClient logtoClient,
|
||||
TenantProvisioner tenantProvisioner) {
|
||||
this.tenantService = tenantService;
|
||||
this.licenseService = licenseService;
|
||||
this.serverApiClient = serverApiClient;
|
||||
this.logtoClient = logtoClient;
|
||||
this.tenantProvisioner = tenantProvisioner;
|
||||
}
|
||||
|
||||
// --- Inner records ---
|
||||
@@ -160,4 +164,12 @@ public class TenantPortalService {
|
||||
tenant.getServerEndpoint(), tenant.getCreatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public void restartServer() {
|
||||
TenantEntity tenant = resolveTenant();
|
||||
if (tenantProvisioner.isAvailable()) {
|
||||
tenantProvisioner.stop(tenant.getSlug());
|
||||
tenantProvisioner.start(tenant.getSlug());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,16 @@ public class VendorTenantController {
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/restart")
|
||||
public ResponseEntity<Void> restart(@PathVariable UUID id) {
|
||||
try {
|
||||
vendorTenantService.restartServer(id);
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/suspend")
|
||||
public ResponseEntity<TenantResponse> suspend(@PathVariable UUID id,
|
||||
@AuthenticationPrincipal Jwt jwt) {
|
||||
|
||||
@@ -213,6 +213,15 @@ public class VendorTenantService {
|
||||
return serverApiClient.getHealth(endpoint);
|
||||
}
|
||||
|
||||
public void restartServer(UUID tenantId) {
|
||||
TenantEntity tenant = tenantService.getById(tenantId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Tenant not found"));
|
||||
if (tenantProvisioner.isAvailable()) {
|
||||
tenantProvisioner.stop(tenant.getSlug());
|
||||
tenantProvisioner.start(tenant.getSlug());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TenantEntity suspend(UUID tenantId, UUID actorId) {
|
||||
TenantEntity tenant = tenantService.getById(tenantId)
|
||||
|
||||
Reference in New Issue
Block a user