| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  | import { BookingStatus, Prisma } from "@prisma/client"; | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | import { z } from "zod"; | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | import { checkPremiumUsername } from "@ee/lib/core/checkPremiumUsername"; | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | import { checkRegularUsername } from "@lib/core/checkRegularUsername"; | 
					
						
							| 
									
										
										
										
											2021-10-13 11:35:25 +00:00
										 |  |  | import { ALL_INTEGRATIONS } from "@lib/integrations/getIntegrations"; | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | import slugify from "@lib/slugify"; | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-20 15:42:40 +00:00
										 |  |  | import getCalendarCredentials from "@server/integrations/getCalendarCredentials"; | 
					
						
							|  |  |  | import getConnectedCalendars from "@server/integrations/getConnectedCalendars"; | 
					
						
							| 
									
										
										
										
											2021-10-14 19:22:01 +00:00
										 |  |  | import { TRPCError } from "@trpc/server"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-14 10:57:49 +00:00
										 |  |  | import { createProtectedRouter, createRouter } from "../createRouter"; | 
					
						
							| 
									
										
										
										
											2021-09-30 20:37:29 +00:00
										 |  |  | import { resizeBase64Image } from "../lib/resizeBase64Image"; | 
					
						
							| 
									
										
										
										
											2021-10-25 16:15:52 +00:00
										 |  |  | import { webhookRouter } from "./viewer/webhook"; | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | const checkUsername = | 
					
						
							|  |  |  |   process.env.NEXT_PUBLIC_APP_URL === "https://cal.com" ? checkPremiumUsername : checkRegularUsername; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-14 10:57:49 +00:00
										 |  |  | // things that unauthenticated users can query about themselves
 | 
					
						
							|  |  |  | const publicViewerRouter = createRouter() | 
					
						
							|  |  |  |   .query("session", { | 
					
						
							|  |  |  |     resolve({ ctx }) { | 
					
						
							|  |  |  |       return ctx.session; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  |   .query("i18n", { | 
					
						
							|  |  |  |     async resolve({ ctx }) { | 
					
						
							|  |  |  |       const { locale, i18n } = ctx; | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         i18n, | 
					
						
							|  |  |  |         locale, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | // routes only available to authenticated users
 | 
					
						
							| 
									
										
										
										
											2021-10-14 10:57:49 +00:00
										 |  |  | const loggedInViewerRouter = createProtectedRouter() | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |   .query("me", { | 
					
						
							|  |  |  |     resolve({ ctx }) { | 
					
						
							| 
									
										
										
										
											2021-10-13 11:35:25 +00:00
										 |  |  |       const { | 
					
						
							|  |  |  |         // pick only the part we want to expose in the API
 | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         name, | 
					
						
							|  |  |  |         username, | 
					
						
							|  |  |  |         email, | 
					
						
							|  |  |  |         startTime, | 
					
						
							|  |  |  |         endTime, | 
					
						
							|  |  |  |         bufferTime, | 
					
						
							|  |  |  |         locale, | 
					
						
							|  |  |  |         avatar, | 
					
						
							|  |  |  |         createdDate, | 
					
						
							|  |  |  |         completedOnboarding, | 
					
						
							| 
									
										
										
										
											2021-10-14 10:57:49 +00:00
										 |  |  |         twoFactorEnabled, | 
					
						
							| 
									
										
										
										
											2021-10-13 11:35:25 +00:00
										 |  |  |       } = ctx.user; | 
					
						
							|  |  |  |       const me = { | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         name, | 
					
						
							|  |  |  |         username, | 
					
						
							|  |  |  |         email, | 
					
						
							|  |  |  |         startTime, | 
					
						
							|  |  |  |         endTime, | 
					
						
							|  |  |  |         bufferTime, | 
					
						
							|  |  |  |         locale, | 
					
						
							|  |  |  |         avatar, | 
					
						
							|  |  |  |         createdDate, | 
					
						
							|  |  |  |         completedOnboarding, | 
					
						
							| 
									
										
										
										
											2021-10-14 10:57:49 +00:00
										 |  |  |         twoFactorEnabled, | 
					
						
							| 
									
										
										
										
											2021-10-13 11:35:25 +00:00
										 |  |  |       }; | 
					
						
							|  |  |  |       return me; | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |     }, | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2021-10-15 19:07:00 +00:00
										 |  |  |   .query("eventTypes", { | 
					
						
							|  |  |  |     async resolve({ ctx }) { | 
					
						
							|  |  |  |       const { prisma } = ctx; | 
					
						
							|  |  |  |       const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({ | 
					
						
							|  |  |  |         id: true, | 
					
						
							|  |  |  |         title: true, | 
					
						
							|  |  |  |         description: true, | 
					
						
							|  |  |  |         length: true, | 
					
						
							|  |  |  |         schedulingType: true, | 
					
						
							|  |  |  |         slug: true, | 
					
						
							|  |  |  |         hidden: true, | 
					
						
							|  |  |  |         price: true, | 
					
						
							|  |  |  |         currency: true, | 
					
						
							|  |  |  |         users: { | 
					
						
							|  |  |  |           select: { | 
					
						
							|  |  |  |             id: true, | 
					
						
							|  |  |  |             avatar: true, | 
					
						
							|  |  |  |             name: true, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const user = await prisma.user.findUnique({ | 
					
						
							|  |  |  |         where: { | 
					
						
							|  |  |  |           id: ctx.user.id, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         select: { | 
					
						
							|  |  |  |           id: true, | 
					
						
							|  |  |  |           username: true, | 
					
						
							|  |  |  |           name: true, | 
					
						
							|  |  |  |           startTime: true, | 
					
						
							|  |  |  |           endTime: true, | 
					
						
							|  |  |  |           bufferTime: true, | 
					
						
							|  |  |  |           avatar: true, | 
					
						
							|  |  |  |           plan: true, | 
					
						
							|  |  |  |           teams: { | 
					
						
							|  |  |  |             where: { | 
					
						
							|  |  |  |               accepted: true, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             select: { | 
					
						
							|  |  |  |               role: true, | 
					
						
							|  |  |  |               team: { | 
					
						
							|  |  |  |                 select: { | 
					
						
							|  |  |  |                   id: true, | 
					
						
							|  |  |  |                   name: true, | 
					
						
							|  |  |  |                   slug: true, | 
					
						
							|  |  |  |                   logo: true, | 
					
						
							|  |  |  |                   members: { | 
					
						
							|  |  |  |                     select: { | 
					
						
							|  |  |  |                       userId: true, | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                   }, | 
					
						
							|  |  |  |                   eventTypes: { | 
					
						
							|  |  |  |                     select: eventTypeSelect, | 
					
						
							|  |  |  |                   }, | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           eventTypes: { | 
					
						
							|  |  |  |             where: { | 
					
						
							|  |  |  |               team: null, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             select: eventTypeSelect, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-18 07:02:25 +00:00
										 |  |  |       if (!user) { | 
					
						
							|  |  |  |         throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-10-15 19:07:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // backwards compatibility, TMP:
 | 
					
						
							|  |  |  |       const typesRaw = await prisma.eventType.findMany({ | 
					
						
							|  |  |  |         where: { | 
					
						
							|  |  |  |           userId: ctx.user.id, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         select: eventTypeSelect, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       type EventTypeGroup = { | 
					
						
							|  |  |  |         teamId?: number | null; | 
					
						
							|  |  |  |         profile: { | 
					
						
							|  |  |  |           slug: typeof user["username"]; | 
					
						
							|  |  |  |           name: typeof user["name"]; | 
					
						
							|  |  |  |           image: typeof user["avatar"]; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         metadata: { | 
					
						
							|  |  |  |           membershipCount: number; | 
					
						
							|  |  |  |           readOnly: boolean; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         eventTypes: (typeof user.eventTypes[number] & { $disabled?: boolean })[]; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       let eventTypeGroups: EventTypeGroup[] = []; | 
					
						
							|  |  |  |       const eventTypesHashMap = user.eventTypes.concat(typesRaw).reduce((hashMap, newItem) => { | 
					
						
							|  |  |  |         const oldItem = hashMap[newItem.id] || {}; | 
					
						
							|  |  |  |         hashMap[newItem.id] = { ...oldItem, ...newItem }; | 
					
						
							|  |  |  |         return hashMap; | 
					
						
							|  |  |  |       }, {} as Record<number, EventTypeGroup["eventTypes"][number]>); | 
					
						
							|  |  |  |       const mergedEventTypes = Object.values(eventTypesHashMap).map((et, index) => ({ | 
					
						
							|  |  |  |         ...et, | 
					
						
							|  |  |  |         $disabled: user.plan === "FREE" && index > 0, | 
					
						
							|  |  |  |       })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       eventTypeGroups.push({ | 
					
						
							|  |  |  |         teamId: null, | 
					
						
							|  |  |  |         profile: { | 
					
						
							|  |  |  |           slug: user.username, | 
					
						
							|  |  |  |           name: user.name, | 
					
						
							|  |  |  |           image: user.avatar, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         eventTypes: mergedEventTypes, | 
					
						
							|  |  |  |         metadata: { | 
					
						
							|  |  |  |           membershipCount: 1, | 
					
						
							|  |  |  |           readOnly: false, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       eventTypeGroups = ([] as EventTypeGroup[]).concat( | 
					
						
							|  |  |  |         eventTypeGroups, | 
					
						
							|  |  |  |         user.teams.map((membership) => ({ | 
					
						
							|  |  |  |           teamId: membership.team.id, | 
					
						
							|  |  |  |           profile: { | 
					
						
							|  |  |  |             name: membership.team.name, | 
					
						
							|  |  |  |             image: membership.team.logo || "", | 
					
						
							|  |  |  |             slug: "team/" + membership.team.slug, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           metadata: { | 
					
						
							|  |  |  |             membershipCount: membership.team.members.length, | 
					
						
							|  |  |  |             readOnly: membership.role !== "OWNER", | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           eventTypes: membership.team.eventTypes, | 
					
						
							|  |  |  |         })) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const canAddEvents = user.plan !== "FREE" || eventTypeGroups[0].eventTypes.length < 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         canAddEvents, | 
					
						
							|  |  |  |         user, | 
					
						
							|  |  |  |         // don't display event teams without event types,
 | 
					
						
							|  |  |  |         eventTypeGroups: eventTypeGroups.filter((groupBy) => !!groupBy.eventTypes?.length), | 
					
						
							|  |  |  |         // so we can show a dropdown when the user has teams
 | 
					
						
							|  |  |  |         profiles: eventTypeGroups.map((group) => ({ | 
					
						
							|  |  |  |           teamId: group.teamId, | 
					
						
							|  |  |  |           ...group.profile, | 
					
						
							|  |  |  |           ...group.metadata, | 
					
						
							|  |  |  |         })), | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |   .query("bookings", { | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |     input: z.object({ | 
					
						
							| 
									
										
										
										
											2021-10-02 13:29:26 +00:00
										 |  |  |       status: z.enum(["upcoming", "past", "cancelled"]), | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |     }), | 
					
						
							|  |  |  |     async resolve({ ctx, input }) { | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |       const { prisma, user } = ctx; | 
					
						
							| 
									
										
										
										
											2021-10-02 13:29:26 +00:00
										 |  |  |       const bookingListingByStatus = input.status; | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |       const bookingListingFilters: Record<typeof bookingListingByStatus, Prisma.BookingWhereInput[]> = { | 
					
						
							| 
									
										
										
										
											2021-10-08 15:58:37 +00:00
										 |  |  |         upcoming: [{ endTime: { gte: new Date() }, NOT: { status: { equals: BookingStatus.CANCELLED } } }], | 
					
						
							|  |  |  |         past: [{ endTime: { lte: new Date() }, NOT: { status: { equals: BookingStatus.CANCELLED } } }], | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |         cancelled: [{ status: { equals: BookingStatus.CANCELLED } }], | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const bookingListingOrderby: Record<typeof bookingListingByStatus, Prisma.BookingOrderByInput> = { | 
					
						
							|  |  |  |         upcoming: { startTime: "desc" }, | 
					
						
							|  |  |  |         past: { startTime: "asc" }, | 
					
						
							|  |  |  |         cancelled: { startTime: "asc" }, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const passedBookingsFilter = bookingListingFilters[bookingListingByStatus]; | 
					
						
							|  |  |  |       const orderBy = bookingListingOrderby[bookingListingByStatus]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |       const bookingsQuery = await prisma.booking.findMany({ | 
					
						
							|  |  |  |         where: { | 
					
						
							|  |  |  |           OR: [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               userId: user.id, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               attendees: { | 
					
						
							|  |  |  |                 some: { | 
					
						
							|  |  |  |                   email: user.email, | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           ], | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |           AND: passedBookingsFilter, | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |         }, | 
					
						
							|  |  |  |         select: { | 
					
						
							|  |  |  |           uid: true, | 
					
						
							|  |  |  |           title: true, | 
					
						
							|  |  |  |           description: true, | 
					
						
							|  |  |  |           attendees: true, | 
					
						
							|  |  |  |           confirmed: true, | 
					
						
							|  |  |  |           rejected: true, | 
					
						
							|  |  |  |           id: true, | 
					
						
							|  |  |  |           startTime: true, | 
					
						
							|  |  |  |           endTime: true, | 
					
						
							|  |  |  |           eventType: { | 
					
						
							|  |  |  |             select: { | 
					
						
							|  |  |  |               team: { | 
					
						
							|  |  |  |                 select: { | 
					
						
							|  |  |  |                   name: true, | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           status: true, | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |         orderBy, | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const bookings = bookingsQuery.reverse().map((booking) => { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           ...booking, | 
					
						
							|  |  |  |           startTime: booking.startTime.toISOString(), | 
					
						
							|  |  |  |           endTime: booking.endTime.toISOString(), | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return bookings; | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  |   }) | 
					
						
							| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  |   .query("integrations", { | 
					
						
							|  |  |  |     async resolve({ ctx }) { | 
					
						
							|  |  |  |       const { user } = ctx; | 
					
						
							|  |  |  |       const { credentials } = user; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 11:35:25 +00:00
										 |  |  |       function countActive(items: { credentialIds: unknown[] }[]) { | 
					
						
							|  |  |  |         return items.reduce((acc, item) => acc + item.credentialIds.length, 0); | 
					
						
							| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-10-13 11:35:25 +00:00
										 |  |  |       const integrations = ALL_INTEGRATIONS.map((integration) => ({ | 
					
						
							|  |  |  |         ...integration, | 
					
						
							|  |  |  |         credentialIds: credentials | 
					
						
							|  |  |  |           .filter((credential) => credential.type === integration.type) | 
					
						
							|  |  |  |           .map((credential) => credential.id), | 
					
						
							|  |  |  |       })); | 
					
						
							|  |  |  |       // `flatMap()` these work like `.filter()` but infers the types correctly
 | 
					
						
							| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  |       const conferencing = integrations.flatMap((item) => (item.variant === "conferencing" ? [item] : [])); | 
					
						
							|  |  |  |       const payment = integrations.flatMap((item) => (item.variant === "payment" ? [item] : [])); | 
					
						
							|  |  |  |       const calendar = integrations.flatMap((item) => (item.variant === "calendar" ? [item] : [])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // get user's credentials + their connected integrations
 | 
					
						
							| 
									
										
										
										
											2021-10-20 15:42:40 +00:00
										 |  |  |       const calendarCredentials = getCalendarCredentials(user.credentials, user.id); | 
					
						
							| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // get all the connected integrations' calendars (from third party)
 | 
					
						
							| 
									
										
										
										
											2021-10-20 15:42:40 +00:00
										 |  |  |       const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars); | 
					
						
							| 
									
										
										
										
											2021-10-18 07:02:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       const webhooks = await ctx.prisma.webhook.findMany({ | 
					
						
							|  |  |  |         where: { | 
					
						
							|  |  |  |           userId: user.id, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  |       return { | 
					
						
							|  |  |  |         conferencing: { | 
					
						
							|  |  |  |           items: conferencing, | 
					
						
							|  |  |  |           numActive: countActive(conferencing), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         calendar: { | 
					
						
							|  |  |  |           items: calendar, | 
					
						
							|  |  |  |           numActive: countActive(calendar), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         payment: { | 
					
						
							|  |  |  |           items: payment, | 
					
						
							|  |  |  |           numActive: countActive(payment), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         connectedCalendars, | 
					
						
							| 
									
										
										
										
											2021-10-18 07:02:25 +00:00
										 |  |  |         webhooks, | 
					
						
							| 
									
										
										
										
											2021-10-12 09:35:44 +00:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  |   .mutation("updateProfile", { | 
					
						
							|  |  |  |     input: z.object({ | 
					
						
							|  |  |  |       username: z.string().optional(), | 
					
						
							|  |  |  |       name: z.string().optional(), | 
					
						
							|  |  |  |       bio: z.string().optional(), | 
					
						
							|  |  |  |       avatar: z.string().optional(), | 
					
						
							|  |  |  |       timeZone: z.string().optional(), | 
					
						
							|  |  |  |       weekStart: z.string().optional(), | 
					
						
							|  |  |  |       hideBranding: z.boolean().optional(), | 
					
						
							| 
									
										
										
										
											2021-10-02 20:16:51 +00:00
										 |  |  |       theme: z.string().optional().nullable(), | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  |       completedOnboarding: z.boolean().optional(), | 
					
						
							|  |  |  |       locale: z.string().optional(), | 
					
						
							|  |  |  |     }), | 
					
						
							|  |  |  |     async resolve({ input, ctx }) { | 
					
						
							|  |  |  |       const { user, prisma } = ctx; | 
					
						
							|  |  |  |       const data: Prisma.UserUpdateInput = { | 
					
						
							|  |  |  |         ...input, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       if (input.username) { | 
					
						
							|  |  |  |         const username = slugify(input.username); | 
					
						
							|  |  |  |         // Only validate if we're changing usernames
 | 
					
						
							|  |  |  |         if (username !== user.username) { | 
					
						
							|  |  |  |           data.username = username; | 
					
						
							|  |  |  |           const response = await checkUsername(username); | 
					
						
							|  |  |  |           if (!response.available) { | 
					
						
							|  |  |  |             throw new TRPCError({ code: "BAD_REQUEST", message: response.message }); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-09-30 20:37:29 +00:00
										 |  |  |       if (input.avatar) { | 
					
						
							|  |  |  |         data.avatar = await resizeBase64Image(input.avatar); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-09-28 08:57:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       await prisma.user.update({ | 
					
						
							|  |  |  |         where: { | 
					
						
							|  |  |  |           id: user.id, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         data, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2021-09-27 14:47:55 +00:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2021-10-14 10:57:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-25 16:15:52 +00:00
										 |  |  | export const viewerRouter = createRouter() | 
					
						
							|  |  |  |   .merge(publicViewerRouter) | 
					
						
							|  |  |  |   .merge(loggedInViewerRouter) | 
					
						
							|  |  |  |   .merge("webhook.", webhookRouter); |