318 lines
12 KiB
TypeScript
318 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from "@workspace/ui/components/alert-dialog";
|
|
import { Badge } from "@workspace/ui/components/badge";
|
|
import { Button } from "@workspace/ui/components/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@workspace/ui/components/card";
|
|
import { Icon } from "@workspace/ui/composed/icon";
|
|
import { getModuleConfig } from "@workspace/ui/services/admin/system";
|
|
import { restartSystem } from "@workspace/ui/services/admin/tool";
|
|
import { basicCheckServiceVersion } from "@workspace/ui/services/gateway/basicCheckServiceVersion";
|
|
import { basicUpdateService } from "@workspace/ui/services/gateway/basicUpdateService";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { toast } from "sonner";
|
|
import packageJson from "../../../../../../package.json";
|
|
import SystemLogsDialog from "./system-logs-dialog";
|
|
|
|
export default function SystemVersionCard() {
|
|
const { t } = useTranslation("tool");
|
|
const queryClient = useQueryClient();
|
|
const [openRestart, setOpenRestart] = useState(false);
|
|
const [isRestarting, setIsRestarting] = useState(false);
|
|
const [openUpdateWeb, setOpenUpdateWeb] = useState(false);
|
|
const [openUpdateServer, setOpenUpdateServer] = useState(false);
|
|
const [isUpdatingWeb, setIsUpdatingWeb] = useState(false);
|
|
|
|
const { data: moduleConfig } = useQuery({
|
|
queryKey: ["getModuleConfig"],
|
|
queryFn: async () => {
|
|
const { data } = await getModuleConfig({ skipErrorHandler: true });
|
|
return data.data;
|
|
},
|
|
staleTime: 0,
|
|
});
|
|
|
|
const { data: serverVersionInfo } = useQuery({
|
|
queryKey: ["checkServerVersion", moduleConfig?.secret],
|
|
queryFn: async () => {
|
|
const { data } = await basicCheckServiceVersion(
|
|
{
|
|
service_name: moduleConfig!.service_name,
|
|
secret: moduleConfig!.secret,
|
|
},
|
|
{ skipErrorHandler: true }
|
|
);
|
|
return data.data;
|
|
},
|
|
enabled: !!moduleConfig?.secret,
|
|
staleTime: 0,
|
|
retry: 1,
|
|
});
|
|
|
|
const { data: webVersionInfo } = useQuery({
|
|
queryKey: ["checkWebVersion", moduleConfig?.secret],
|
|
queryFn: async () => {
|
|
const { data } = await basicCheckServiceVersion(
|
|
{
|
|
service_name: "admin",
|
|
secret: moduleConfig!.secret,
|
|
},
|
|
{ skipErrorHandler: true }
|
|
);
|
|
return data.data;
|
|
},
|
|
enabled: !!moduleConfig?.secret,
|
|
staleTime: 0,
|
|
retry: 1,
|
|
});
|
|
|
|
const updateServerMutation = useMutation({
|
|
mutationFn: async (serviceName: string) => {
|
|
await basicUpdateService({
|
|
service_name: serviceName,
|
|
secret: moduleConfig!.secret,
|
|
});
|
|
},
|
|
onSuccess: () => {
|
|
toast.success(t("updateSuccess", "Update completed successfully"));
|
|
queryClient.invalidateQueries({ queryKey: ["checkServerVersion"] });
|
|
queryClient.invalidateQueries({ queryKey: ["getModuleConfig"] });
|
|
setOpenUpdateServer(false);
|
|
},
|
|
onError: () => {
|
|
toast.error(t("updateFailed", "Update failed"));
|
|
},
|
|
});
|
|
|
|
const handleUpdateWeb = async () => {
|
|
if (!moduleConfig?.secret) return;
|
|
|
|
setIsUpdatingWeb(true);
|
|
try {
|
|
await basicUpdateService({
|
|
service_name: "admin",
|
|
secret: moduleConfig.secret,
|
|
});
|
|
toast.success(t("adminUpdateSuccess", "Admin updated successfully"));
|
|
|
|
await basicUpdateService({
|
|
service_name: "user",
|
|
secret: moduleConfig.secret,
|
|
});
|
|
toast.success(t("userUpdateSuccess", "User updated successfully"));
|
|
|
|
setOpenUpdateWeb(false);
|
|
window.location.reload();
|
|
} catch {
|
|
toast.error(t("updateFailed", "Update failed"));
|
|
} finally {
|
|
setIsUpdatingWeb(false);
|
|
}
|
|
};
|
|
|
|
const hasServerNewVersion = serverVersionInfo?.has_update ?? false;
|
|
const hasWebNewVersion = webVersionInfo?.has_update ?? false;
|
|
const isUpdatingServer = updateServerMutation.isPending;
|
|
|
|
return (
|
|
<Card className="gap-0 p-3">
|
|
<CardHeader className="mb-2 p-0">
|
|
<CardTitle className="flex items-center justify-between">
|
|
{t("systemServices", "System Services")}
|
|
<div className="flex items-center space-x-2">
|
|
<SystemLogsDialog size="sm" variant="outline" />
|
|
<AlertDialog onOpenChange={setOpenRestart} open={openRestart}>
|
|
<AlertDialogTrigger asChild>
|
|
<Button size="sm" variant="destructive">
|
|
{t("systemReboot", "System Reboot")}
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>
|
|
{t("confirmSystemReboot", "Confirm System Reboot")}
|
|
</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
{t(
|
|
"rebootDescription",
|
|
"Are you sure you want to reboot the system? This action cannot be undone."
|
|
)}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>{t("cancel", "Cancel")}</AlertDialogCancel>
|
|
<Button
|
|
disabled={isRestarting}
|
|
onClick={async () => {
|
|
setIsRestarting(true);
|
|
await restartSystem();
|
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
setIsRestarting(false);
|
|
setOpenRestart(false);
|
|
}}
|
|
>
|
|
{isRestarting && (
|
|
<Icon className="mr-2 animate-spin" icon="mdi:loading" />
|
|
)}
|
|
{isRestarting
|
|
? t("rebooting", "Rebooting...")
|
|
: t("confirmReboot", "Confirm Reboot")}
|
|
</Button>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</div>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3 p-0">
|
|
<div className="flex flex-1 items-center justify-between">
|
|
<div className="flex items-center">
|
|
<Icon className="mr-2 h-4 w-4 text-green-600" icon="mdi:web" />
|
|
<span className="font-medium text-sm">
|
|
{t("webVersion", "Web Version")}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge>V{packageJson.version}</Badge>
|
|
{hasWebNewVersion && webVersionInfo && (
|
|
<AlertDialog onOpenChange={setOpenUpdateWeb} open={openUpdateWeb}>
|
|
<AlertDialogTrigger asChild>
|
|
<Button
|
|
className="h-6 px-2 text-xs"
|
|
disabled={isUpdatingWeb}
|
|
size="sm"
|
|
variant="outline"
|
|
>
|
|
<Icon className="mr-1 h-3 w-3" icon="mdi:download" />
|
|
{t("update", "Update")} V{webVersionInfo.latest_version}
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>
|
|
{t("confirmUpdate", "Confirm Update")}
|
|
</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
{t(
|
|
"updateWebDescription",
|
|
"Are you sure you want to update the web version from V{{current}} to V{{latest}}?",
|
|
{
|
|
current: packageJson.version,
|
|
latest: webVersionInfo.latest_version,
|
|
}
|
|
)}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>
|
|
{t("cancel", "Cancel")}
|
|
</AlertDialogCancel>
|
|
<Button disabled={isUpdatingWeb} onClick={handleUpdateWeb}>
|
|
{isUpdatingWeb && (
|
|
<Icon
|
|
className="mr-2 animate-spin"
|
|
icon="mdi:loading"
|
|
/>
|
|
)}
|
|
{t("confirmUpdate", "Confirm Update")}
|
|
</Button>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-1 items-center justify-between">
|
|
<div className="flex items-center">
|
|
<Icon className="mr-2 h-4 w-4 text-blue-600" icon="mdi:server" />
|
|
<span className="font-medium text-sm">
|
|
{t("serverVersion", "Server Version")}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge>
|
|
V
|
|
{moduleConfig?.service_version ||
|
|
serverVersionInfo?.current_version ||
|
|
"1.0.0"}
|
|
</Badge>
|
|
{hasServerNewVersion && serverVersionInfo && moduleConfig && (
|
|
<AlertDialog
|
|
onOpenChange={setOpenUpdateServer}
|
|
open={openUpdateServer}
|
|
>
|
|
<AlertDialogTrigger asChild>
|
|
<Button
|
|
className="h-6 px-2 text-xs"
|
|
disabled={isUpdatingServer}
|
|
size="sm"
|
|
variant="outline"
|
|
>
|
|
<Icon className="mr-1 h-3 w-3" icon="mdi:download" />
|
|
{t("update", "Update")} V{serverVersionInfo.latest_version}
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>
|
|
{t("confirmUpdate", "Confirm Update")}
|
|
</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
{t(
|
|
"updateServerDescription",
|
|
"Are you sure you want to update the server version from V{{current}} to V{{latest}}?",
|
|
{
|
|
current:
|
|
moduleConfig.service_version ||
|
|
serverVersionInfo.current_version,
|
|
latest: serverVersionInfo.latest_version,
|
|
}
|
|
)}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>
|
|
{t("cancel", "Cancel")}
|
|
</AlertDialogCancel>
|
|
<Button
|
|
disabled={isUpdatingServer}
|
|
onClick={() =>
|
|
updateServerMutation.mutate(moduleConfig.service_name)
|
|
}
|
|
>
|
|
{isUpdatingServer && (
|
|
<Icon
|
|
className="mr-2 animate-spin"
|
|
icon="mdi:loading"
|
|
/>
|
|
)}
|
|
{t("confirmUpdate", "Confirm Update")}
|
|
</Button>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|