chore: Insights readonly DB client (#12373)

This commit is contained in:
Erik 2023-11-15 18:50:20 -03:00 committed by GitHub
parent eb97e1660b
commit 6a8726f5f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 36 deletions

View File

@ -25,6 +25,7 @@ CALCOM_LICENSE_KEY=
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso" DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
UPSTASH_REDIS_REST_URL= UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN= UPSTASH_REDIS_REST_TOKEN=
INSIGHTS_DATABASE_URL=
# Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy # Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy
# Cold boots will be faster and you'll be able to scale your DB independently of your app. # Cold boots will be faster and you'll be able to scale your DB independently of your app.

View File

@ -3,7 +3,7 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import superjson from "superjson"; import superjson from "superjson";
import { CALCOM_VERSION } from "@calcom/lib/constants"; import { CALCOM_VERSION } from "@calcom/lib/constants";
import prisma from "@calcom/prisma"; import prisma, { readonlyPrisma } from "@calcom/prisma";
import { createProxySSGHelpers } from "@calcom/trpc/react/ssg"; import { createProxySSGHelpers } from "@calcom/trpc/react/ssg";
import { appRouter } from "@calcom/trpc/server/routers/_app"; import { appRouter } from "@calcom/trpc/server/routers/_app";
@ -31,6 +31,7 @@ export async function ssgInit<TParams extends { locale?: string }>(opts: GetStat
transformer: superjson, transformer: superjson,
ctx: { ctx: {
prisma, prisma,
insightsDb: readonlyPrisma,
session: null, session: null,
locale, locale,
i18n: _i18n, i18n: _i18n,

View File

@ -1,6 +1,6 @@
import type { Dayjs } from "@calcom/dayjs"; import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
import { prisma } from "@calcom/prisma"; import { readonlyPrisma as prisma } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client"; import type { Prisma } from "@calcom/prisma/client";
import type { RawDataInput } from "./raw-data.schema"; import type { RawDataInput } from "./raw-data.schema";

View File

@ -35,7 +35,7 @@ const userBelongsToTeamProcedure = authedProcedure.use(async ({ ctx, next, rawIn
membershipWhereConditional["teamId"] = parse.data.teamId; membershipWhereConditional["teamId"] = parse.data.teamId;
} }
const membership = await ctx.prisma.membership.findFirst({ const membership = await ctx.insightsDb.membership.findFirst({
where: membershipWhereConditional, where: membershipWhereConditional,
}); });
@ -43,7 +43,7 @@ const userBelongsToTeamProcedure = authedProcedure.use(async ({ ctx, next, rawIn
// So that would mean ctx.user.organization is present // So that would mean ctx.user.organization is present
if ((parse.data.isAll && ctx.user.organizationId) || (!membership && ctx.user.organizationId)) { if ((parse.data.isAll && ctx.user.organizationId) || (!membership && ctx.user.organizationId)) {
//Look for membership type in organizationId //Look for membership type in organizationId
const membershipOrg = await ctx.prisma.membership.findFirst({ const membershipOrg = await ctx.insightsDb.membership.findFirst({
where: { where: {
userId: ctx.user.id, userId: ctx.user.id,
teamId: ctx.user.organizationId, teamId: ctx.user.organizationId,
@ -152,7 +152,7 @@ export const insightsRouter = router({
} }
if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) { if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) {
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: ctx.user.organizationId, parentId: ctx.user.organizationId,
}, },
@ -168,7 +168,7 @@ export const insightsRouter = router({
in: [ctx.user.organizationId, ...teamsFromOrg.map((t) => t.id)], in: [ctx.user.organizationId, ...teamsFromOrg.map((t) => t.id)],
}, },
}; };
const usersFromOrg = await ctx.prisma.membership.findMany({ const usersFromOrg = await ctx.insightsDb.membership.findMany({
where: { where: {
team: teamConditional, team: teamConditional,
accepted: true, accepted: true,
@ -197,7 +197,7 @@ export const insightsRouter = router({
} }
if (teamId && !isAll) { if (teamId && !isAll) {
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId: teamId, teamId: teamId,
accepted: true, accepted: true,
@ -348,7 +348,7 @@ export const insightsRouter = router({
let whereConditional: Prisma.BookingTimeStatusWhereInput = {}; let whereConditional: Prisma.BookingTimeStatusWhereInput = {};
if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) { if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) {
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: user.organizationId, parentId: user.organizationId,
}, },
@ -357,7 +357,7 @@ export const insightsRouter = router({
}, },
}); });
const usersFromOrg = await ctx.prisma.membership.findMany({ const usersFromOrg = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId: { teamId: {
in: [ctx.user.organizationId, ...teamsFromOrg.map((t) => t.id)], in: [ctx.user.organizationId, ...teamsFromOrg.map((t) => t.id)],
@ -388,7 +388,7 @@ export const insightsRouter = router({
} }
if (teamId && !isAll) { if (teamId && !isAll) {
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId, teamId,
accepted: true, accepted: true,
@ -537,7 +537,7 @@ export const insightsRouter = router({
}; };
if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) { if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) {
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: user.organizationId, parentId: user.organizationId,
}, },
@ -546,7 +546,7 @@ export const insightsRouter = router({
}, },
}); });
const usersFromOrg = await ctx.prisma.membership.findMany({ const usersFromOrg = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId: { teamId: {
in: [ctx.user.organizationId, ...teamsFromOrg.map((t) => t.id)], in: [ctx.user.organizationId, ...teamsFromOrg.map((t) => t.id)],
@ -578,7 +578,7 @@ export const insightsRouter = router({
} }
if (teamId && !isAll) { if (teamId && !isAll) {
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId, teamId,
accepted: true, accepted: true,
@ -615,7 +615,7 @@ export const insightsRouter = router({
bookingWhere.userId = memberUserId; bookingWhere.userId = memberUserId;
} }
const bookingsFromSelected = await ctx.prisma.bookingTimeStatus.groupBy({ const bookingsFromSelected = await ctx.insightsDb.bookingTimeStatus.groupBy({
by: ["eventTypeId"], by: ["eventTypeId"],
where: bookingWhere, where: bookingWhere,
_count: { _count: {
@ -639,7 +639,7 @@ export const insightsRouter = router({
}, },
}; };
const eventTypesFrom = await ctx.prisma.eventType.findMany({ const eventTypesFrom = await ctx.insightsDb.eventType.findMany({
select: { select: {
id: true, id: true,
title: true, title: true,
@ -752,7 +752,7 @@ export const insightsRouter = router({
} }
if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) { if (isAll && ctx.user.isOwnerAdminOfParentTeam && ctx.user.organizationId) {
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: ctx.user?.organizationId, parentId: ctx.user?.organizationId,
}, },
@ -777,7 +777,7 @@ export const insightsRouter = router({
} }
if (teamId && !isAll) { if (teamId && !isAll) {
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId, teamId,
accepted: true, accepted: true,
@ -832,7 +832,7 @@ export const insightsRouter = router({
const startDate = dayjs(date).startOf(startOfEndOf); const startDate = dayjs(date).startOf(startOfEndOf);
const endDate = dayjs(date).endOf(startOfEndOf); const endDate = dayjs(date).endOf(startOfEndOf);
const bookingsInTimeRange = await ctx.prisma.bookingTimeStatus.findMany({ const bookingsInTimeRange = await ctx.insightsDb.bookingTimeStatus.findMany({
select: { select: {
eventLength: true, eventLength: true,
}, },
@ -896,7 +896,7 @@ export const insightsRouter = router({
if (isAll && user.isOwnerAdminOfParentTeam && user.organizationId) { if (isAll && user.isOwnerAdminOfParentTeam && user.organizationId) {
delete bookingWhere.teamId; delete bookingWhere.teamId;
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: user?.organizationId, parentId: user?.organizationId,
}, },
@ -904,7 +904,7 @@ export const insightsRouter = router({
id: true, id: true,
}, },
}); });
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId: { teamId: {
in: [user?.organizationId, ...teamsFromOrg.map((t) => t.id)], in: [user?.organizationId, ...teamsFromOrg.map((t) => t.id)],
@ -931,7 +931,7 @@ export const insightsRouter = router({
} }
if (teamId && !isAll) { if (teamId && !isAll) {
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId, teamId,
accepted: true, accepted: true,
@ -956,7 +956,7 @@ export const insightsRouter = router({
]; ];
} }
const bookingsFromTeam = await ctx.prisma.bookingTimeStatus.groupBy({ const bookingsFromTeam = await ctx.insightsDb.bookingTimeStatus.groupBy({
by: ["userId"], by: ["userId"],
where: bookingWhere, where: bookingWhere,
_count: { _count: {
@ -977,7 +977,7 @@ export const insightsRouter = router({
return []; return [];
} }
const usersFromTeam = await ctx.prisma.user.findMany({ const usersFromTeam = await ctx.insightsDb.user.findMany({
where: { where: {
id: { id: {
in: userIds as number[], in: userIds as number[],
@ -1030,7 +1030,7 @@ export const insightsRouter = router({
if (isAll && user.isOwnerAdminOfParentTeam) { if (isAll && user.isOwnerAdminOfParentTeam) {
delete bookingWhere.teamId; delete bookingWhere.teamId;
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: user?.organizationId, parentId: user?.organizationId,
}, },
@ -1038,7 +1038,7 @@ export const insightsRouter = router({
id: true, id: true,
}, },
}); });
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId: { teamId: {
in: teamsFromOrg.map((t) => t.id), in: teamsFromOrg.map((t) => t.id),
@ -1066,7 +1066,7 @@ export const insightsRouter = router({
} }
if (teamId && !isAll) { if (teamId && !isAll) {
const usersFromTeam = await ctx.prisma.membership.findMany({ const usersFromTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId, teamId,
accepted: true, accepted: true,
@ -1089,7 +1089,7 @@ export const insightsRouter = router({
]; ];
} }
const bookingsFromTeam = await ctx.prisma.bookingTimeStatus.groupBy({ const bookingsFromTeam = await ctx.insightsDb.bookingTimeStatus.groupBy({
by: ["userId"], by: ["userId"],
where: bookingWhere, where: bookingWhere,
_count: { _count: {
@ -1109,7 +1109,7 @@ export const insightsRouter = router({
if (userIds.length === 0) { if (userIds.length === 0) {
return []; return [];
} }
const usersFromTeam = await ctx.prisma.user.findMany({ const usersFromTeam = await ctx.insightsDb.user.findMany({
where: { where: {
id: { id: {
in: userIds as number[], in: userIds as number[],
@ -1138,7 +1138,7 @@ export const insightsRouter = router({
const user = ctx.user; const user = ctx.user;
// Fetch user data // Fetch user data
const userData = await ctx.prisma.user.findUnique({ const userData = await ctx.insightsDb.user.findUnique({
where: { where: {
id: user.id, id: user.id,
}, },
@ -1167,7 +1167,7 @@ export const insightsRouter = router({
// Validate if user belongs to org as admin/owner // Validate if user belongs to org as admin/owner
if (user.organizationId) { if (user.organizationId) {
const teamsFromOrg = await ctx.prisma.team.findMany({ const teamsFromOrg = await ctx.insightsDb.team.findMany({
where: { where: {
parentId: user.organizationId, parentId: user.organizationId,
}, },
@ -1178,7 +1178,7 @@ export const insightsRouter = router({
logo: true, logo: true,
}, },
}); });
const orgTeam = await ctx.prisma.team.findUnique({ const orgTeam = await ctx.insightsDb.team.findUnique({
where: { where: {
id: user.organizationId, id: user.organizationId,
}, },
@ -1214,7 +1214,7 @@ export const insightsRouter = router({
} }
// Look if user it's admin/owner in multiple teams // Look if user it's admin/owner in multiple teams
const belongsToTeams = await ctx.prisma.membership.findMany({ const belongsToTeams = await ctx.insightsDb.membership.findMany({
where: membershipConditional, where: membershipConditional,
include: { include: {
team: { team: {
@ -1255,7 +1255,7 @@ export const insightsRouter = router({
} }
if (isAll && user.organizationId && user.isOwnerAdminOfParentTeam) { if (isAll && user.organizationId && user.isOwnerAdminOfParentTeam) {
const usersInTeam = await ctx.prisma.membership.findMany({ const usersInTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
team: { team: {
parentId: user.organizationId, parentId: user.organizationId,
@ -1271,7 +1271,7 @@ export const insightsRouter = router({
return usersInTeam.map((membership) => membership.user); return usersInTeam.map((membership) => membership.user);
} }
const membership = await ctx.prisma.membership.findFirst({ const membership = await ctx.insightsDb.membership.findFirst({
where: { where: {
userId: user.id, userId: user.id,
teamId, teamId,
@ -1292,7 +1292,7 @@ export const insightsRouter = router({
return [membership.user]; return [membership.user];
} }
const usersInTeam = await ctx.prisma.membership.findMany({ const usersInTeam = await ctx.insightsDb.membership.findMany({
where: { where: {
teamId, teamId,
accepted: true, accepted: true,

View File

@ -59,6 +59,14 @@ const prismaWithClientExtensions = prismaWithoutClientExtensions
export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions; export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions;
// This prisma instance is meant to be used only for READ operations.
// If self hosting, feel free to leave INSIGHTS_DATABASE_URL as empty and `readonlyPrisma` will default to `prisma`.
export const readonlyPrisma = process.env.INSIGHTS_DATABASE_URL
? customPrisma({
datasources: { db: { url: process.env.INSIGHTS_DATABASE_URL } },
})
: prisma;
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== "production") {
globalForPrisma.prismaWithoutClientExtensions = prismaWithoutClientExtensions; globalForPrisma.prismaWithoutClientExtensions = prismaWithoutClientExtensions;
globalForPrisma.prismaWithClientExtensions = prisma; globalForPrisma.prismaWithClientExtensions = prisma;

View File

@ -4,7 +4,7 @@ import type { Session } from "next-auth";
import type { serverSideTranslations } from "next-i18next/serverSideTranslations"; import type { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { getLocale } from "@calcom/features/auth/lib/getLocale"; import { getLocale } from "@calcom/features/auth/lib/getLocale";
import prisma from "@calcom/prisma"; import prisma, { readonlyPrisma } from "@calcom/prisma";
import type { SelectedCalendar, User as PrismaUser } from "@calcom/prisma/client"; import type { SelectedCalendar, User as PrismaUser } from "@calcom/prisma/client";
import type { CreateNextContextOptions } from "@trpc/server/adapters/next"; import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
@ -53,6 +53,7 @@ export type GetSessionFn =
export async function createContextInner(opts: CreateInnerContextOptions) { export async function createContextInner(opts: CreateInnerContextOptions) {
return { return {
prisma, prisma,
insightsDb: readonlyPrisma,
...opts, ...opts,
}; };
} }

View File

@ -247,6 +247,7 @@
"INTEGRATION_TEST_MODE", "INTEGRATION_TEST_MODE",
"INTERCOM_SECRET", "INTERCOM_SECRET",
"INTERCOM_SECRET", "INTERCOM_SECRET",
"INSIGHTS_DATABASE_URL",
"IP_BANLIST", "IP_BANLIST",
"LARK_OPEN_APP_ID", "LARK_OPEN_APP_ID",
"LARK_OPEN_APP_SECRET", "LARK_OPEN_APP_SECRET",