show id in credential regen

This commit is contained in:
miloschwartz
2025-12-06 12:07:43 -05:00
parent 1d303feca2
commit 00174be8c0
6 changed files with 23795 additions and 23572 deletions

View File

@@ -6,6 +6,7 @@ import {
SettingsSection,
SettingsSectionBody,
SettingsSectionDescription,
SettingsSectionFooter,
SettingsSectionHeader,
SettingsSectionTitle
} from "@app/components/Settings";
@@ -21,11 +22,20 @@ import {
QuickStartRemoteExitNodeResponse
} from "@server/routers/remoteExitNode/types";
import { useRemoteExitNodeContext } from "@app/hooks/useRemoteExitNodeContext";
import RegenerateCredentialsModal from "@app/components/RegenerateCredentialsModal";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { build } from "@server/build";
import { SecurityFeaturesAlert } from "@app/components/SecurityFeaturesAlert";
import {
InfoSection,
InfoSectionContent,
InfoSections,
InfoSectionTitle
} from "@app/components/InfoSection";
import CopyToClipboard from "@app/components/CopyToClipboard";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { InfoIcon } from "lucide-react";
export default function CredentialsPage() {
const { env } = useEnvContext();
@@ -38,6 +48,13 @@ export default function CredentialsPage() {
const [modalOpen, setModalOpen] = useState(false);
const [credentials, setCredentials] =
useState<PickRemoteExitNodeDefaultsResponse | null>(null);
const [currentRemoteExitNodeId, setCurrentRemoteExitNodeId] = useState<
string | null
>(remoteExitNode.remoteExitNodeId);
const [regeneratedSecret, setRegeneratedSecret] = useState<string | null>(
null
);
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
const subscription = useSubscriptionStatusContext();
@@ -50,39 +67,62 @@ export default function CredentialsPage() {
};
const handleConfirmRegenerate = async () => {
const response = await api.get<
AxiosResponse<PickRemoteExitNodeDefaultsResponse>
>(`/org/${orgId}/pick-remote-exit-node-defaults`);
try {
const response = await api.get<
AxiosResponse<PickRemoteExitNodeDefaultsResponse>
>(`/org/${orgId}/pick-remote-exit-node-defaults`);
const data = response.data.data;
setCredentials(data);
const data = response.data.data;
setCredentials(data);
await api.put<AxiosResponse<QuickStartRemoteExitNodeResponse>>(
`/re-key/${orgId}/regenerate-remote-exit-node-secret`,
{
const rekeyRes = await api.put<
AxiosResponse<QuickStartRemoteExitNodeResponse>
>(`/re-key/${orgId}/regenerate-remote-exit-node-secret`, {
remoteExitNodeId: remoteExitNode.remoteExitNodeId,
secret: data.secret
});
if (rekeyRes && rekeyRes.status === 200) {
const rekeyData = rekeyRes.data.data;
if (rekeyData && rekeyData.remoteExitNodeId) {
setCurrentRemoteExitNodeId(rekeyData.remoteExitNodeId);
setRegeneratedSecret(data.secret);
setCredentials({
...data,
remoteExitNodeId: rekeyData.remoteExitNodeId
});
setShowCredentialsAlert(true);
}
}
);
toast({
title: t("credentialsSaved"),
description: t("credentialsSavedDescription")
});
router.refresh();
};
const getCredentials = () => {
if (credentials) {
return {
Id: remoteExitNode.remoteExitNodeId,
Secret: credentials.secret
};
toast({
title: t("credentialsSaved"),
description: t("credentialsSavedDescription")
});
} catch (error) {
toast({
variant: "destructive",
title: t("error") || "Error",
description:
formatAxiosError(error) ||
t("credentialsRegenerateError") ||
"Failed to regenerate credentials"
});
}
return undefined;
};
const getConfirmationString = () => {
return (
remoteExitNode?.name ||
remoteExitNode?.remoteExitNodeId ||
"My remote exit node"
);
};
const displayRemoteExitNodeId =
currentRemoteExitNodeId || remoteExitNode?.remoteExitNodeId || null;
const displaySecret = regeneratedSecret || null;
return (
<>
<SettingsContainer>
@@ -95,26 +135,96 @@ export default function CredentialsPage() {
{t("regenerateCredentials")}
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
<SecurityFeaturesAlert />
<InfoSections cols={3}>
<InfoSection>
<InfoSectionTitle>
{t("endpoint") || "Endpoint"}
</InfoSectionTitle>
<InfoSectionContent>
<CopyToClipboard
text={env.app.dashboardUrl}
/>
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("remoteExitNodeId") ||
"Remote Exit Node ID"}
</InfoSectionTitle>
<InfoSectionContent>
{displayRemoteExitNodeId ? (
<CopyToClipboard
text={displayRemoteExitNodeId}
/>
) : (
<span>{"••••••••••••••••"}</span>
)}
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("secretKey") || "Secret Key"}
</InfoSectionTitle>
<InfoSectionContent>
{displaySecret ? (
<CopyToClipboard text={displaySecret} />
) : (
<span>
{"••••••••••••••••••••••••••••••••"}
</span>
)}
</InfoSectionContent>
</InfoSection>
</InfoSections>
{showCredentialsAlert && displaySecret && (
<Alert variant="neutral" className="mt-4">
<InfoIcon className="h-4 w-4" />
<AlertTitle className="font-semibold">
{t("credentialsSave") ||
"Save the Credentials"}
</AlertTitle>
<AlertDescription>
{t("credentialsSaveDescription") ||
"You will only be able to see this once. Make sure to copy it to a secure place."}
</AlertDescription>
</Alert>
)}
</SettingsSectionBody>
<SettingsSectionFooter>
<Button
onClick={() => setModalOpen(true)}
disabled={isSecurityFeatureDisabled()}
>
{t("regeneratecredentials")}
{t("regenerateCredentialsButton")}
</Button>
</SettingsSectionBody>
</SettingsSectionFooter>
</SettingsSection>
</SettingsContainer>
<RegenerateCredentialsModal
<ConfirmDeleteDialog
open={modalOpen}
onOpenChange={setModalOpen}
type="remote-exit-node"
onConfirmRegenerate={handleConfirmRegenerate}
dashboardUrl={env.app.dashboardUrl}
credentials={getCredentials()}
setOpen={(val) => {
setModalOpen(val);
// Prevent modal from reopening during refresh
if (!val) {
setTimeout(() => {
router.refresh();
}, 150);
}
}}
dialog={
<div className="space-y-2">
<p>{t("regenerateCredentialsConfirmation")}</p>
<p>{t("regenerateCredentialsWarning")}</p>
</div>
}
buttonText={t("regenerateCredentialsButton")}
onConfirm={handleConfirmRegenerate}
string={getConfirmationString()}
title={t("regenerateCredentials")}
warningText={t("cannotbeUndone")}
/>
</>
);

View File

@@ -44,7 +44,7 @@ export default function CredentialsPage() {
const [modalOpen, setModalOpen] = useState(false);
const [clientDefaults, setClientDefaults] =
useState<PickClientDefaultsResponse | null>(null);
const [currentOlmId, setCurrentOlmId] = useState<string | null>(null);
const [currentOlmId, setCurrentOlmId] = useState<string | null>(client.olmId);
const [regeneratedSecret, setRegeneratedSecret] = useState<string | null>(
null
);
@@ -154,7 +154,7 @@ export default function CredentialsPage() {
{displaySecret ? (
<CopyToClipboard text={displaySecret} />
) : (
<span>{"••••••••••••••••"}</span>
<span>{"••••••••••••••••••••••••••••••••"}</span>
)}
</InfoSectionContent>
</InfoSection>

View File

@@ -53,7 +53,7 @@ export default function CredentialsPage() {
useState<PickSiteDefaultsResponse | null>(null);
const [wgConfig, setWgConfig] = useState("");
const [publicKey, setPublicKey] = useState("");
const [currentNewtId, setCurrentNewtId] = useState<string | null>(null);
const [currentNewtId, setCurrentNewtId] = useState<string | null>(site.newtId);
const [regeneratedSecret, setRegeneratedSecret] = useState<string | null>(
null
);
@@ -233,7 +233,7 @@ export default function CredentialsPage() {
text={displaySecret}
/>
) : (
<span>{"••••••••••••••••"}</span>
<span>{"••••••••••••••••••••••••••••••••"}</span>
)}
</InfoSectionContent>
</InfoSection>