Skip to content

Commit

Permalink
Code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ZerNico committed Feb 23, 2023
1 parent b59593e commit adf0ac4
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 95 deletions.
57 changes: 25 additions & 32 deletions apps/api/src/trpc/middlewares/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,42 +32,35 @@ const isOAuthed = middleware(async ({ ctx, next }) => {
throw new TRPCError({ code: 'UNAUTHORIZED' })
}

try {
// Check if token is valid and get user info
const response = await ofetch<UserInfoResponse>(`${env.ZITADEL_URL}/oidc/v1/userinfo`, {
headers: {
Authorization: `Bearer ${token}`,
},
}).catch((e) => {
throw new TRPCError({ code: 'UNAUTHORIZED', cause: e })
})
// Check if token is valid and get user info
const response = await ofetch<UserInfoResponse>(`${env.ZITADEL_URL}/oidc/v1/userinfo`, {
headers: {
Authorization: `Bearer ${token}`,
},
}).catch((e) => {
throw new TRPCError({ code: 'UNAUTHORIZED', cause: e })
})

// remove org domain from username
const orgDomain = response['urn:zitadel:iam:org:domain:primary']
const username = response.preferred_username.replace(`@${orgDomain}`, '')
// remove org domain from username
const orgDomain = response['urn:zitadel:iam:org:domain:primary']
const username = response.preferred_username.replace(`@${orgDomain}`, '')

// validate user info
const parsedUser = userSchema.parse({
id: response.sub,
username,
picture: response.picture,
orgDomain,
})
// validate user info
const parsedUser = userSchema.parse({
id: response.sub,
username,
picture: response.picture,
orgDomain,
})

// create or update user
const user = await ctx.prisma.user.upsert({
where: { id: parsedUser.id },
update: { ...parsedUser },
create: { ...parsedUser },
})
return next({ ctx: { ...ctx, user } })
} catch (e) {
if (e instanceof TRPCError) {
throw e
}
// create or update user
const user = await ctx.prisma.user.upsert({
where: { id: parsedUser.id },
update: { ...parsedUser },
create: { ...parsedUser },
})

throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', cause: e })
}
return next({ ctx: { ...ctx, user } })
})

