Skip to content

Commit c29ccbf

Browse files
committed
fix: move session management and user delete to orpc
1 parent 3f781ed commit c29ccbf

File tree

3 files changed

+291
-166
lines changed

3 files changed

+291
-166
lines changed

app/features/user/manager/page-user.tsx

Lines changed: 107 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { toast } from 'sonner';
1212
import { match } from 'ts-pattern';
1313

1414
import { authClient } from '@/lib/auth/client';
15-
import { Role } from '@/lib/auth/permissions';
1615
import { orpc } from '@/lib/orpc/client';
1716
import { getUiState } from '@/lib/ui-state';
1817

@@ -62,32 +61,28 @@ export const PageUser = (props: { params: { id: string } }) => {
6261
);
6362

6463
const deleteUser = async () => {
65-
const response = await authClient.admin.removeUser({
66-
userId: props.params.id,
67-
});
64+
try {
65+
await orpc.user.delete.call({ id: props.params.id });
66+
await Promise.all([
67+
// Invalidate users list
68+
queryClient.invalidateQueries({
69+
queryKey: orpc.user.getAll.key(),
70+
type: 'all',
71+
}),
72+
// Remove user from cache
73+
queryClient.removeQueries({
74+
queryKey: orpc.user.getById.key({ input: { id: props.params.id } }),
75+
}),
76+
]);
6877

69-
if (response.error) {
78+
// Redirect
79+
if (canGoBack) {
80+
router.history.back();
81+
} else {
82+
router.navigate({ to: '..', replace: true });
83+
}
84+
} catch {
7085
toast.error('Failed to delete the user');
71-
return;
72-
}
73-
74-
await Promise.all([
75-
// Invalidate users list
76-
queryClient.invalidateQueries({
77-
queryKey: orpc.user.getAll.key(),
78-
type: 'all',
79-
}),
80-
// Remove user from cache
81-
queryClient.removeQueries({
82-
queryKey: orpc.user.getById.key({ input: { id: props.params.id } }),
83-
}),
84-
]);
85-
86-
// Redirect
87-
if (canGoBack) {
88-
router.history.back();
89-
} else {
90-
router.navigate({ to: '..', replace: true });
9186
}
9287
};
9388

@@ -218,7 +213,9 @@ export const PageUser = (props: { params: { id: string } }) => {
218213
</Card>
219214

220215
<div className="flex flex-2 flex-col">
221-
<UserSessions userId={props.params.id} />
216+
<WithPermission permission={{ session: ['list'] }}>
217+
<UserSessions userId={props.params.id} />
218+
</WithPermission>
222219
</div>
223220
</div>
224221
))
@@ -229,19 +226,8 @@ export const PageUser = (props: { params: { id: string } }) => {
229226
};
230227

