* move SystemField to features * migrate workflow service * merge two tests for team repository * update imports and migrate team repository * migrate delegation credential repository * migrate credential repository * migrate entityPermissionUtils * migrate hashedLink service and repository * migrate membership service * update imports * remove file * migrate buildEventUrlFromBooking * migrate getAllUserBookings to features * update imports * update organizationMock * migrate slots * migrate date-ranges to schedules dir * migrate getAggregatedAvailability * fix * refactor * migrate useCreateEventType hook to features * migrate assignValueToUser * migrate validateUsername to auth features * migrate system field back to lib * migrate getLabelValueMapFromResponses back to lib * update imports * use relative path * fix type checks * fix * fix * fix tests * update gh codeowners * fix * fix
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import { Dub } from "dub-package";
|
|
|
|
import logger from "@calcom/lib/logger";
|
|
import { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
|
|
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",
|
|
});
|
|
}
|
|
}
|