diff --git a/packages/app-store/apps.keys-schemas.generated.ts b/packages/app-store/apps.keys-schemas.generated.ts index ecd6547d98..b780d034f1 100644 --- a/packages/app-store/apps.keys-schemas.generated.ts +++ b/packages/app-store/apps.keys-schemas.generated.ts @@ -6,6 +6,7 @@ import { appKeysSchema as alby_zod_ts } from "./alby/zod"; import { appKeysSchema as basecamp3_zod_ts } from "./basecamp3/zod"; import { appKeysSchema as closecom_zod_ts } from "./closecom/zod"; import { appKeysSchema as dailyvideo_zod_ts } from "./dailyvideo/zod"; +import { appKeysSchema as dub_zod_ts } from "./dub/zod"; import { appKeysSchema as fathom_zod_ts } from "./fathom/zod"; import { appKeysSchema as feishucalendar_zod_ts } from "./feishucalendar/zod"; import { appKeysSchema as ga4_zod_ts } from "./ga4/zod"; @@ -55,6 +56,7 @@ export const appKeysSchemas = { basecamp3: basecamp3_zod_ts, closecom: closecom_zod_ts, dailyvideo: dailyvideo_zod_ts, + dub: dub_zod_ts, fathom: fathom_zod_ts, feishucalendar: feishucalendar_zod_ts, ga4: ga4_zod_ts, diff --git a/packages/app-store/apps.schemas.generated.ts b/packages/app-store/apps.schemas.generated.ts index 3e62914377..3970c1a229 100644 --- a/packages/app-store/apps.schemas.generated.ts +++ b/packages/app-store/apps.schemas.generated.ts @@ -6,6 +6,7 @@ import { appDataSchema as alby_zod_ts } from "./alby/zod"; import { appDataSchema as basecamp3_zod_ts } from "./basecamp3/zod"; import { appDataSchema as closecom_zod_ts } from "./closecom/zod"; import { appDataSchema as dailyvideo_zod_ts } from "./dailyvideo/zod"; +import { appDataSchema as dub_zod_ts } from "./dub/zod"; import { appDataSchema as fathom_zod_ts } from "./fathom/zod"; import { appDataSchema as feishucalendar_zod_ts } from "./feishucalendar/zod"; import { appDataSchema as ga4_zod_ts } from "./ga4/zod"; @@ -55,6 +56,7 @@ export const appDataSchemas = { basecamp3: basecamp3_zod_ts, closecom: closecom_zod_ts, dailyvideo: dailyvideo_zod_ts, + dub: dub_zod_ts, fathom: fathom_zod_ts, feishucalendar: feishucalendar_zod_ts, ga4: ga4_zod_ts, diff --git a/packages/app-store/dub/api/add.ts b/packages/app-store/dub/api/add.ts index e91b9a0281..4faf4f8ee8 100644 --- a/packages/app-store/dub/api/add.ts +++ b/packages/app-store/dub/api/add.ts @@ -20,6 +20,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { throw new HttpError({ statusCode: 400, message: "Session user must have an email" }); } + const { teamId } = req.query; const { client_id, redirect_uris } = await getParsedAppKeysFromSlug("dub", dubAppKeysSchema); const url = new URL("https://app.dub.co/oauth/authorize"); @@ -27,6 +28,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { url.searchParams.append("redirect_uri", redirect_uris); url.searchParams.append("response_type", "code"); url.searchParams.append("scope", scopeString); + if (typeof teamId === "string" && !Number.isNaN(Number(teamId))) { + url.searchParams.append("state", JSON.stringify({ teamId: Number(teamId) })); + } const oauthUrl = url.toString(); return res.status(200).json({ url: oauthUrl }); diff --git a/packages/app-store/dub/lib/AnalyticsService.ts b/packages/app-store/dub/lib/AnalyticsService.ts index fd2ca86e8f..14af8e6b50 100644 --- a/packages/app-store/dub/lib/AnalyticsService.ts +++ b/packages/app-store/dub/lib/AnalyticsService.ts @@ -73,6 +73,19 @@ export default class DubService implements AnalyticsService { "Content-Type": "application/x-www-form-urlencoded", }, }); + + if (!response.ok) { + const res = await response.json(); + if (response.status === 401) { + await CredentialRepository.updateCredentialById({ + id: this.credential.id, + data: { + invalid: true, + }, + }); + } + throw new Error(`Error refreshing dub token: ${res?.error?.message ?? response.statusText}`); + } return await response.json(); }, "dub", @@ -89,6 +102,7 @@ export default class DubService implements AnalyticsService { return newToken; } catch (err) { this.log.error(err); + throw err; } } diff --git a/packages/app-store/dub/zod.ts b/packages/app-store/dub/zod.ts new file mode 100644 index 0000000000..959007b719 --- /dev/null +++ b/packages/app-store/dub/zod.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +export const appKeysSchema = z.object({ + redirect_uris: z.string(), + client_id: z.string(), + client_secret: z.string(), +}); + +export const appDataSchema = z.object({}); diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index eab92f03cc..80687c3812 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -2195,6 +2195,7 @@ async function handler( email: bookerEmail, eventName: "Cal.com lead", }, + isTeamEventType, }); } diff --git a/packages/lib/analyticsManager/handleAnalyticsEvents.ts b/packages/lib/analyticsManager/handleAnalyticsEvents.ts index 9aec16c889..c068594c5e 100644 --- a/packages/lib/analyticsManager/handleAnalyticsEvents.ts +++ b/packages/lib/analyticsManager/handleAnalyticsEvents.ts @@ -9,18 +9,24 @@ interface HandleAnalyticsEventsProps { name: string; eventName: string; }; + isTeamEventType: boolean; } export const handleAnalyticsEvents = async ({ credentials, rawBookingData, bookingInfo, + isTeamEventType, }: HandleAnalyticsEventsProps) => { const { dub_id } = await rawBookingData; if (!dub_id || typeof dub_id !== "string") return; - const dubCredential = credentials.find((cred) => cred.appId === "dub"); + const dubCredential = credentials.find((cred) => { + if (cred.appId !== "dub") return false; + if (isTeamEventType && !cred.teamId) return false; + return true; + }); if (!dubCredential) return; diff --git a/packages/lib/server/repository/credential.ts b/packages/lib/server/repository/credential.ts index 6fbf39017c..99eb77a13e 100644 --- a/packages/lib/server/repository/credential.ts +++ b/packages/lib/server/repository/credential.ts @@ -23,6 +23,7 @@ type CredentialUpdateInput = { userId?: number; appId?: string; delegationCredentialId?: string | null; + invalid?: boolean; }; export class CredentialRepository {