Skip to content

Commit 6aaee1a

Browse files
cleanup
1 parent cd568da commit 6aaee1a

File tree

5 files changed

+123
-109
lines changed

5 files changed

+123
-109
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ packages/*/typedoc/*
3030
storybook-static
3131
.aider*
3232

33-
tsconfig.tsbuildinfo
33+
tsconfig.tsbuildinfo
34+
.cursor

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/engine/vault/components/create-vault-account.client.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export default function CreateVaultAccountButton(props: { project: Project }) {
100100
const blob = new Blob([fileContent], { type: "text/plain;charset=utf-8" });
101101
const url = URL.createObjectURL(blob);
102102
const link = document.createElement("a");
103-
const filename = `${props.project.name}-${props.project.publishableKey}-vault-keys.txt`;
103+
const filename = `${props.project.name}-vault-keys.txt`;
104104
link.href = url;
105105
link.download = filename;
106106
document.body.appendChild(link); // Required for Firefox
@@ -122,6 +122,7 @@ export default function CreateVaultAccountButton(props: { project: Project }) {
122122
setModalOpen(false);
123123
setKeysConfirmed(false);
124124
setKeysDownloaded(false);
125+
initialiseProjectWithVaultMutation.reset();
125126
// invalidate the page to force a reload
126127
router.refresh();
127128
};

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/engine/vault/components/key-management.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Project } from "@/api/projects";
22
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
33
import { InfoIcon } from "lucide-react";
44
import Link from "next/link";
5-
import RotateAdminKeyButton from "../../server-wallets/components/rotate-admin-key.client";
5+
import RotateAdminKeyButton from "./rotate-admin-key.client";
66
import CreateVaultAccountButton from "./create-vault-account.client";
77
import ListAccessTokens from "./list-access-tokens.client";
88

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/engine/vault/components/list-access-tokens.client.tsx

Lines changed: 53 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"use client";
22
import type { Project } from "@/api/projects";
33
import { CopyTextButton } from "@/components/ui/CopyTextButton";
4-
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
4+
import {} from "@/components/ui/alert";
55
import { Button } from "@/components/ui/button";
6-
import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox";
6+
import {} from "@/components/ui/checkbox";
77
import {
88
Dialog,
99
DialogContent,
@@ -12,7 +12,7 @@ import {
1212
} from "@/components/ui/dialog";
1313
import { Input } from "@/components/ui/input";
1414
import { Skeleton } from "@/components/ui/skeleton";
15-
import { useMutation, useQuery } from "@tanstack/react-query";
15+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
1616
import { listAccessTokens, revokeAccessToken } from "@thirdweb-dev/vault-sdk";
1717
import { Loader2, LockIcon, Trash2 } from "lucide-react";
1818
import { useState } from "react";
@@ -31,8 +31,7 @@ export default function ListAccessTokens(props: {
3131
const [typedAdminKey, setTypedAdminKey] = useState("");
3232
const [adminKey, setAdminKey] = useState("");
3333
const [deletingTokenId, setDeletingTokenId] = useState<string | null>(null);
34-
const [newAccessToken, setNewAccessToken] = useState<string | null>(null);
35-
const [keysConfirmed, setKeysConfirmed] = useState(false);
34+
const queryClient = useQueryClient();
3635
// TODO allow passing permissions to the access token
3736
const createAccessTokenMutation = useMutation({
3837
mutationFn: async (args: { adminKey: string }) => {
@@ -57,9 +56,10 @@ export default function ListAccessTokens(props: {
5756
onError: (error) => {
5857
toast.error(error.message);
5958
},
60-
onSuccess: (data) => {
61-
setNewAccessToken(data.userAccessToken.accessToken);
62-
listAccessTokensQuery.refetch();
59+
onSuccess: () => {
60+
queryClient.invalidateQueries({
61+
queryKey: ["list-access-tokens"],
62+
});
6363
},
6464
});
6565

@@ -95,7 +95,9 @@ export default function ListAccessTokens(props: {
9595
setDeletingTokenId(null);
9696
},
9797
onSuccess: () => {
98-
listAccessTokensQuery.refetch();
98+
queryClient.invalidateQueries({
99+
queryKey: ["list-access-tokens"],
100+
});
99101
setDeletingTokenId(null);
100102
},
101103
});
@@ -171,6 +173,9 @@ export default function ListAccessTokens(props: {
171173
setModalOpen(true);
172174
} else {
173175
setAdminKey("");
176+
queryClient.invalidateQueries({
177+
queryKey: ["list-access-tokens"],
178+
});
174179
}
175180
}}
176181
variant={"primary"}
@@ -281,98 +286,47 @@ export default function ListAccessTokens(props: {
281286
<DialogHeader className="px-6 pt-6">
282287
<DialogTitle>Unlock Vault</DialogTitle>
283288
</DialogHeader>
284-
{/* If new access token, show copy button */}
285-
{newAccessToken ? (
286-
<div className="flex flex-col gap-4">
287-
<div className="flex flex-col gap-4 px-6 ">
288-
<p className="text-muted-foreground text-xs">
289-
Here's your new access token. Store it securely as it will
290-
not be displayed again.
291-
</p>
292-
<CopyTextButton
293-
textToCopy={newAccessToken}
294-
className="!h-auto w-full justify-between bg-background px-3 py-3 font-mono text-xs"
295-
textToShow={maskSecret(newAccessToken)}
296-
copyIconPosition="right"
297-
tooltip="Copy Vault Access Token"
298-
/>
299-
<p className="text-muted-foreground text-xs">
300-
This access token is used to sign transactions and messages
301-
from your backend. Can be revoked and recreated with your
302-
admin key.
303-
</p>
304-
<Alert variant="destructive">
305-
<AlertTitle>Secure your keys</AlertTitle>
306-
<AlertDescription>
307-
These keys will not be displayed again. Store them
308-
securely as they provide access to your server wallets.
309-
</AlertDescription>
310-
<div className="h-4" />
311-
<CheckboxWithLabel className="text-foreground">
312-
<Checkbox
313-
checked={keysConfirmed}
314-
onCheckedChange={(v) => setKeysConfirmed(!!v)}
315-
/>
316-
I confirm that I've securely stored these keys
317-
</CheckboxWithLabel>
318-
</Alert>
319-
</div>
320-
<div className="flex justify-end gap-3 border-t bg-card px-6 py-4">
321-
<Button
322-
onClick={() => {
323-
setNewAccessToken(null);
324-
setKeysConfirmed(false);
325-
}}
326-
disabled={!keysConfirmed}
327-
variant={"primary"}
328-
>
329-
Go Back
330-
</Button>
331-
</div>
332-
</div>
333-
) : (
334-
<div className="flex flex-col gap-4">
335-
<div className="flex flex-col gap-4 px-6">
336-
<p className="flex items-center gap-2 text-sm text-warning-text">
337-
<LockIcon className="h-4 w-4" /> This action requires your
338-
Vault admin key.
339-
</p>
340-
<Input
341-
type="password"
342-
placeholder="sa_adm_ABCD_1234..."
343-
value={typedAdminKey}
344-
onChange={(e) => setTypedAdminKey(e.target.value)}
345-
onKeyDown={(e) => {
346-
if (e.key === "Enter") {
347-
setAdminKey(typedAdminKey);
348-
}
349-
}}
350-
/>
351-
</div>
352-
<div className="flex justify-end gap-3 border-t bg-card px-6 py-4">
353-
<Button
354-
variant={"outline"}
355-
onClick={() => {
356-
setAdminKey("");
357-
setTypedAdminKey("");
358-
setModalOpen(false);
359-
}}
360-
>
361-
Cancel
362-
</Button>
363-
<Button
364-
variant={"primary"}
365-
onClick={() => {
289+
<div className="flex flex-col gap-4">
290+
<div className="flex flex-col gap-4 px-6">
291+
<p className="flex items-center gap-2 text-sm text-warning-text">
292+
<LockIcon className="h-4 w-4" /> This action requires your
293+
Vault admin key.
294+
</p>
295+
<Input
296+
type="password"
297+
placeholder="sa_adm_ABCD_1234..."
298+
value={typedAdminKey}
299+
onChange={(e) => setTypedAdminKey(e.target.value)}
300+
onKeyDown={(e) => {
301+
if (e.key === "Enter") {
366302
setAdminKey(typedAdminKey);
367-
handleCloseModal();
368-
}}
369-
disabled={!typedAdminKey || listAccessTokensQuery.isLoading}
370-
>
371-
Unlock Vault
372-
</Button>
373-
</div>
303+
}
304+
}}
305+
/>
374306
</div>
375-
)}
307+
<div className="flex justify-end gap-3 border-t bg-card px-6 py-4">
308+
<Button
309+
variant={"outline"}
310+
onClick={() => {
311+
setAdminKey("");
312+
setTypedAdminKey("");
313+
setModalOpen(false);
314+
}}
315+
>
316+
Cancel
317+
</Button>
318+
<Button
319+
variant={"primary"}
320+
onClick={() => {
321+
setAdminKey(typedAdminKey);
322+
handleCloseModal();
323+
}}
324+
disabled={!typedAdminKey || listAccessTokensQuery.isLoading}
325+
>
326+
Unlock Vault
327+
</Button>
328+
</div>
329+
</div>
376330
</DialogContent>
377331
</Dialog>
378332
</div>
Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
DialogHeader,
1414
DialogTitle,
1515
} from "@/components/ui/dialog";
16+
import { useDashboardRouter } from "@/lib/DashboardRouter";
1617
import { cn } from "@/lib/utils";
1718
import { useMutation } from "@tanstack/react-query";
1819
import { rotateServiceAccount } from "@thirdweb-dev/vault-sdk";
@@ -25,11 +26,11 @@ import {
2526
initVaultClient,
2627
maskSecret,
2728
} from "../../lib/vault.client";
28-
import { useDashboardRouter } from "@/lib/DashboardRouter";
2929

