feat: add server upgrade action — force-pull latest images and re-provision
All checks were successful
CI / build (push) Successful in 1m19s
CI / docker (push) Successful in 48s

Restart only stops/starts existing containers with the same image. The new
upgrade action removes server + UI containers, force-pulls the latest
Docker images, then re-provisions (preserving app containers, volumes, and
networks). Available to both vendor (tenant detail) and tenant admin
(dashboard).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-11 10:45:45 +02:00
parent d5eead888d
commit e2e5c794a2
11 changed files with 122 additions and 5 deletions

View File

@@ -9,7 +9,7 @@ import {
Spinner,
useToast,
} from '@cameleer/design-system';
import { ArrowLeft, RefreshCw, Trash2 } from 'lucide-react';
import { ArrowLeft, ArrowUpCircle, RefreshCw, Trash2 } from 'lucide-react';
import {
useVendorTenant,
useSuspendTenant,
@@ -17,6 +17,7 @@ import {
useDeleteTenant,
useRenewLicense,
useRestartServer,
useUpgradeServer,
} from '../../api/vendor-hooks';
import { ServerStatusBadge } from '../../components/ServerStatusBadge';
import { tierColor } from '../../utils/tier';
@@ -69,6 +70,7 @@ export function TenantDetailPage() {
const deleteTenant = useDeleteTenant();
const renewLicense = useRenewLicense();
const restartServer = useRestartServer();
const upgradeServer = useUpgradeServer();
const [deleteOpen, setDeleteOpen] = useState(false);
@@ -117,6 +119,16 @@ export function TenantDetailPage() {
}
}
async function handleUpgrade() {
if (!id) return;
try {
await upgradeServer.mutateAsync(id);
toast({ title: 'Server upgrade started — pulling latest images', variant: 'success' });
} catch (err) {
toast({ title: 'Upgrade failed', description: String(err), variant: 'error' });
}
}
async function handleDelete() {
if (!id) return;
try {
@@ -267,6 +279,14 @@ export function TenantDetailPage() {
<RefreshCw size={14} style={{ marginRight: 6 }} />
Restart Server
</Button>
<Button
variant="secondary"
onClick={handleUpgrade}
loading={upgradeServer.isPending}
>
<ArrowUpCircle size={14} style={{ marginRight: 6 }} />
Upgrade Server
</Button>
<Button
variant="secondary"
onClick={handleSuspendToggle}