Compare commits

...
Author SHA1 Message Date
github-actions[bot]andLauris Skraucis 979e1fd28b chore: version packages (#22143)
* chore: version packages

* Update package.json

* Update CHANGELOG.md

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
2025-06-30 07:51:11 +00:00
Lauris Skraucis 3437c4331d fix: changesets atoms size (#22134)
* fix: changesets atoms npm package size

* test changesets entry
2025-06-30 07:22:09 +00:00
github-actions[bot] 616d62094c chore: version packages (#22116)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-29 13:25:48 +02:00
ce7ff1c43c fix(signup): add missing key prop to feature list (#21437) (#21506)
* fix(signup): add missing key prop to feature list (#21437)

* refactor

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-06-28 09:27:25 +00:00
Romit e27e41bc16 fix (#22112) 2025-06-28 14:30:54 +05:30
Hariom Balhara 93e183e6c3 fix: Zoom - More logging and fix for unexpected errors seen in logs (#22118)
* fix: Use WEBAPP_URL_FOR_OAUTH to be able to use http://localhost:3000
for local testing

* In case of JSON.parse crash that could happen if zoom reponsed with xml

* Fix cases where auto_recording and default_password_for_scheduled_meetings could be nullish
2025-06-28 08:11:25 +00:00
Lauris Skraucis 88a2bd3317 fix: changesets release memory (#22107)
* chore: increase instance size for changesets

* test release

* Update CHANGELOG.md
2025-06-28 04:37:47 +00:00
Keith Williams 45caaa55b0 chore: Use DBA team for prisma CODEOWNER (#22109) 2025-06-27 23:01:32 +00:00
14 changed files with 123 additions and 49 deletions
+1 -1
View File
@@ -24,7 +24,7 @@
/packages/lib/server/getLuckyUser.ts @calcom/Foundation
/packages/lib/slots.ts @calcom/Foundation
/packages/platform/**/* @calcom/Platform @calcom/Foundation
/packages/prisma/**/* @calcom/Foundation
/packages/prisma/**/* @calcom/DBA
/packages/trpc/server/routers/viewer/bookings/confirm.handler.ts @calcom/Foundation
/packages/trpc/server/routers/viewer/bookings/get.handler.ts @calcom/Foundation
/packages/trpc/server/routers/viewer/slots/**/* @calcom/Foundation
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
contents: write # to create release by changesets/action below
pull-requests: write # to create pull request by changesets/action below
name: Release
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- name: Checkout Repo
uses: actions/checkout@v4
+16 -18
View File
@@ -691,25 +691,23 @@ export default function Signup({
/>
</div>
<div className="mr-12 mt-8 hidden h-full w-full grid-cols-3 gap-4 overflow-hidden lg:grid">
{FEATURES.map((feature) => (
<>
<div className="max-w-52 mb-8 flex flex-col leading-none sm:mb-0">
<div className="text-emphasis items-center">
<Icon name={feature.icon} className="mb-1 h-4 w-4" />
<span className="text-sm font-medium">{t(feature.title)}</span>
</div>
<div className="text-subtle text-sm">
<p>
{t(
feature.description,
feature.i18nOptions && {
...feature.i18nOptions,
}
)}
</p>
</div>
{FEATURES.map((feature, index) => (
<div key={index} className="max-w-52 mb-8 flex flex-col leading-none sm:mb-0">
<div className="text-emphasis items-center">
<Icon name={feature.icon} className="mb-1 h-4 w-4" />
<span className="text-sm font-medium">{t(feature.title)}</span>
</div>
</>
<div className="text-subtle text-sm">
<p>
{t(
feature.description,
feature.i18nOptions && {
...feature.i18nOptions,
}
)}
</p>
</div>
</div>
))}
</div>
</div>
+1 -1
View File
@@ -82,7 +82,7 @@
"docker-stop-api": "docker ps --filter 'ancestor=cal-api' -q | xargs docker stop",
"changesets-add": "yarn changeset add",
"changesets-version": "yarn changeset version",
"changesets-release": "NODE_OPTIONS='--max_old_space_size=8192' turbo run build --filter=@calcom/atoms && yarn changeset publish"
"changesets-release": "NODE_OPTIONS='--max_old_space_size=12288' turbo run build --filter=@calcom/atoms && yarn changeset publish"
},
"devDependencies": {
"@changesets/changelog-github": "^0.5.1",
@@ -1,7 +1,12 @@
/**
* Manages OAuth2.0 tokens for an app and resourceOwner. It automatically refreshes the token when needed.
* It is aware of the credential sync endpoint and can sync the token from the third party source.
* It is unaware of Prisma and App logic. It is just a utility to manage OAuth2.0 tokens with life cycle methods
* Manages OAuth2.0 as well as JWT tokens(For JWT tokens, only Google Calendar use it at the moment) for an app and resourceOwner.
* What it does
* - It automatically refreshes the token if needed when making a request.
* - It is aware of the credential sync endpoint and can sync the token from the third party source.
* - It is kept unaware of Prisma and App logic. It is just a utility to manage OAuth2.0 tokens with life cycle methods
*
* What it doesn't do yet
* - It doesn't have a flow to re-send the request if the access-token had been communicated as invalid after making the request itself. It relies on the caller to make the next request in which it will actually refresh the token.
*
* For a recommended usage example, see Zoom VideoApiAdapter.ts
*/
@@ -89,7 +94,13 @@ export class OAuthManager {
currentTokenObject,
fetchNewTokenObject,
updateTokenObject,
/**
* The fn must not crash. It is the responsibility of the caller to handle any error and appropriately decide what to return
*/
isTokenObjectUnusable,
/**
* The fn must not crash. It is the responsibility of the caller to handle any error and appropriately decide what to return
*/
isAccessTokenUnusable,
invalidateTokenObject,
expireAccessToken,
+2 -2
View File
@@ -1,7 +1,7 @@
import type { NextApiRequest } from "next";
import { stringify } from "querystring";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { WEBAPP_URL_FOR_OAUTH } from "@calcom/lib/constants";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
@@ -26,7 +26,7 @@ async function handler(req: NextApiRequest) {
const params = {
response_type: "code",
client_id,
redirect_uri: `${WEBAPP_URL}/api/integrations/zoomvideo/callback`,
redirect_uri: `${WEBAPP_URL_FOR_OAUTH}/api/integrations/zoomvideo/callback`,
state,
};
const query = stringify(params);
+2 -2
View File
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { WEBAPP_URL_FOR_OAUTH } from "@calcom/lib/constants";
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
import prisma from "@calcom/prisma";
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { code } = req.query;
const { client_id, client_secret } = await getZoomAppKeys();
const redirectUri = encodeURI(`${WEBAPP_URL}/api/integrations/zoomvideo/callback`);
const redirectUri = encodeURI(`${WEBAPP_URL_FOR_OAUTH}/api/integrations/zoomvideo/callback`);
const authHeader = `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString("base64")}`;
const result = await fetch(
`https://zoom.us/oauth/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirectUri}`,
@@ -8,6 +8,7 @@ import {
CREDENTIAL_SYNC_SECRET_HEADER_NAME,
} from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import { getPiiFreeCalendarEvent } from "@calcom/lib/piiFreeData";
import { safeStringify } from "@calcom/lib/safeStringify";
import prisma from "@calcom/prisma";
import { Frequency } from "@calcom/prisma/zod-utils";
@@ -65,12 +66,12 @@ export type ZoomUserSettings = z.infer<typeof zoomUserSettingsSchema>;
export const zoomUserSettingsSchema = z.object({
recording: z
.object({
auto_recording: z.string(),
auto_recording: z.string().nullish(),
})
.nullish(),
schedule_meeting: z
.object({
default_password_for_scheduled_meetings: z.string(),
default_password_for_scheduled_meetings: z.string().nullish(),
})
.nullish(),
});
@@ -185,6 +186,32 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
};
};
/**
* Zoom is known to return xml response in some cases.
* e.g. Wrong request or some special case of invalid token
*/
const handleZoomResponseJsonParseError = async ({
error,
clonedResponse,
}: {
error: unknown;
clonedResponse: Response;
}) => {
// In some cases, Zoom responds with xml response, so we log the response for debugging
// We need to see why that error occurs exactly and then later we decide if mark the access token and token object unusable or not
log.error(
"Error in JSON parsing Zoom API response",
safeStringify({
error: safeStringify(error),
// Log Raw response body here.
responseBody: await clonedResponse.text(),
status: clonedResponse.status,
})
);
return null;
};
const fetchZoomApi = async (endpoint: string, options?: RequestInit) => {
const auth = new OAuthManager({
credentialSyncVariables: {
@@ -220,9 +247,18 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
},
isTokenObjectUnusable: async function (response) {
const myLog = logger.getSubLogger({ prefix: ["zoomvideo:isTokenObjectUnusable"] });
myLog.debug(safeStringify({ status: response.status, ok: response.ok }));
if (!response.ok || (response.status < 200 && response.status >= 300)) {
const responseBody = await response.json();
myLog.info(safeStringify({ status: response.status, ok: response.ok }));
if (!response.ok) {
let responseBody;
const responseToUseInCaseOfError = response.clone();
try {
responseBody = await response.json();
} catch (e) {
return await handleZoomResponseJsonParseError({
error: e,
clonedResponse: responseToUseInCaseOfError,
});
}
myLog.debug(safeStringify({ responseBody }));
if (responseBody.error === "invalid_grant") {
@@ -233,11 +269,20 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
},
isAccessTokenUnusable: async function (response) {
const myLog = logger.getSubLogger({ prefix: ["zoomvideo:isAccessTokenUnusable"] });
myLog.debug(safeStringify({ status: response.status, ok: response.ok }));
if (!response.ok || (response.status < 200 && response.status >= 300)) {
const responseBody = await response.json();
myLog.info(safeStringify({ status: response.status, ok: response.ok }));
if (!response.ok) {
let responseBody;
const responseToUseInCaseOfError = response.clone();
try {
responseBody = await response.json();
} catch (e) {
return await handleZoomResponseJsonParseError({
error: e,
clonedResponse: responseToUseInCaseOfError,
});
}
myLog.debug(safeStringify({ responseBody }));
// 124 is the error code for invalid access token from Zoom API
if (responseBody.code === 124) {
return { reason: responseBody.message ?? "" };
}
@@ -284,7 +329,7 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
end: new Date(new Date(meeting.start_time).getTime() + meeting.duration * 60000).toISOString(),
}));
} catch (err) {
console.error(err);
log.error("Failed to get availability", safeStringify(err));
/* Prevents booking failure when Zoom Token is expired */
return [];
}
@@ -311,8 +356,10 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
}
throw new Error(`Failed to create meeting. Response is ${JSON.stringify(result)}`);
} catch (err) {
console.error(err);
log.error("Zoom meeting creation failed", safeStringify({ error: err, event }));
log.error(
"Zoom meeting creation failed",
safeStringify({ error: safeStringify(err), event: getPiiFreeCalendarEvent(event) })
);
/* Prevents meeting creation failure when Zoom Token is expired */
throw new Error("Unexpected error");
}
@@ -347,7 +394,10 @@ const ZoomVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
url: result.join_url,
};
} catch (err) {
log.error("Failed to update meeting", safeStringify(err));
log.error(
"Failed to update meeting",
safeStringify({ error: err, event: getPiiFreeCalendarEvent(event) })
);
return Promise.reject(new Error("Failed to update meeting"));
}
},
+1 -1
View File
@@ -244,7 +244,7 @@ export default function Booker( props : BookerProps ) {
namespace: string;
}) => {
return code`${getApiNameForVanillaJsSnippet({ namespace, mainApiName: "Cal" })}("inline", {
elementOrSelector:"#my-cal-inline",
elementOrSelector:"#my-cal-inline-${namespace}",
config: ${JSON.stringify(previewState.config)},
calLink: "${calLink}",
});
+1 -1
View File
@@ -59,7 +59,7 @@ export const tabs = [
embedType === "inline"
? `<div style="width:${getDimension(previewState.inline.width)};height:${getDimension(
previewState.inline.height
)};overflow:scroll" id="my-cal-inline"></div>\n`
)};overflow:scroll" id="my-cal-inline-${namespace}"></div>\n`
: ""
}<script type="text/javascript">
${embedSnippetString}
+14 -2
View File
@@ -1,7 +1,17 @@
## 1.0.63
## 1.0.108
## 1.0.112
### Patch Changes
- [#22134](https://github.com/calcom/cal.com/pull/22134) [`3437c43`](https://github.com/calcom/cal.com/commit/3437c4331da892bd77afaea84be2db196543a207) Thanks [@supalarry](https://github.com/supalarry)! - ignore this test log
## 1.0.110
### Patch Changes
- [#22107](https://github.com/calcom/cal.com/pull/22107) [`88a2bd3`](https://github.com/calcom/cal.com/commit/88a2bd3317a2cfaf6b367524e2736445544dbd63) Thanks [@supalarry](https://github.com/supalarry)! - test log can ignore
### Patch Changes
- [#22103](https://github.com/calcom/cal.com/pull/22103) [`61274bc`](https://github.com/calcom/cal.com/commit/61274bc7efc67b162d46b59cd75bd376ad515c51) Thanks [@supalarry](https://github.com/supalarry)! - testing changesets - can ignore this
@@ -14,6 +24,8 @@
- [#21864](https://github.com/calcom/cal.com/pull/21864) [`540bf86`](https://github.com/calcom/cal.com/commit/540bf868b5b60a98d9d3aeb565e2089f15c3dfd3) Thanks [@supalarry](https://github.com/supalarry)! - fix saving event type settings
## 1.0.63
1. 💥 BREAKING - `useGetBooking` hook has been renamed to `useBooking` hook and `useGetBookings` hook to `useBookings` and the data returned has different
structure. Here is example response from `useBooking`:
+1 -1
View File
@@ -13,6 +13,6 @@ Currently supports React 18, React 19, Next 14 and Next 15.
3. Some of the versions are suffixed e.g. `1.0.102-framer` and are intended for isolated use cases, so you most probably
want to use version without any suffix e.g. `1.0.103`.
### Documentation
### Documentation
Documentation on how to get started with platform solution is [here](https://cal.com/docs/platform/quickstart) and list of atoms can be viewed
[here](https://cal.com/docs/platform/atoms/cal-provider)
+3 -3
View File
@@ -4,11 +4,11 @@
"type": "module",
"description": "Customizable UI components to integrate scheduling into your product.",
"authors": "Cal.com, Inc.",
"version": "1.0.108",
"version": "1.0.112",
"scripts": {
"dev": "yarn vite build --watch & npx tailwindcss -i ./globals.css -o ./globals.min.css --postcss --minify --watch",
"build": "NODE_OPTIONS='--max_old_space_size=16384' rm -rf dist && yarn vite build && npx tailwindcss -i ./globals.css -o ./globals.min.css --postcss --minify && mkdir ./dist/packages/prisma-client && cp -rf ../../../node_modules/.prisma/client/*.d.ts ./dist/packages/prisma-client",
"publish": "yarn build && npm publish --access public",
"build": "NODE_OPTIONS='--max_old_space_size=12288' rm -rf dist && yarn vite build && npx tailwindcss -i ./globals.css -o ./globals.min.css --postcss --minify && mkdir ./dist/packages/prisma-client && cp -rf ../../../node_modules/.prisma/client/*.d.ts ./dist/packages/prisma-client",
"publish-npm": "yarn build && npm publish --access public",
"test": "jest"
},
"devDependencies": {
+3
View File
@@ -68,6 +68,9 @@
"cache": false,
"dependsOn": []
},
"@calcom/atoms#build": {
"outputs": ["dist/**", "globals.min.css"]
},
"@calcom/api-v2#build": {
"dependsOn": ["^build"],
"env": [