diff --git a/apps/api/v2/next-i18next.config.js b/apps/api/v2/next-i18next.config.js deleted file mode 100644 index a07cf20981782..0000000000000 --- a/apps/api/v2/next-i18next.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -const path = require("path"); -const i18nConfig = require("@calcom/config/next-i18next.config"); - -/** @type {import("next-i18next").UserConfig} */ -const config = { - ...i18nConfig, - localePath: path.resolve("../../web/public/static/locales"), -}; - -module.exports = config; diff --git a/apps/api/v2/src/app.ts b/apps/api/v2/src/app.ts index 417839abbc186..eb798b2cc5e82 100644 --- a/apps/api/v2/src/app.ts +++ b/apps/api/v2/src/app.ts @@ -4,7 +4,7 @@ import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter"; import { SentryFilter } from "@/filters/sentry-exception.filter"; import { ZodExceptionFilter } from "@/filters/zod-exception.filter"; import type { ValidationError } from "@nestjs/common"; -import { BadRequestException, RequestMethod, ValidationPipe, VersioningType } from "@nestjs/common"; +import { BadRequestException, ValidationPipe, VersioningType } from "@nestjs/common"; import { HttpAdapterHost } from "@nestjs/core"; import type { NestExpressApplication } from "@nestjs/platform-express"; import * as Sentry from "@sentry/node"; diff --git a/apps/api/v2/src/ee/event-types/event-types.repository.ts b/apps/api/v2/src/ee/event-types/event-types.repository.ts index 560513b98255e..cc696cdd12a14 100644 --- a/apps/api/v2/src/ee/event-types/event-types.repository.ts +++ b/apps/api/v2/src/ee/event-types/event-types.repository.ts @@ -1,11 +1,11 @@ import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input"; import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; -import type { PrismaClient } from "@calcom/prisma"; import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { UserWithProfile } from "@/modules/users/users.repository"; import { Injectable } from "@nestjs/common"; import { getEventTypeById } from "@calcom/platform-libraries"; +import type { PrismaClient } from "@calcom/prisma"; @Injectable() export class EventTypesRepository { diff --git a/packages/embeds/embed-react/playwright/tests/basic.e2e.ts b/packages/embeds/embed-react/playwright/tests/basic.e2e.ts index d5bdb5432443e..300a99f1cab95 100644 --- a/packages/embeds/embed-react/playwright/tests/basic.e2e.ts +++ b/packages/embeds/embed-react/playwright/tests/basic.e2e.ts @@ -1,6 +1,7 @@ import { expect } from "@playwright/test"; import { getEmbedIframe } from "@calcom/embed-core/playwright/lib/testUtils"; +// eslint-disable-next-line no-restricted-imports import { test } from "@calcom/web/playwright/lib/fixtures"; test.describe("React Embed", () => { diff --git a/packages/eslint-plugin/src/configs/recommended.ts b/packages/eslint-plugin/src/configs/recommended.ts index 05c5405e7580c..2e4d48e90b401 100644 --- a/packages/eslint-plugin/src/configs/recommended.ts +++ b/packages/eslint-plugin/src/configs/recommended.ts @@ -6,6 +6,7 @@ const recommended = { "@calcom/eslint/deprecated-imports-next-router": "error", "@calcom/eslint/avoid-web-storage": "error", "@calcom/eslint/avoid-prisma-client-import-for-enums": "error", + "@calcom/eslint/no-prisma-include-true": "warn", }, }; diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index da144ae093dba..9f67c105d96f6 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -5,5 +5,6 @@ export default { "deprecated-imports": require("./deprecated-imports").default, "avoid-web-storage": require("./avoid-web-storage").default, "avoid-prisma-client-import-for-enums": require("./avoid-prisma-client-import-for-enums").default, + "no-prisma-include-true": require("./no-prisma-include-true").default, "deprecated-imports-next-router": require("./deprecated-imports-next-router").default, } as ESLint.Plugin["rules"]; diff --git a/packages/eslint-plugin/src/rules/no-prisma-include-true.ts b/packages/eslint-plugin/src/rules/no-prisma-include-true.ts new file mode 100644 index 0000000000000..d1dac23b39c8f --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-prisma-include-true.ts @@ -0,0 +1,100 @@ +import type { TSESTree } from "@typescript-eslint/utils"; +import { ESLintUtils } from "@typescript-eslint/utils"; +import type { ReportDescriptor } from "@typescript-eslint/utils/dist/ts-eslint"; + +const createRule = ESLintUtils.RuleCreator((name) => `https://developer.cal.com/eslint/rule/${name}`); + +const assesIncludePropertyIncludesTrue = ( + includeProperty: TSESTree.Property, + reporter: { (reportObj: ReportDescriptor<"no-prisma-include-true">): void } +) => { + if (includeProperty.value.type === "ObjectExpression") { + includeProperty.value.properties.forEach((childProperty) => { + if ( + childProperty.type === "Property" && + childProperty.value.type === "Literal" && + childProperty.value.value === true + ) { + reporter({ + node: childProperty, + messageId: "no-prisma-include-true", + }); + } + }); + } +}; + +const searchIncludeProperty = ( + property: TSESTree.Property, + reporter: { (reportObj: ReportDescriptor<"no-prisma-include-true">): void } +) => { + if (property.type === "Property") { + // If property is include, check if it has a child property with value true + if (property.key.type === "Identifier" && property.key.name === "include") { + assesIncludePropertyIncludesTrue(property, reporter); + } + + // If property value is also an object, recursively search for include property + if (property.value.type === "ObjectExpression") { + property.value.properties.forEach((childProperty) => { + if (childProperty.type === "Property") { + searchIncludeProperty(childProperty, reporter); + } + }); + } + } +}; + +const rule = createRule({ + create: function (context) { + return { + CallExpression(node) { + if (!(node.callee as TSESTree.MemberExpression).property) { + return null; + } + + const nodeName = ((node.callee as TSESTree.MemberExpression).property as TSESTree.Identifier).name; + + if ( + !["findUnique", "findUniqueOrThrow", "findFirst", "findFirstOrThrow", "findMany"].includes(nodeName) + ) { + return null; + } + + const nodeArgs = node.arguments[0] as TSESTree.ObjectExpression; + if (!nodeArgs) { + return null; + } + + const backReporter = (reportObj: ReportDescriptor<"no-prisma-include-true">) => { + context.report(reportObj); + }; + + nodeArgs.properties?.forEach((property) => { + if (property.type === "Property") { + searchIncludeProperty(property, backReporter); + } + }); + return null; + }, + }; + }, + + name: "no-prisma-include-true", + meta: { + type: "problem", + docs: { + description: + "Disallow passing argument object with include: { AnyPropertyName: true } to prisma methods", + recommended: "error", + }, + messages: { + "no-prisma-include-true": `Do not pass argument object with include: { AnyPropertyName: true } to prisma methods`, + }, + fixable: "code", + schema: [], + }, + defaultOptions: [], +}); + +export default rule; diff --git a/packages/lib/entityPermissionUtils.ts b/packages/lib/entityPermissionUtils.ts index 6031c0a50b6d2..a5200206e2775 100644 --- a/packages/lib/entityPermissionUtils.ts +++ b/packages/lib/entityPermissionUtils.ts @@ -64,7 +64,12 @@ async function getMembership(teamId: number | null, userId: number) { }, }, include: { - members: true, + members: { + select: { + userId: true, + role: true, + }, + }, }, }) : null; @@ -81,8 +86,7 @@ export async function canCreateEntity({ if (targetTeamId) { // If it doesn't exist and it is being created for a team. Check if user is the member of the team const membership = await getMembership(targetTeamId, userId); - const creationAllowed = membership ? withRoleCanCreateEntity(membership.role) : false; - return creationAllowed; + return membership ? withRoleCanCreateEntity(membership.role) : false; } return true; } diff --git a/packages/lib/server/queries/teams/index.ts b/packages/lib/server/queries/teams/index.ts index 6543966e1fe84..fffa5ea9d740b 100644 --- a/packages/lib/server/queries/teams/index.ts +++ b/packages/lib/server/queries/teams/index.ts @@ -261,7 +261,15 @@ export async function isTeamAdmin(userId: number, teamId: number) { accepted: true, OR: [{ role: "ADMIN" }, { role: "OWNER" }], }, - include: { team: true }, + include: { + team: { + select: { + metadata: true, + parentId: true, + isOrganization: true, + }, + }, + }, }); if (!team) return false; return team; diff --git a/packages/lib/server/repository/profile.ts b/packages/lib/server/repository/profile.ts index 2614bea6c8d5d..867ad3898f020 100644 --- a/packages/lib/server/repository/profile.ts +++ b/packages/lib/server/repository/profile.ts @@ -348,7 +348,11 @@ export class ProfileRepository { user: { select: userSelect, }, - movedFromUser: true, + movedFromUser: { + select: { + id: true, + }, + }, organization: { select: { calVideoLogo: true,