Files
calendar/packages/app-store/dub/lib/AnalyticsService.ts
T
Amit SharmaGitHubcubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
50c15f0d51 fix: dub installation on teams (#21654)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-06-11 18:17:55 +02:00

123 lines
3.6 KiB
TypeScript

import { Dub } from "dub-package";
import logger from "@calcom/lib/logger";
import { CredentialRepository } from "@calcom/lib/server/repository/credential";
import type { AnalyticsService, SendEventProps } from "@calcom/types/AnalyticsService";
import type { CredentialPayload } from "@calcom/types/Credential";
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
import refreshOAuthTokens from "../../_utils/oauth/refreshOAuthTokens";
import type { DubOAuthToken } from "./type";
export default class DubService implements AnalyticsService {
private dubClient?: Dub;
private client_id = "";
private client_secret = "";
private log = logger.getSubLogger({ prefix: ["[[lib]] dub"] });
private credential: CredentialPayload;
constructor(credential: CredentialPayload) {
this.credential = credential;
this.client_id = "";
this.client_secret = "";
}
private async initClient() {
const appKeys = await getAppKeysFromSlug("dub");
const { client_id, client_secret } = appKeys;
if (!client_id || !client_secret) {
this.log.error("Dub.co app keys missing!");
return;
}
this.client_id = client_id as string;
this.client_secret = client_secret as string;
let token = this.credential.key as unknown as DubOAuthToken | undefined;
if (!token) return;
const isTokenExpired = (token: DubOAuthToken) => {
if (!token || !token.access_token) return true;
if (token.expiry_date) {
return token.expiry_date < Date.now();
}
return false;
};
if (isTokenExpired(token)) {
token = await this.refreshAccessToken(token.refresh_token);
if (!token) return;
}
this.dubClient = new Dub({ token: token.access_token });
}
private async refreshAccessToken(refreshToken: string): Promise<DubOAuthToken | undefined> {
try {
if (!refreshToken) return;
const newToken: DubOAuthToken = await refreshOAuthTokens(
async () => {
const response = await fetch(`https://api.dub.co/oauth/token`, {
method: "POST",
body: new URLSearchParams({
client_id: this.client_id,
client_secret: this.client_secret,
grant_type: "refresh_token",
refresh_token: refreshToken,
}).toString(),
headers: {
"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",
this.credential.userId
);
newToken.expiry_date = Date.now() + newToken.expires_in * 1000;
await CredentialRepository.updateCredentialById({
id: this.credential.id,
data: { key: newToken as any },
});
return newToken;
} catch (err) {
this.log.error(err);
throw err;
}
}
async sendEvent({ name, email, eventName, id, externalId }: SendEventProps): Promise<void> {
await this.initClient();
if (!this.dubClient) return;
await this.dubClient.track.lead({
clickId: id,
customerName: name,
customerEmail: email,
externalId: externalId ?? email,
eventName: eventName ?? "Cal.com lead",
});
}
}