Files
ai-showcase-platform/app/admin/database-shutdown/page.tsx

268 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React, { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Loader2, Database, Power, AlertTriangle, CheckCircle } from 'lucide-react';
interface ShutdownStatus {
isShuttingDown: boolean;
handlerCount: number;
registeredHandlers: string[];
}
export default function DatabaseShutdownPage() {
const [status, setStatus] = useState<ShutdownStatus | null>(null);
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState<string>('');
const [error, setError] = useState<string>('');
// 獲取關閉狀態
const fetchStatus = async () => {
try {
setLoading(true);
const response = await fetch('/api/test-shutdown?action=status');
const data = await response.json();
if (data.success) {
setStatus(data.data);
setMessage('狀態更新成功');
setError('');
} else {
setError(data.error || '獲取狀態失敗');
}
} catch (err) {
setError('網路錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
} finally {
setLoading(false);
}
};
// 測試關閉機制
const testShutdown = async () => {
try {
setLoading(true);
const response = await fetch('/api/test-shutdown?action=test');
const data = await response.json();
if (data.success) {
setMessage('關閉機制測試成功');
setError('');
await fetchStatus(); // 重新獲取狀態
} else {
setError(data.error || '測試失敗');
}
} catch (err) {
setError('測試錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
} finally {
setLoading(false);
}
};
// 強制關閉測試
const forceShutdown = async () => {
if (!confirm('確定要執行強制關閉測試嗎?這可能會影響應用程式運行!')) {
return;
}
try {
setLoading(true);
const response = await fetch('/api/test-shutdown?action=force');
const data = await response.json();
if (data.success) {
setMessage('強制關閉測試完成');
setError('');
await fetchStatus(); // 重新獲取狀態
} else {
setError(data.error || '強制關閉測試失敗');
}
} catch (err) {
setError('強制關閉錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
} finally {
setLoading(false);
}
};
// 優雅關閉測試
const gracefulShutdown = async () => {
if (!confirm('確定要執行優雅關閉測試嗎?這會關閉所有資料庫連線!')) {
return;
}
try {
setLoading(true);
const response = await fetch('/api/test-shutdown', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'graceful' }),
});
const data = await response.json();
if (data.success) {
setMessage('優雅關閉測試完成');
setError('');
await fetchStatus(); // 重新獲取狀態
} else {
setError(data.error || '優雅關閉測試失敗');
}
} catch (err) {
setError('優雅關閉錯誤: ' + (err instanceof Error ? err.message : '未知錯誤'));
} finally {
setLoading(false);
}
};
// 組件載入時獲取狀態
useEffect(() => {
fetchStatus();
}, []);
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold"></h1>
<p className="text-muted-foreground">
</p>
</div>
<Button
onClick={fetchStatus}
disabled={loading}
variant="outline"
>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Database className="h-4 w-4" />}
</Button>
</div>
{/* 狀態顯示 */}
{status && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Database className="h-5 w-5" />
</CardTitle>
<CardDescription>
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">:</span>
<Badge variant={status.isShuttingDown ? "destructive" : "default"}>
{status.isShuttingDown ? "關閉中" : "正常"}
</Badge>
</div>
<div className="flex items-center gap-2">
<span className="text-sm font-medium">:</span>
<Badge variant="outline">{status.handlerCount}</Badge>
</div>
<div className="flex items-center gap-2">
<span className="text-sm font-medium">:</span>
<Badge variant={status.isShuttingDown ? "destructive" : "default"}>
{status.isShuttingDown ? "異常" : "正常"}
</Badge>
</div>
</div>
<div>
<span className="text-sm font-medium">:</span>
<div className="mt-2 flex flex-wrap gap-2">
{status.registeredHandlers.map((handler, index) => (
<Badge key={index} variant="secondary">
{handler}
</Badge>
))}
</div>
</div>
</CardContent>
</Card>
)}
{/* 操作按鈕 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Power className="h-5 w-5" />
</CardTitle>
<CardDescription>
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Button
onClick={testShutdown}
disabled={loading}
variant="outline"
className="w-full"
>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <CheckCircle className="h-4 w-4" />}
</Button>
<Button
onClick={forceShutdown}
disabled={loading}
variant="destructive"
className="w-full"
>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <AlertTriangle className="h-4 w-4" />}
</Button>
<Button
onClick={gracefulShutdown}
disabled={loading}
variant="destructive"
className="w-full"
>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Power className="h-4 w-4" />}
</Button>
</div>
</CardContent>
</Card>
{/* 訊息顯示 */}
{message && (
<Alert>
<CheckCircle className="h-4 w-4" />
<AlertDescription>{message}</AlertDescription>
</Alert>
)}
{error && (
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* 說明資訊 */}
<Card>
<CardHeader>
<CardTitle>使</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-sm text-muted-foreground">
<p> <strong>:</strong> </p>
<p> <strong>:</strong> </p>
<p> <strong>:</strong> </p>
<p> SIGINT SIGTERM </p>
<p> </p>
</CardContent>
</Card>
</div>
);
}