seperate credentials rekeying in modal for reuse

This commit is contained in:
Pallavi Kumari
2025-11-07 23:30:24 +05:30
parent 90e72c6aca
commit 2b8204fdc8
5 changed files with 391 additions and 523 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useEffect, useState } from "react";
import { useState } from "react";
import {
SettingsContainer,
SettingsSection,
@@ -10,9 +10,6 @@ import {
SettingsSectionTitle
} from "@app/components/Settings";
import { Button } from "@app/components/ui/button";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { InfoIcon } from "lucide-react";
import CopyTextBox from "@app/components/CopyTextBox";
import { createApiClient, formatAxiosError } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { toast } from "@app/hooks/useToast";
@@ -24,6 +21,7 @@ import {
QuickStartRemoteExitNodeResponse
} from "@server/routers/remoteExitNode/types";
import { useRemoteExitNodeContext } from "@app/hooks/useRemoteExitNodeContext";
import RegenerateCredentialsModal from "@app/components/RegenerateCredentialsModal";
export default function CredentialsPage() {
const { env } = useEnvContext();
@@ -31,79 +29,44 @@ export default function CredentialsPage() {
const { orgId } = useParams();
const router = useRouter();
const t = useTranslations();
const { remoteExitNode, updateRemoteExitNode } = useRemoteExitNodeContext();
const { remoteExitNode } = useRemoteExitNodeContext();
const [credentials, setCredentials] =
useState<PickRemoteExitNodeDefaultsResponse | null>(null);
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [credentials, setCredentials] = useState<PickRemoteExitNodeDefaultsResponse | null>(null);
// Clear credentials when user leaves/reloads
useEffect(() => {
const clearCreds = () => setCredentials(null);
window.addEventListener("beforeunload", clearCreds);
return () => window.removeEventListener("beforeunload", clearCreds);
}, []);
const handleConfirmRegenerate = async () => {
const response = await api.get<AxiosResponse<PickRemoteExitNodeDefaultsResponse>>(
`/org/${orgId}/pick-remote-exit-node-defaults`
);
const handleRegenerate = async () => {
try {
setLoading(true);
const response = await api.get<
AxiosResponse<PickRemoteExitNodeDefaultsResponse>
>(`/org/${orgId}/pick-remote-exit-node-defaults`);
const data = response.data.data;
setCredentials(data);
setCredentials(response.data.data);
toast({
title: t("success"),
description: t("Credentials generated successfully."),
});
} catch (error) {
toast({
title: t("error"),
description: formatAxiosError(
error,
t("Failed to generate credentials")
),
variant: "destructive",
});
} finally {
setLoading(false);
}
await api.put<AxiosResponse<QuickStartRemoteExitNodeResponse>>(
`/org/${orgId}/reGenerate-remote-exit-node-secret`,
{
remoteExitNodeId: remoteExitNode.remoteExitNodeId,
secret: data.secret,
}
);
toast({
title: t("credentialsSaved"),
description: t("credentialsSavedDescription")
});
router.refresh();
};
const handleSave = async () => {
if (!credentials) return;
try {
setSaving(true);
const response = await api.put<
AxiosResponse<QuickStartRemoteExitNodeResponse>
>(`/org/${orgId}/reGenerate-remote-exit-node-secret`, {
remoteExitNodeId: remoteExitNode.remoteExitNodeId,
secret: credentials.secret,
});
toast({
title: t("success"),
description: t("Credentials saved successfully."),
});
// For security, clear them from UI
setCredentials(null);
} catch (error) {
toast({
title: t("error"),
description: formatAxiosError(
error,
t("Failed to save credentials")
),
variant: "destructive",
});
} finally {
setSaving(false);
const getCredentials = () => {
if (credentials) {
return {
Id: remoteExitNode.remoteExitNodeId,
Secret: credentials.secret
};
}
return undefined;
};
return (
@@ -114,58 +77,25 @@ export default function CredentialsPage() {
{t("generatedcredentials")}
</SettingsSectionTitle>
<SettingsSectionDescription>
{t("regenerateClientCredentials")}
{t("regenerateCredentials")}
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
{!credentials ? (
<Button
onClick={handleRegenerate}
loading={loading}
disabled={loading}
>
{t("regeneratecredentials")}
</Button>
) : (
<>
<CopyTextBox
text={`managed:
id: "${remoteExitNode.remoteExitNodeId}"
secret: "${credentials.secret}"`}
/>
<Alert variant="neutral" className="mt-4">
<InfoIcon className="h-4 w-4" />
<AlertTitle className="font-semibold">
{t("copyandsavethesecredentials")}
</AlertTitle>
<AlertDescription>
{t(
"copyandsavethesecredentialsdescription"
)}
</AlertDescription>
</Alert>
<div className="flex justify-end mt-6 space-x-2">
<Button
variant="outline"
onClick={() => setCredentials(null)}
>
{t("cancel")}
</Button>
<Button
onClick={handleSave}
loading={saving}
disabled={saving}
>
{t("savecredentials")}
</Button>
</div>
</>
)}
<Button onClick={() => setModalOpen(true)}>
{t("regeneratecredentials")}
</Button>
</SettingsSectionBody>
</SettingsSection>
<RegenerateCredentialsModal
open={modalOpen}
onOpenChange={setModalOpen}
type="remote-exit-node"
onConfirmRegenerate={handleConfirmRegenerate}
dashboardUrl={env.app.dashboardUrl}
credentials={getCredentials()}
/>
</SettingsContainer>
);
}
}