From 81051ca9654462b599244988f0ffcbc60feba65d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sun, 2 Aug 2026 16:43:03 -0600 Subject: [PATCH] refactor(passkeys): list passkeys via tRPC instead of better-auth client atom --- .../settings/profile/manage-passkeys.tsx | 15 +++++++++++---- apps/dokploy/server/api/routers/user.ts | 4 ++++ packages/server/src/services/user.ts | 18 +++++++++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/manage-passkeys.tsx b/apps/dokploy/components/dashboard/settings/profile/manage-passkeys.tsx index 945343c67..82be73760 100644 --- a/apps/dokploy/components/dashboard/settings/profile/manage-passkeys.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/manage-passkeys.tsx @@ -16,10 +16,17 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { authClient } from "@/lib/auth-client"; +import { api } from "@/utils/api"; export const ManagePasskeys = () => { + const utils = api.useUtils(); const [isDialogOpen, setIsDialogOpen] = useState(false); - const { data: passkeys, isPending, refetch } = authClient.useListPasskeys(); + const { data: passkeys, isLoading } = api.user.listPasskeys.useQuery( + undefined, + { + enabled: isDialogOpen, + }, + ); const [name, setName] = useState(""); const [isAdding, setIsAdding] = useState(false); const [deletingId, setDeletingId] = useState(null); @@ -39,7 +46,7 @@ export const ManagePasskeys = () => { toast.success("Passkey added successfully"); setName(""); - refetch(); + utils.user.listPasskeys.invalidate(); } catch { toast.error("Failed to add passkey"); } finally { @@ -58,7 +65,7 @@ export const ManagePasskeys = () => { } toast.success("Passkey removed"); - refetch(); + utils.user.listPasskeys.invalidate(); } catch { toast.error("Failed to remove passkey"); } finally { @@ -84,7 +91,7 @@ export const ManagePasskeys = () => {
- {isPending ? ( + {isLoading ? (
Loading... diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index b4782b1d2..4f90b2cdc 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -3,6 +3,7 @@ import { createOrganizationUserWithCredentials, findNotificationById, findOrganizationById, + findPasskeysByUserId, findUserById, getDokployUrl, getUserByToken, @@ -173,6 +174,9 @@ export const userRouter = createTRPCRouter({ getPermissions: protectedProcedure.query(async ({ ctx }) => { return resolvePermissions(ctx); }), + listPasskeys: protectedProcedure.query(async ({ ctx }) => { + return findPasskeysByUserId(ctx.user.id); + }), haveRootAccess: protectedProcedure.query(async ({ ctx }) => { if (!IS_CLOUD) { return false; diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 0b59502a6..dbc78c275 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -4,11 +4,12 @@ import { apikey, invitation, member, + passkey, user, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; -import { and, eq } from "drizzle-orm"; +import { and, desc, eq } from "drizzle-orm"; import { auth } from "../lib/auth"; export type User = typeof user.$inferSelect; @@ -396,6 +397,21 @@ export const findMemberById = async ( return result; }; +export const findPasskeysByUserId = async (userId: string) => { + return db.query.passkey.findMany({ + where: eq(passkey.userId, userId), + columns: { + id: true, + name: true, + deviceType: true, + backedUp: true, + createdAt: true, + aaguid: true, + }, + orderBy: [desc(passkey.createdAt)], + }); +}; + export const createOrganizationUserWithCredentials = async ({ organizationId, email,