Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/app-store/apps.keys-schemas.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
**/
import { appKeysSchema as alby_zod_ts } from "./alby/zod";
import { appKeysSchema as basecamp3_zod_ts } from "./basecamp3/zod";
import { appKeysSchema as bigbluebutton_zod_ts } from "./bigbluebutton/zod";
import { appKeysSchema as btcpayserver_zod_ts } from "./btcpayserver/zod";
import { appKeysSchema as closecom_zod_ts } from "./closecom/zod";
import { appKeysSchema as dailyvideo_zod_ts } from "./dailyvideo/zod";
Expand Down Expand Up @@ -55,6 +56,7 @@ import { appKeysSchema as zoomvideo_zod_ts } from "./zoomvideo/zod";
export const appKeysSchemas = {
alby: alby_zod_ts,
basecamp3: basecamp3_zod_ts,
bigbluebutton: bigbluebutton_zod_ts,
btcpayserver: btcpayserver_zod_ts,
closecom: closecom_zod_ts,
dailyvideo: dailyvideo_zod_ts,
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/apps.metadata.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import attio_config_json from "./attio/config.json";
import autocheckin_config_json from "./autocheckin/config.json";
import baa_for_hipaa_config_json from "./baa-for-hipaa/config.json";
import basecamp3_config_json from "./basecamp3/config.json";
import { metadata as bigbluebutton__metadata_ts } from "./bigbluebutton/_metadata";
import bolna_config_json from "./bolna/config.json";
import btcpayserver_config_json from "./btcpayserver/config.json";
import { metadata as caldavcalendar__metadata_ts } from "./caldavcalendar/_metadata";
Expand Down Expand Up @@ -121,6 +122,7 @@ export const appStoreMetadata = {
autocheckin: autocheckin_config_json,
"baa-for-hipaa": baa_for_hipaa_config_json,
basecamp3: basecamp3_config_json,
bigbluebutton: bigbluebutton__metadata_ts,
bolna: bolna_config_json,
btcpayserver: btcpayserver_config_json,
caldavcalendar: caldavcalendar__metadata_ts,
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/apps.schemas.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
**/
import { appDataSchema as alby_zod_ts } from "./alby/zod";
import { appDataSchema as basecamp3_zod_ts } from "./basecamp3/zod";
import { appDataSchema as bigbluebutton_zod_ts } from "./bigbluebutton/zod";
import { appDataSchema as btcpayserver_zod_ts } from "./btcpayserver/zod";
import { appDataSchema as closecom_zod_ts } from "./closecom/zod";
import { appDataSchema as dailyvideo_zod_ts } from "./dailyvideo/zod";
Expand Down Expand Up @@ -55,6 +56,7 @@ import { appDataSchema as zoomvideo_zod_ts } from "./zoomvideo/zod";
export const appDataSchemas = {
alby: alby_zod_ts,
basecamp3: basecamp3_zod_ts,
bigbluebutton: bigbluebutton_zod_ts,
btcpayserver: btcpayserver_zod_ts,
closecom: closecom_zod_ts,
dailyvideo: dailyvideo_zod_ts,
Expand Down
1 change: 1 addition & 0 deletions packages/app-store/apps.server.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const apiHandlers = {
applecalendar: import("./applecalendar/api"),
attio: import("./attio/api"),
basecamp3: import("./basecamp3/api"),
bigbluebutton: import("./bigbluebutton/api"),
btcpayserver: import("./btcpayserver/api"),
caldavcalendar: import("./caldavcalendar/api"),
campfire: import("./campfire/api"),
Expand Down
3 changes: 3 additions & 0 deletions packages/app-store/bigbluebutton/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BigBlueButton is an open-source web conferencing system built for online learning and webinars.

This integration lets Cal.com create a BigBlueButton meeting URL for bookings.
29 changes: 29 additions & 0 deletions packages/app-store/bigbluebutton/_metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { AppMeta } from "@calcom/types/App";

export const metadata = {
name: "BigBlueButton",
description: "Open-source video conferencing for online learning and webinars.",
installed: true,
type: "bigbluebutton_video",
variant: "conferencing",
categories: ["conferencing"],
logo: "icon.svg",
publisher: "Cal.diy",
url: "https://bigbluebutton.org/",
slug: "bigbluebutton",
title: "BigBlueButton",
isGlobal: false,
email: "help@cal.com",
appData: {
location: {
linkType: "dynamic",
type: "integrations:bigbluebutton",
label: "BigBlueButton",
},
Comment on lines +17 to +22
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the location label.

appData.location.label is a user-facing string, so hardcoding "BigBlueButton" here skips the app’s i18n flow. Please add it to packages/i18n/locales/en/common.json and reference the translated value instead.

As per coding guidelines, "**/*.{ts,tsx,jsx}: Add translations to packages/i18n/locales/en/common.json for all UI strings".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app-store/bigbluebutton/_metadata.ts` around lines 17 - 22, Update
the user-facing label in packages/app-store/bigbluebutton/_metadata.ts to use a
translation key instead of the hardcoded string: replace appData.location.label
= "BigBlueButton" with a lookup of the i18n key (e.g.,
t('apps.bigbluebutton.label')) and add that key/value to
packages/i18n/locales/en/common.json (for example "apps": { "bigbluebutton": {
"label": "BigBlueButton" }}) so the string flows through the app i18n system.

},
dirName: "bigbluebutton",
concurrentMeetings: true,
isOAuth: false,
} as AppMeta;

export default metadata;
113 changes: 113 additions & 0 deletions packages/app-store/bigbluebutton/api/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { ErrorWithCode } from "@calcom/lib/errors";
import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown";
import prisma from "@calcom/prisma";
import type { NextApiRequest, NextApiResponse } from "next";
import getInstalledAppPath from "../../_utils/getInstalledAppPath";

const getSingleQueryValue = (value: string | string[] | undefined) =>
Array.isArray(value) ? value[0] : value;

const getTeamId = (value: string | string[] | undefined) => {
const rawTeamId = getSingleQueryValue(value);
if (!rawTeamId) {
return null;
}

if (!/^\d+$/.test(rawTeamId)) {
throw new ErrorWithCode(ErrorCode.BadRequest, "Invalid teamId");
}

return Number(rawTeamId);
};

const getSafeReturnTo = (value: string | string[] | undefined) => {
const returnTo = getSingleQueryValue(value);
if (
!returnTo ||
!returnTo.startsWith("/") ||
returnTo.startsWith("//") ||
/^[a-z][a-z\d+.-]*:/i.test(returnTo)
) {
return getInstalledAppPath({
variant: "conferencing",
slug: "bigbluebutton",
});
}

return returnTo;
};

export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
return res.status(405).json({ message: "Method Not Allowed" });
}

if (!req.session?.user?.id) {
return res
.status(401)
.json({ message: "You must be logged in to do this" });
}

const { teamId, returnTo } = req.query;

try {
const teamIdNumber = getTeamId(teamId);

await throwIfNotHaveAdminAccessToTeam({
teamId: teamIdNumber,
userId: req.session.user.id,
});

const installForObject = teamIdNumber
? { teamId: teamIdNumber }
: { userId: req.session.user.id };
const appType = "bigbluebutton_video";

const alreadyInstalled = await prisma.credential.findFirst({
where: {
type: appType,
...installForObject,
},
select: {
id: true,
},
});

if (alreadyInstalled) {
throw new ErrorWithCode(ErrorCode.BookingConflict, "Already installed");
}

const installation = await prisma.credential.create({
data: {
type: appType,
key: {},
...installForObject,
appId: "bigbluebutton",
},
select: {
id: true,
},
});

if (!installation) {
throw new ErrorWithCode(
ErrorCode.InternalServerError,
"Unable to create user credential for bigbluebutton",
);
}
} catch (error: unknown) {
const httpError = getServerErrorFromUnknown(error);
return res
.status(httpError.statusCode)
.json({ message: httpError.message });
}

return res.status(200).json({
url: getSafeReturnTo(returnTo),
});
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
1 change: 1 addition & 0 deletions packages/app-store/bigbluebutton/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as add } from "./add";
3 changes: 3 additions & 0 deletions packages/app-store/bigbluebutton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { metadata } from "./_metadata";
export * as api from "./api";
export * as lib from "./lib";
Loading
Loading