mirror of
https://github.com/dyzulk/trustlab.git
synced 2026-01-26 13:32:06 +07:00
feat: implement Renew All (Bulk CA Chain Refresh) functionality
This commit is contained in:
@@ -14,38 +14,10 @@ import PageLoader from "@/components/ui/PageLoader";
|
||||
import { useTranslations } from "next-intl";
|
||||
import CdnManagementCard from "@/components/admin/CdnManagementCard";
|
||||
import ArchiveManagementTable from "@/components/admin/ArchiveManagementTable";
|
||||
import Button from "@/components/ui/button/Button";
|
||||
|
||||
const fetcher = (url: string) => axios.get(url).then((res) => res.data);
|
||||
|
||||
function SyncButton({ onClick, disabled, isLoading, label, variant = 'blue' }: { onClick: () => void, disabled: boolean, isLoading: boolean, label: string, variant?: 'blue' | 'gray' | 'purple' | 'indigo' }) {
|
||||
const variantClasses = {
|
||||
blue: 'bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400',
|
||||
gray: 'bg-gray-600 hover:bg-gray-700 disabled:bg-gray-400',
|
||||
purple: 'bg-purple-600 hover:bg-purple-700 disabled:bg-purple-400',
|
||||
indigo: 'bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-400',
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-1.5 ${variantClasses[variant]} text-white text-xs font-medium rounded-lg transition-all shadow-sm active:scale-95`}
|
||||
>
|
||||
{isLoading ? (
|
||||
<svg className="animate-spin h-3.5 w-3.5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||
</svg>
|
||||
)}
|
||||
{isLoading ? "Syncing..." : label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RootCaManagementClient() {
|
||||
const t = useTranslations("RootCA");
|
||||
const { user } = useAuth();
|
||||
@@ -53,9 +25,11 @@ export default function RootCaManagementClient() {
|
||||
const { addToast } = useToast();
|
||||
const { data, error, mutate, isLoading } = useSWR("/api/admin/ca-certificates", fetcher);
|
||||
const [isRenewing, setIsRenewing] = useState(false);
|
||||
const [isBulkRenewing, setIsBulkRenewing] = useState(false);
|
||||
const [activeSync, setActiveSync] = useState<string | null>(null);
|
||||
const [isPromoting, setIsPromoting] = useState(false);
|
||||
const [confirmRenewUuid, setConfirmRenewUuid] = useState<string | null>(null);
|
||||
const [showBulkRenewConfirm, setShowBulkRenewConfirm] = useState(false);
|
||||
|
||||
// Redirect if not admin or owner (double security, backend also checks)
|
||||
const { isAdminOrOwner } = useAuth();
|
||||
@@ -125,6 +99,23 @@ export default function RootCaManagementClient() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRenewAll = async () => {
|
||||
setIsBulkRenewing(true);
|
||||
try {
|
||||
const response = await axios.post("/api/admin/ca-certificates/renew-all", { days: 3650 });
|
||||
if (response.data.status === "success") {
|
||||
addToast(t("toast_bulk_renew_success"), "success");
|
||||
mutate();
|
||||
setShowBulkRenewConfirm(false);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
addToast(err.response?.data?.message || t("toast_bulk_renew_failed"), "error");
|
||||
} finally {
|
||||
setIsBulkRenewing(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (error) return <div className="p-10 text-center text-error-500">{t("load_failed")}</div>;
|
||||
|
||||
const certificates = data?.data || [];
|
||||
@@ -133,6 +124,15 @@ export default function RootCaManagementClient() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between mb-2 gap-4">
|
||||
<PageBreadcrumb pageTitle={t("management_title")} />
|
||||
<Button
|
||||
variant="warning"
|
||||
size="sm"
|
||||
onClick={() => setShowBulkRenewConfirm(true)}
|
||||
loading={isBulkRenewing}
|
||||
disabled={isLoading || isRenewing || isPromoting}
|
||||
>
|
||||
{t("renew_all_button")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
@@ -196,6 +196,18 @@ export default function RootCaManagementClient() {
|
||||
variant="warning"
|
||||
requiredInput="RENEW"
|
||||
/>
|
||||
|
||||
<ConfirmationModal
|
||||
isOpen={showBulkRenewConfirm}
|
||||
onClose={() => setShowBulkRenewConfirm(false)}
|
||||
onConfirm={handleRenewAll}
|
||||
title={t("bulk_renew_modal_title")}
|
||||
message={t("bulk_renew_modal_msg")}
|
||||
isLoading={isBulkRenewing}
|
||||
confirmLabel={t("bulk_renew_confirm_label")}
|
||||
variant="warning"
|
||||
requiredInput="RENEW-ALL"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user