Fix/user creds (#8393)
* fix-user-creds * Type fixes * Type fixes * Type fixes --------- Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
co-authored by
Alex van Andel
parent
c1c95f711b
commit
fe996b4e41
@@ -18,7 +18,7 @@ export const getStaticProps: GetStaticProps<
|
||||
{ user: string }
|
||||
> = async (context) => {
|
||||
const { user: username, month } = paramsSchema.parse(context.params);
|
||||
const user = await prisma.user.findUnique({
|
||||
const userWithCredentials = await prisma.user.findUnique({
|
||||
where: {
|
||||
username,
|
||||
},
|
||||
@@ -34,14 +34,15 @@ export const getStaticProps: GetStaticProps<
|
||||
).startOf("day");
|
||||
const endDate = startDate.endOf("month");
|
||||
try {
|
||||
const results = user?.credentials
|
||||
const results = userWithCredentials?.credentials
|
||||
? await getCachedResults(
|
||||
user?.credentials,
|
||||
userWithCredentials?.credentials,
|
||||
startDate.format(),
|
||||
endDate.format(),
|
||||
user?.selectedCalendars
|
||||
userWithCredentials?.selectedCalendars
|
||||
)
|
||||
: [];
|
||||
|
||||
return {
|
||||
props: { results, date: new Date().toISOString() },
|
||||
revalidate: 1,
|
||||
|
||||
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
const userWithCredentials = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: session.user.id,
|
||||
},
|
||||
@@ -25,11 +25,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
selectedCalendars: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
if (!userWithCredentials) {
|
||||
res.status(401).json({ message: "Not authenticated" });
|
||||
return;
|
||||
}
|
||||
const { credentials, ...user } = userWithCredentials;
|
||||
|
||||
if (req.method === "POST") {
|
||||
await prisma.selectedCalendar.upsert({
|
||||
@@ -80,7 +80,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
|
||||
// get user's credentials + their connected integrations
|
||||
const calendarCredentials = getCalendarCredentials(user.credentials);
|
||||
const calendarCredentials = getCalendarCredentials(credentials);
|
||||
// get all the connected integrations' calendars (from third party)
|
||||
const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
|
||||
const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty);
|
||||
|
||||
@@ -54,7 +54,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
credentials: true,
|
||||
timeZone: true,
|
||||
email: true,
|
||||
name: true,
|
||||
|
||||
@@ -282,7 +282,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
credentials: true,
|
||||
timeZone: true,
|
||||
name: true,
|
||||
email: true,
|
||||
|
||||
@@ -70,7 +70,10 @@ type EventType = Awaited<ReturnType<typeof getEventType>>;
|
||||
const getUser = (where: Prisma.UserWhereUniqueInput) =>
|
||||
prisma.user.findUnique({
|
||||
where,
|
||||
select: availabilityUserSelect,
|
||||
select: {
|
||||
...availabilityUserSelect,
|
||||
credentials: true,
|
||||
},
|
||||
});
|
||||
|
||||
type User = Awaited<ReturnType<typeof getUser>>;
|
||||
|
||||
@@ -38,7 +38,7 @@ async function getBookingToDelete(id: number | undefined, uid: string | undefine
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
credentials: true,
|
||||
credentials: true, // Not leaking at the moment, be careful with
|
||||
email: true,
|
||||
timeZone: true,
|
||||
name: true,
|
||||
|
||||
@@ -197,7 +197,12 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
|
||||
id: true,
|
||||
customInputs: true,
|
||||
disableGuests: true,
|
||||
users: userSelect,
|
||||
users: {
|
||||
select: {
|
||||
credentials: true,
|
||||
...userSelect.select,
|
||||
},
|
||||
},
|
||||
slug: true,
|
||||
team: {
|
||||
select: {
|
||||
@@ -253,7 +258,12 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
|
||||
hosts: {
|
||||
select: {
|
||||
isFixed: true,
|
||||
user: userSelect,
|
||||
user: {
|
||||
select: {
|
||||
credentials: true,
|
||||
...userSelect.select,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
availability: {
|
||||
@@ -277,7 +287,7 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
|
||||
};
|
||||
};
|
||||
|
||||
type IsFixedAwareUser = User & { isFixed: boolean };
|
||||
type IsFixedAwareUser = User & { isFixed: boolean; credentials: Credential[] };
|
||||
|
||||
async function ensureAvailableUsers(
|
||||
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
|
||||
@@ -623,6 +633,7 @@ async function handler(
|
||||
},
|
||||
select: {
|
||||
...userSelect.select,
|
||||
credentials: true, // Don't leak to client
|
||||
metadata: true,
|
||||
},
|
||||
})
|
||||
@@ -654,7 +665,10 @@ async function handler(
|
||||
where: {
|
||||
id: eventType.userId,
|
||||
},
|
||||
...userSelect,
|
||||
select: {
|
||||
credentials: true, // Don't leak to client
|
||||
...userSelect.select,
|
||||
},
|
||||
});
|
||||
if (!eventTypeUser) {
|
||||
log.warn({ message: "NewBooking: eventTypeUser.notFound" });
|
||||
|
||||
@@ -55,7 +55,6 @@ async function getBooking(bookingId: number) {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
credentials: true,
|
||||
timeZone: true,
|
||||
email: true,
|
||||
name: true,
|
||||
@@ -176,9 +175,11 @@ async function handlePaymentSuccess(event: Stripe.Event) {
|
||||
eventTypeRaw = await getEventType(booking.eventTypeId);
|
||||
}
|
||||
|
||||
const { user } = booking;
|
||||
const { user: userWithCredentials } = booking;
|
||||
|
||||
if (!user) throw new HttpCode({ statusCode: 204, message: "No user found" });
|
||||
if (!userWithCredentials) throw new HttpCode({ statusCode: 204, message: "No user found" });
|
||||
|
||||
const { credentials, ...user } = userWithCredentials;
|
||||
|
||||
const t = await getTranslation(user.locale ?? "en", "common");
|
||||
const attendeesListPromises = booking.attendees.map(async (attendee) => {
|
||||
@@ -227,7 +228,7 @@ async function handlePaymentSuccess(event: Stripe.Event) {
|
||||
|
||||
const isConfirmed = booking.status === BookingStatus.ACCEPTED;
|
||||
if (isConfirmed) {
|
||||
const eventManager = new EventManager(user);
|
||||
const eventManager = new EventManager(userWithCredentials);
|
||||
const scheduleResult = await eventManager.create(evt);
|
||||
bookingData.references = { create: scheduleResult.referencesToCreate };
|
||||
}
|
||||
@@ -255,7 +256,14 @@ async function handlePaymentSuccess(event: Stripe.Event) {
|
||||
await prisma.$transaction([paymentUpdate, bookingUpdate]);
|
||||
|
||||
if (!isConfirmed && !eventTypeRaw?.requiresConfirmation) {
|
||||
await handleConfirmation({ user, evt, prisma, bookingId: booking.id, booking, paid: true });
|
||||
await handleConfirmation({
|
||||
user: userWithCredentials,
|
||||
evt,
|
||||
prisma,
|
||||
bookingId: booking.id,
|
||||
booking,
|
||||
paid: true,
|
||||
});
|
||||
} else {
|
||||
await sendScheduledEmails({ ...evt });
|
||||
}
|
||||
@@ -276,14 +284,32 @@ const handleSetupSuccess = async (event: Stripe.Event) => {
|
||||
|
||||
if (!payment?.data || !payment?.id) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
|
||||
|
||||
const { booking, user, evt, eventTypeRaw } = await getBooking(payment.bookingId);
|
||||
const { user, evt, eventTypeRaw } = await getBooking(payment.bookingId);
|
||||
|
||||
const bookingData: Prisma.BookingUpdateInput = {
|
||||
paid: true,
|
||||
};
|
||||
|
||||
const userWithCredentials = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
timeZone: true,
|
||||
email: true,
|
||||
name: true,
|
||||
locale: true,
|
||||
destinationCalendar: true,
|
||||
credentials: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userWithCredentials) throw new HttpCode({ statusCode: 204, message: "No user found" });
|
||||
|
||||
if (!eventTypeRaw?.requiresConfirmation) {
|
||||
const eventManager = new EventManager(user);
|
||||
const eventManager = new EventManager(userWithCredentials);
|
||||
const scheduleResult = await eventManager.create(evt);
|
||||
bookingData.references = { create: scheduleResult.referencesToCreate };
|
||||
bookingData.status = BookingStatus.ACCEPTED;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import type { Prisma, Credential } from "@prisma/client";
|
||||
import { PeriodType, SchedulingType } from "@prisma/client";
|
||||
|
||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
||||
@@ -25,7 +25,7 @@ type UsernameSlugLinkProps = {
|
||||
slug: string;
|
||||
};
|
||||
|
||||
const user: User = {
|
||||
const user: User & { credentials: Credential[] } = {
|
||||
metadata: null,
|
||||
theme: null,
|
||||
credentials: [],
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
export const availabilityUserSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
credentials: true,
|
||||
id: true,
|
||||
timeZone: true,
|
||||
bufferTime: true,
|
||||
availability: true,
|
||||
id: true,
|
||||
startTime: true,
|
||||
username: true,
|
||||
endTime: true,
|
||||
selectedCalendars: true,
|
||||
timeFormat: true,
|
||||
defaultScheduleId: true,
|
||||
// Relationships
|
||||
schedules: {
|
||||
select: {
|
||||
availability: true,
|
||||
@@ -18,7 +17,8 @@ export const availabilityUserSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
defaultScheduleId: true,
|
||||
availability: true,
|
||||
selectedCalendars: true,
|
||||
});
|
||||
|
||||
export const baseUserSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
|
||||
@@ -1041,7 +1041,6 @@ const loggedInViewerRouter = router({
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
credentials: true,
|
||||
email: true,
|
||||
timeZone: true,
|
||||
name: true,
|
||||
|
||||
@@ -218,12 +218,16 @@ async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer<typeo
|
||||
select: {
|
||||
isFixed: true,
|
||||
user: {
|
||||
select: availabilityUserSelect,
|
||||
select: {
|
||||
credentials: true, // Don't leak credentials to the client
|
||||
...availabilityUserSelect,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
users: {
|
||||
select: {
|
||||
credentials: true, // Don't leak credentials to the client
|
||||
...availabilityUserSelect,
|
||||
},
|
||||
},
|
||||
@@ -250,6 +254,7 @@ async function getDynamicEventType(ctx: { prisma: typeof prisma }, input: z.infe
|
||||
},
|
||||
select: {
|
||||
allowDynamicBooking: true,
|
||||
credentials: true, // Don't leak credentials to the client
|
||||
...availabilityUserSelect,
|
||||
},
|
||||
});
|
||||
@@ -305,17 +310,17 @@ export async function getSchedule(input: z.infer<typeof getScheduleSchema>, ctx:
|
||||
}
|
||||
let currentSeats: CurrentSeats | undefined;
|
||||
|
||||
let users = eventType.users.map((user) => ({
|
||||
let usersWithCredentials = eventType.users.map((user) => ({
|
||||
isFixed: !eventType.schedulingType || eventType.schedulingType === SchedulingType.COLLECTIVE,
|
||||
...user,
|
||||
}));
|
||||
// overwrite if it is a team event & hosts is set, otherwise keep using users.
|
||||
if (eventType.schedulingType && !!eventType.hosts?.length) {
|
||||
users = eventType.hosts.map(({ isFixed, user }) => ({ isFixed, ...user }));
|
||||
usersWithCredentials = eventType.hosts.map(({ isFixed, user }) => ({ isFixed, ...user }));
|
||||
}
|
||||
/* We get all users working hours and busy slots */
|
||||
const userAvailability = await Promise.all(
|
||||
users.map(async (currentUser) => {
|
||||
usersWithCredentials.map(async (currentUser) => {
|
||||
const {
|
||||
busy,
|
||||
workingHours,
|
||||
@@ -398,7 +403,7 @@ export async function getSchedule(input: z.infer<typeof getScheduleSchema>, ctx:
|
||||
/* FIXME: For some reason this returns undefined while testing in Jest */
|
||||
(await ctx.prisma.selectedSlots.findMany({
|
||||
where: {
|
||||
userId: { in: users.map((user) => user.id) },
|
||||
userId: { in: usersWithCredentials.map((user) => user.id) },
|
||||
releaseAt: { gt: dayjs.utc().format() },
|
||||
},
|
||||
select: {
|
||||
@@ -527,13 +532,19 @@ export async function getSchedule(input: z.infer<typeof getScheduleSchema>, ctx:
|
||||
) => {
|
||||
// TODO: Adds unit tests to prevent regressions in getSchedule (try multiple timezones)
|
||||
const time = _time.tz(input.timeZone);
|
||||
|
||||
r[time.format("YYYY-MM-DD")] = r[time.format("YYYY-MM-DD")] || [];
|
||||
r[time.format("YYYY-MM-DD")].push({
|
||||
...passThroughProps,
|
||||
time: time.toISOString(),
|
||||
users: (eventType.hosts ? eventType.hosts.map((host) => host.user) : eventType.users).map(
|
||||
(user) => user.username || ""
|
||||
),
|
||||
users: (eventType.hosts
|
||||
? eventType.hosts.map((hostUserWithCredentials) => {
|
||||
const { user } = hostUserWithCredentials;
|
||||
const { credentials: _credentials, ...hostUser } = user;
|
||||
return hostUser;
|
||||
})
|
||||
: eventType.users
|
||||
).map((user) => user.username || ""),
|
||||
// Conditionally add the attendees and booking id to slots object if there is already a booking during that time
|
||||
...(currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString()) && {
|
||||
attendees:
|
||||
|
||||
@@ -520,6 +520,7 @@ export const viewerTeamsRouter = router({
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
credentials: true, // needed for getUserAvailability
|
||||
...availabilityUserSelect,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user