3030
export default function RotateAdminKeyButton(props: { project: Project }) {
3131
const [modalOpen, setModalOpen] = useState(false);
3232
const [keysConfirmed, setKeysConfirmed] = useState(false);
33+
const [keysDownloaded, setKeysDownloaded] = useState(false);
3334
const router = useDashboardRouter();
3435

3536
const rotateAdminKeyMutation = useMutation({
@@ -41,8 +42,6 @@ export default function RotateAdminKeyButton(props: { project: Project }) {
4142
(service) => service.name === "engineCloud",
4243
)?.rotationCode;
4344

44-
console.log("DEBUG", rotationCode);
45-
4645
if (!rotationCode) {
4746
throw new Error("Rotation code not found");
4847
}
@@ -94,17 +93,50 @@ export default function RotateAdminKeyButton(props: { project: Project }) {
9493
},
9594
});
9695

96+
const handleDownloadKeys = () => {
97+
if (!rotateAdminKeyMutation.data) {
98+
return;
99+
}
100+
101+
const fileContent = `Project: ${props.project.name} (${props.project.publishableKey})\nVault Admin Key: ${rotateAdminKeyMutation.data.adminKey}\nVault Access Token: ${rotateAdminKeyMutation.data.userAccessToken.accessToken}\n`;
102+
const blob = new Blob([fileContent], { type: "text/plain;charset=utf-8" });
103+
const url = URL.createObjectURL(blob);
104+
const link = document.createElement("a");
105+
const filename = `${props.project.name}-vault-keys-rotated.txt`;
106+
link.href = url;
107+
link.download = filename;
108+
document.body.appendChild(link); // Required for Firefox
109+
link.click();
110+
111+
// Clean up
112+
document.body.removeChild(link);
113+
URL.revokeObjectURL(url);
114+
115+
toast.success(`Keys downloaded as ${filename}`);
116+
setKeysDownloaded(true);
117+
};
118+
97119
const handleCloseModal = () => {
98120
if (!keysConfirmed) {
99121
return;
100122
}
123+
101124
setModalOpen(false);
102125
setKeysConfirmed(false);
126+
setKeysDownloaded(false);
127+
// invalidate the page to force a reload
103128
rotateAdminKeyMutation.reset();
104-
// invalidate the page to force a reload of the project data
105129
router.refresh();
106130
};
107131

132+
const handlePrimaryButton = () => {
133+
if (!keysDownloaded) {
134+
handleDownloadKeys();
135+
return;
136+
}
137+
handleCloseModal();
138+
};
139+
108140
const isLoading = rotateAdminKeyMutation.isPending;
109141

110142
return (
@@ -164,6 +196,32 @@ export default function RotateAdminKeyButton(props: { project: Project }) {
164196
</p>
165197
</div>
166198
</div>
199+
200+
<div>
201+
<h3 className="mb-2 font-medium text-sm">
202+
New Vault Access Token
203+
</h3>
204+
<div className="flex flex-col gap-2 ">
205+
<CopyTextButton
206+
textToCopy={
207+
rotateAdminKeyMutation.data.userAccessToken
208+
.accessToken
209+
}
210+
className="!h-auto w-full justify-between bg-background px-3 py-3 font-mono text-xs"
211+
textToShow={maskSecret(
212+
rotateAdminKeyMutation.data.userAccessToken
213+
.accessToken,
214+
)}
215+
copyIconPosition="right"
216+
tooltip="Copy Vault Access Token"
217+
/>
218+
<p className="text-muted-foreground text-xs">
219+
This access token is used to sign transactions and
220+
messages from your backend. Can be revoked and recreated
221+
with your admin key.
222+
</p>
223+
</div>
224+
</div>
167225
</div>
168226
<Alert variant="destructive">
169227
<AlertTitle>Secure your keys</AlertTitle>
@@ -184,11 +242,11 @@ export default function RotateAdminKeyButton(props: { project: Project }) {
184242

185243
<div className="flex justify-end gap-3 border-t bg-card px-6 py-4">
186244
<Button
187-
onClick={handleCloseModal}
188-
disabled={!keysConfirmed}
245+
onClick={handlePrimaryButton}
246+
disabled={keysDownloaded && !keysConfirmed}
189247
variant="primary"
190248
>
191-
Close
249+
{keysDownloaded ? "Close" : "Download Keys"}
192250
</Button>
193251
</div>
194252
</div>

0 commit comments

Comments
 (0)