export const oAuthedProcedure = publicProcedure.use(isOAuthed)
86 changes: 38 additions & 48 deletions apps/api/src/trpc/routes/lobby/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,14 @@ export const lobbyRouter = router({
}
}),
leave: oAuthedProcedure.mutation(async ({ ctx }) => {
try {
await ctx.prisma.user.update({
where: { id: ctx.user.id },
data: {
lobby: {
disconnect: true,
},
await ctx.prisma.user.update({
where: { id: ctx.user.id },
data: {
lobby: {
disconnect: true,
},
})
return
} catch (e) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to leave lobby',
cause: e,
})
}
},
})
}),
joined: oAuthedProcedure.query(async ({ ctx }) => {
const user = await ctx.prisma.user.findUnique({
Expand All @@ -107,47 +98,46 @@ export const lobbyRouter = router({
return { lobby: user?.lobby }
}),
status: authedProcedure.query(async ({ ctx }) => {
try {
const lobby = await ctx.prisma.lobby.findUnique({
where: { id: ctx.user.sub },
include: {
users: true,
const lobby = await ctx.prisma.lobby.findUnique({
where: { id: ctx.user.sub },
include: {
users: true,
},
})

if (!lobby) throw new TRPCError({ code: 'NOT_FOUND', message: 'Lobby not found' })

return { lobby }
}),
users: oAuthedProcedure.query(async ({ ctx }) => {
const user = await ctx.prisma.user.findUnique({
where: { id: ctx.user.id },
include: {
lobby: {
include: {
users: true,
},
},
})
},
})

if (!lobby) throw new TRPCError({ code: 'NOT_FOUND', message: 'Lobby not found' })
if (!user?.lobby) throw new TRPCError({ code: 'NOT_FOUND', message: 'Lobby not found' })

return { lobby }
} catch (e) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to get lobby status',
cause: e,
})
}
return { users: user.lobby.users }
}),
kick: authedProcedure
.input(z.object({ userId: z.string() }))
.mutation(async ({ ctx, input }) => {
try {
await ctx.prisma.lobby.update({
where: { id: ctx.user.sub },
data: {
users: {
disconnect: {
id: input.userId,
},
await ctx.prisma.lobby.update({
where: { id: ctx.user.sub },
data: {
users: {
disconnect: {
id: input.userId,
},
},
})
return
} catch (e) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to kick user from lobby',
cause: e,
})
}
},
})
}),
delete: authedProcedure.mutation(async ({ ctx }) => {
await ctx.prisma.lobby.delete({
Expand Down
7 changes: 1 addition & 6 deletions apps/web/components/Avatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
const props = defineProps<{
src?: string
alt?: string
firstName?: string
lastName?: string
username?: string
}>()
Expand All @@ -19,11 +17,8 @@ watch(() => props.src, (src) => {
}, { immediate: true })
const initials = computed(() => {
if (props.firstName && props.lastName) {
return `${props.firstName[0]}${props.lastName[0]}`
}
if (props.username) {
return props.username[0]
return props.username[0].toUpperCase()
}
return '?'
})
Expand Down
20 changes: 20 additions & 0 deletions apps/web/composables/useAuthProxyFn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TRPCClientError } from '@trpc/client'

type InferArgs<T> = T extends (...t: [...infer Arg]) => any ? Arg : never
type InferReturn<T> = T extends (...t: [...infer Arg]) => infer Res ? Res : never

export default function useAuthProxyFn<TFunc extends (...args: any[]) => any>(fn: TFunc): (...args: InferArgs<TFunc>) => InferReturn<TFunc> {
const { logIn } = useAuth()
return (...args: InferArgs<TFunc>) => {
const val = fn(...args)
if (val instanceof Promise) {
return val.catch(async (e) => {
if (e instanceof TRPCClientError && e.data.code === 'UNAUTHORIZED') {
await logIn()
}
throw e
})
}
return val
}
}
12 changes: 7 additions & 5 deletions apps/web/layouts/auth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const leave = useMutation({
retry: 2,
retryDelay: 0,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['queryJoined'] })
router.push('/')
queryClient.invalidateQueries({ queryKey: ['queryJoined', 'queryUsers'] })
router.push('/join')
},
onError: () => {
notify({
Expand All @@ -35,7 +35,7 @@ const leave = useMutation({
<div>
<HeadlessMenuButton class="flex rounded-full bg-gray-800 text-sm hover:(ring-2 ring-offset-2 ring-offset-gray-800 ring-blue-500)">
<span class="sr-only">Open user menu</span>
<Avatar :first-name="user?.firstName" :last-name="user?.lastName" :username="user?.username" :src="user?.image" alt="User Avatar" />
<Avatar :username="user?.username" :src="user?.image" alt="User Avatar" />
</HeadlessMenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
Expand All @@ -44,7 +44,7 @@ const leave = useMutation({
<a :href="runtimeConfig.public.zitadelIssuer" target="_blank" class="block px-4 py-2 text-sm text-gray-700" :class="[active ? 'bg-gray-100' : '']">Edit profile</a>
</HeadlessMenuItem>
<HeadlessMenuItem v-slot="{ active }" role="button">
<a class="block px-4 py-2 text-sm text-gray-700" :class="[active ? 'bg-gray-100' : '']" @click="() => leave.mutate()">Leave session</a>
<a class="block px-4 py-2 text-sm text-gray-700" :class="[active ? 'bg-gray-100' : '']" @click="() => leave.mutate()">Leave lobby</a>
</HeadlessMenuItem>
<HeadlessMenuItem v-slot="{ active }" role="button">
<a class="block px-4 py-2 text-sm text-gray-700" :class="[active ? 'bg-gray-100' : '']" @click="() => logOut()">Sign out</a>
Expand All @@ -53,6 +53,8 @@ const leave = useMutation({
</transition>
</HeadlessMenu>
</NavBar>
<slot />
<main class="p-5">
<slot />
</main>
</div>
</template>
3 changes: 2 additions & 1 deletion apps/web/pages/join/[code].vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ const router = useRouter()
const { notify } = useNotification()
const mutateJoin = (code: string) => client.lobby.join.mutate({ code })
const proxy = useAuthProxyFn(mutateJoin)
const join = useMutation({
mutationFn: mutateJoin,
mutationFn: proxy,
retry: 2,
retryDelay: 0,
onSuccess: () => {
Expand Down
5 changes: 3 additions & 2 deletions apps/web/pages/join/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ const queryClient = useQueryClient()
const { notify } = useNotification()
const mutateJoin = (code: string) => client.lobby.join.mutate({ code })
const proxy = useAuthProxyFn(mutateJoin)
const join = useMutation({
mutationFn: mutateJoin,
mutationFn: proxy,
retry: 2,
retryDelay: 0,
onSuccess: () => {
Expand Down Expand Up @@ -48,7 +49,7 @@ const onSubmit = (e: Event) => {

<template>
<div class="flex items-center justify-center p-5">
<form class="py-6 px-8 flex flex-col gap-10 bg-black/20 text-white bg-gray-700 rounded-lg shadow min-w-1/4" @submit="onSubmit">
<form class="py-6 px-8 flex flex-col gap-10 text-white bg-gray-800 border border-gray-700 rounded-lg min-w-1/4" @submit="onSubmit">
<p class="font-medium text-xl">
Enter your lobby code
</p>
Expand Down
53 changes: 52 additions & 1 deletion apps/web/pages/lobby/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,58 @@
<script setup lang="ts">
import { useNotification } from '@kyvg/vue3-notification'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { TRPCClientError } from '@trpc/client'
definePageMeta({ middleware: ['auth'], layout: 'auth' })
const router = useRouter()
const { notify } = useNotification()
const { client } = useTRPC()
const queryClient = useQueryClient()
const queryUsers = async () => {
const res = await client.lobby.users.query()
return res.users
}
const proxy = useAuthProxyFn(queryUsers)
const { data } = useQuery({
queryKey: ['queryUsers'],
queryFn: proxy,
retry: 2,
retryDelay: 1000,
refetchOnWindowFocus: true,
cacheTime: 10000,
refetchInterval: 10000,
onError: (err) => {
if (err instanceof TRPCClientError && err.data.code === 'NOT_FOUND') {
queryClient.invalidateQueries({ queryKey: ['queryUsers'] })
router.push('/join')
} else {
notify({
type: 'error',
title: 'Error',
text: 'Something went wrong!',
})
}
},
})
// sort alphabetically
const sortedUsers = computed(() => {
if (!data.value) return []
return [...data.value].sort((a, b) => a.username.localeCompare(b.username))
})
</script>

<template>
<div>Lobby</div>
<div class="flex flex-col items-center gap-2">
<div v-for="user in sortedUsers" :key="user.id" class="flex items-center bg-gray-800 border border-gray-700 rounded-lg p-2 gap-3 w-full md:w-1/2 xl:w-1-/3">
<Avatar :username="user.username" :src="user.picture ?? undefined" />
<div class="text-white font-semibold truncate">
{{ user.username }}
</div>
</div>
</div>
</template>

0 comments on commit adf0ac4

Please sign in to comment.