231228
const UserSessions = (props: { userId: string }) => {
232-
const queryClient = useQueryClient();
233-
const currentSession = authClient.useSession();
234-
235229
const sessionsQuery = useInfiniteQuery(
236230
orpc.user.getUserSessions.infiniteOptions({
237-
enabled: currentSession.data?.user.role
238-
? authClient.admin.checkRolePermission({
239-
role: currentSession.data.user.role as Role,
240-
permission: {
241-
session: ['list'],
242-
},
243-
})
244-
: false,
245231
input: (cursor: string | undefined) => ({
246232
userId: props.userId,
247233
cursor,
@@ -253,42 +239,6 @@ const UserSessions = (props: { userId: string }) => {
253239
})
254240
);
255241

256-
const revokeAllSessions = useMutation({
257-
mutationFn: async ({ userId }: { userId: string }) => {
258-
const response = await authClient.admin.revokeUserSessions({
259-
userId,
260-
});
261-
if (response.error) {
262-
throw new Error(response.error.message);
263-
}
264-
await queryClient.invalidateQueries({
265-
queryKey: orpc.user.getUserSessions.key({
266-
input: { userId: props.userId },
267-
type: 'infinite',
268-
}),
269-
});
270-
return response.data;
271-
},
272-
});
273-
274-
const revokeSession = useMutation({
275-
mutationFn: async ({ sessionToken }: { sessionToken: string }) => {
276-
const response = await authClient.admin.revokeUserSession({
277-
sessionToken,
278-
});
279-
if (response.error) {
280-
throw new Error(response.error.message);
281-
}
282-
await queryClient.invalidateQueries({
283-
queryKey: orpc.user.getUserSessions.key({
284-
input: { userId: props.userId },
285-
type: 'infinite',
286-
}),
287-
});
288-
return response.data;
289-
},
290-
});
291-
292242
const ui = getUiState((set) => {
293243
if (sessionsQuery.status === 'pending') return set('pending');
294244
if (sessionsQuery.status === 'error') return set('error');
@@ -310,23 +260,9 @@ const UserSessions = (props: { userId: string }) => {
310260

311261
<WithPermission permission={{ session: ['revoke'] }}>
312262
<DataListCell className="flex-none">
313-
<Button
314-
type="button"
315-
size="sm"
316-
variant="secondary"
317-
disabled={
318-
currentSession.data?.user.id === props.userId ||
319-
ui.is('empty')
320-
}
321-
loading={revokeAllSessions.isPending}
322-
onClick={() => {
323-
revokeAllSessions.mutate({
324-
userId: props.userId,
325-
});
326-
}}
327-
>
328-
Revoke all
329-
</Button>
263+
{ui.is('default') && (
264+
<RevokeAllSessionsButton userId={props.userId} />
265+
)}
330266
</DataListCell>
331267
</WithPermission>
332268
</DataListRow>
@@ -363,22 +299,10 @@ const UserSessions = (props: { userId: string }) => {
363299
</DataListCell>
364300
<WithPermission permission={{ session: ['revoke'] }}>
365301
<DataListCell className="flex-none">
366-
<Button
367-
type="button"
368-
size="xs"
369-
variant="secondary"
370-
disabled={
371-
currentSession.data?.session.token === item.token
372-
}
373-
loading={revokeSession.isPending}
374-
onClick={() => {
375-
revokeSession.mutate({
376-
sessionToken: item.token,
377-
});
378-
}}
379-
>
380-
Revoke
381-
</Button>
302+
<RevokeSessionButton
303+
userId={props.userId}
304+
sessionToken={item.token}
305+
/>
382306
</DataListCell>
383307
</WithPermission>
384308
</DataListRow>
@@ -410,3 +334,80 @@ const UserSessions = (props: { userId: string }) => {
410334
</WithPermission>
411335
);
412336
};
337+
338+
const RevokeAllSessionsButton = (props: { userId: string }) => {
339+
const queryClient = useQueryClient();
340+
const currentSession = authClient.useSession();
341+
const revokeAllSessions = useMutation(
342+
orpc.user.revokeUserSessions.mutationOptions({
343+
onSuccess: async () => {
344+
await queryClient.invalidateQueries({
345+
queryKey: orpc.user.getUserSessions.key({
346+
input: { userId: props.userId },
347+
type: 'infinite',
348+
}),
349+
});
350+
},
351+
onError: () => {
352+
toast.error('Failed to revoke all sessions');
353+
},
354+
})
355+
);
356+
357+
return (
358+
<Button
359+
type="button"
360+
size="xs"
361+
variant="secondary"
362+
disabled={currentSession.data?.user.id === props.userId}
363+
loading={revokeAllSessions.isPending}
364+
onClick={() => {
365+
revokeAllSessions.mutate({
366+
id: props.userId,
367+
});
368+
}}
369+
>
370+
Revoke all
371+
</Button>
372+
);
373+
};
374+
375+
const RevokeSessionButton = (props: {
376+
userId: string;
377+
sessionToken: string;
378+
}) => {
379+
const queryClient = useQueryClient();
380+
const currentSession = authClient.useSession();
381+
const revokeSession = useMutation(
382+
orpc.user.revokeUserSession.mutationOptions({
383+
onSuccess: async () => {
384+
await queryClient.invalidateQueries({
385+
queryKey: orpc.user.getUserSessions.key({
386+
input: { userId: props.userId },
387+
type: 'infinite',
388+
}),
389+
});
390+
},
391+
onError: () => {
392+
toast.error('Failed to revoke sessions');
393+
},
394+
})
395+
);
396+
return (
397+
<Button
398+
type="button"
399+
size="xs"
400+
variant="secondary"
401+
disabled={currentSession.data?.session.token === props.sessionToken}
402+
loading={revokeSession.isPending}
403+
onClick={() => {
404+
revokeSession.mutate({
405+
id: props.userId,
406+
sessionToken: props.sessionToken,
407+
});
408+
}}
409+
>
410+
Revoke
411+
</Button>
412+
);
413+
};

app/server/orpc.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const base = os
1818
return await next({
1919
context: {
2020
user: session?.user,
21+
session: session?.session,
2122
db,
2223
},
2324
});
@@ -59,16 +60,17 @@ export const protectedProcedure = ({
5960
permission: Permission | null;
6061
}) =>
6162
base.use(async ({ context, next }) => {
62-
const { user } = context;
63+
const { user, session } = context;
6364

64-
if (!user) {
65+
if (!user || !session) {
6566
throw new ORPCError('UNAUTHORIZED');
6667
}
6768

6869
if (!permission) {
6970
return await next({
7071
context: {
7172
user,
73+
session,
7274
},
7375
});
7476
}
@@ -91,6 +93,7 @@ export const protectedProcedure = ({
9193
return await next({
9294
context: {
9395
user,
96+
session,
9497
},
9598
});
9699
});

0 commit comments

Comments
 (0)