Skip to content
Merged
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 @@ -13,6 +13,7 @@ import { appKeysSchema as googlecalendar_zod_ts } from "./googlecalendar/zod";
import { appKeysSchema as gtm_zod_ts } from "./gtm/zod";
import { appKeysSchema as hubspot_zod_ts } from "./hubspot/zod";
import { appKeysSchema as intercom_zod_ts } from "./intercom/zod";
import { appKeysSchema as jelly_zod_ts } from "./jelly/zod";
import { appKeysSchema as jitsivideo_zod_ts } from "./jitsivideo/zod";
import { appKeysSchema as larkcalendar_zod_ts } from "./larkcalendar/zod";
import { appKeysSchema as make_zod_ts } from "./make/zod";
Expand Down Expand Up @@ -53,6 +54,7 @@ export const appKeysSchemas = {
gtm: gtm_zod_ts,
hubspot: hubspot_zod_ts,
intercom: intercom_zod_ts,
jelly: jelly_zod_ts,
jitsivideo: jitsivideo_zod_ts,
larkcalendar: larkcalendar_zod_ts,
make: make_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 @@ -33,6 +33,7 @@ import { metadata as hubspot__metadata_ts } from "./hubspot/_metadata";
import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata";
import ics_feedcalendar_config_json from "./ics-feedcalendar/config.json";
import intercom_config_json from "./intercom/config.json";
import jelly_config_json from "./jelly/config.json";
import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata";
import { metadata as larkcalendar__metadata_ts } from "./larkcalendar/_metadata";
import linear_config_json from "./linear/config.json";
Expand Down Expand Up @@ -117,6 +118,7 @@ export const appStoreMetadata = {
huddle01video: huddle01video__metadata_ts,
"ics-feedcalendar": ics_feedcalendar_config_json,
intercom: intercom_config_json,
jelly: jelly_config_json,
jitsivideo: jitsivideo__metadata_ts,
larkcalendar: larkcalendar__metadata_ts,
linear: linear_config_json,
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 @@ -13,6 +13,7 @@ import { appDataSchema as googlecalendar_zod_ts } from "./googlecalendar/zod";
import { appDataSchema as gtm_zod_ts } from "./gtm/zod";
import { appDataSchema as hubspot_zod_ts } from "./hubspot/zod";
import { appDataSchema as intercom_zod_ts } from "./intercom/zod";
import { appDataSchema as jelly_zod_ts } from "./jelly/zod";
import { appDataSchema as jitsivideo_zod_ts } from "./jitsivideo/zod";
import { appDataSchema as larkcalendar_zod_ts } from "./larkcalendar/zod";
import { appDataSchema as make_zod_ts } from "./make/zod";
Expand Down Expand Up @@ -53,6 +54,7 @@ export const appDataSchemas = {
gtm: gtm_zod_ts,
hubspot: hubspot_zod_ts,
intercom: intercom_zod_ts,
jelly: jelly_zod_ts,
jitsivideo: jitsivideo_zod_ts,
larkcalendar: larkcalendar_zod_ts,
make: make_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 @@ -33,6 +33,7 @@ export const apiHandlers = {
huddle01video: import("./huddle01video/api"),
"ics-feedcalendar": import("./ics-feedcalendar/api"),
intercom: import("./intercom/api"),
jelly: import("./jelly/api"),
jitsivideo: import("./jitsivideo/api"),
larkcalendar: import("./larkcalendar/api"),
linear: import("./linear/api"),
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/bookerApps.metadata.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ga4_config_json from "./ga4/config.json";
import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata";
import gtm_config_json from "./gtm/config.json";
import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata";
import jelly_config_json from "./jelly/config.json";
import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata";
import matomo_config_json from "./matomo/config.json";
import metapixel_config_json from "./metapixel/config.json";
Expand Down Expand Up @@ -52,6 +53,7 @@ export const appStoreMetadata = {
googlevideo: googlevideo__metadata_ts,
gtm: gtm_config_json,
huddle01video: huddle01video__metadata_ts,
jelly: jelly_config_json,
jitsivideo: jitsivideo__metadata_ts,
matomo: matomo_config_json,
metapixel: metapixel_config_json,
Expand Down
1 change: 1 addition & 0 deletions packages/app-store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const appStore = {
hubspot: () => import("./hubspot"),
huddle01video: () => import("./huddle01video"),
"ics-feedcalendar": () => import("./ics-feedcalendar"),
jellyconferencing: () => import("./jelly"),
jitsivideo: () => import("./jitsivideo"),
larkcalendar: () => import("./larkcalendar"),
office365calendar: () => import("./office365calendar"),
Expand Down
8 changes: 8 additions & 0 deletions packages/app-store/jelly/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
items:
- 1.jpeg
- 2.jpeg
- 3.jpeg
---

{DESCRIPTION}
45 changes: 45 additions & 0 deletions packages/app-store/jelly/api/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { NextApiRequest } from "next";
import { stringify } from "querystring";
import { z } from "zod";

import { WEBAPP_URL } from "@calcom/lib/constants";
import { defaultHandler, defaultResponder } from "@calcom/lib/server";

import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
import { encodeOAuthState } from "../../_utils/oauth/encodeOAuthState";

const jellyAppKeysSchema = z.object({
client_id: z.string(),
client_secret: z.string(),
});

export const getJellyAppKeys = async () => {
const appKeys = await getAppKeysFromSlug("jelly");
return jellyAppKeysSchema.parse(appKeys);
};

async function handler(req: NextApiRequest) {
// Get user
const user = req?.session?.user;
if (!user) {
return { status: 401, body: { error: "Unauthorized" } };
}

const { client_id } = await getJellyAppKeys();
const state = encodeOAuthState(req);

const params = {
response_type: "code",
app_id: client_id,
redirect_uri: `${WEBAPP_URL}/api/integrations/jelly/callback`,
state,
scope: "write:jellies,read:user_email_phone",
};
const query = stringify(params);
const url = `https://jellyjelly.com/login/oauth?${query}`;
return { url };
}

export default defaultHandler({
GET: Promise.resolve({ default: defaultResponder(handler) }),
});
58 changes: 58 additions & 0 deletions packages/app-store/jelly/api/callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { NextApiRequest, NextApiResponse } from "next";

import prisma from "@calcom/prisma";

import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
import createOAuthAppCredential from "../../_utils/oauth/createOAuthAppCredential";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const userId = req.session?.user.id;
if (!userId) {
return res.status(404).json({ message: "No user found" });
}
const { code } = req.query;
const { client_id, client_secret } = await getAppKeysFromSlug("jelly");

const result = await fetch(`https://www.jellyjelly.com/login/oauth/access_token`, {
method: "POST",
body: JSON.stringify({ code, client_id, client_secret }),
});

if (result.status !== 200) {
let errorMessage = "Something is wrong with the Jelly API";
try {
const responseBody = await result.json();
errorMessage = responseBody.error;
} catch (e) {}

res.status(400).json({ message: errorMessage });
return;
}

const responseBody = await result.json();
if (responseBody.error) {
res.status(400).json({ message: responseBody.error });
return;
}

/**
* With this we take care of no duplicate jelly key for a single user
* when creating a room using deleteMany if there is already a jelly key
* */
await prisma.credential.deleteMany({
where: {
type: "jelly_conferencing",
userId,
appId: "jelly",
},
});

await createOAuthAppCredential(
{ appId: "jelly", type: "jelly_conferencing" },
{ access_token: responseBody.access_token },
req
);

res.redirect(getInstalledAppPath({ variant: "conferencing", slug: "jelly" }));
}
2 changes: 2 additions & 0 deletions packages/app-store/jelly/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as add } from "./add";
export { default as callback } from "./callback";
23 changes: 23 additions & 0 deletions packages/app-store/jelly/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"/*": "Don't modify slug - If required, do it using cli edit command",
"name": "Jelly",
"slug": "jelly",
"type": "jelly_conferencing",
"logo": "icon.svg",
"url": "https://jellyjelly.com",
"variant": "conferencing",
"categories": ["conferencing"],
"publisher": "Jelly",
"email": "support@jellyjelly.com",
"appData": {
"location": {
"type": "integrations:{SLUG}_video",
"label": "{TITLE}",
"linkType": "dynamic"
}
},
"description": "Jelly is a camera-free voice chat platform. No frills, no makeup needed, just good talking. Our AI magic handles the rest by highlighting and publishing key moments with rich visuals.",
"isTemplate": false,
"__createdUsingCli": true,
"__template": "event-type-location-video-static"
}
2 changes: 2 additions & 0 deletions packages/app-store/jelly/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as api from "./api";
export * as lib from "./lib";
52 changes: 52 additions & 0 deletions packages/app-store/jelly/lib/VideoApiAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { CalendarEvent, EventBusyDate } from "@calcom/types/Calendar";
import type { CredentialPayload } from "@calcom/types/Credential";
import type { PartialReference } from "@calcom/types/EventManager";
import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";

type JellyToken = {
access_token: string;
};
const JellyVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter => {
return {
createMeeting: async (event: CalendarEvent): Promise<VideoCallData> => {
// get keys from slug
const keys = credential.key as JellyToken;
const { access_token } = keys;
// create jelly link
const jellyLink = await fetch("https://www.jellyjelly.com/api/ti/start_jelly", {
method: "POST",
headers: {
Authorization: `Bearer ${access_token}`,
"Content-Type": "application/json",
},
});
const jellyLinkData = await jellyLink.json();

return {
type: "jelly_conferencing",
id: jellyLinkData.talkId,
password: "",
url: jellyLinkData.url,
};
},
updateMeeting: async (bookingRef: PartialReference, event: CalendarEvent): Promise<VideoCallData> => {
// don't update jelly link
return {
type: "jelly_conferencing",
id: bookingRef.externalCalendarId ? bookingRef.externalCalendarId : "",
password: "",
url: bookingRef.meetingUrl ? bookingRef.meetingUrl : "",
};
},
deleteMeeting: async (uid: string): Promise<unknown> => {
// delete jelly link
return {};
},
getAvailability: async (dateFrom?: string, dateTo?: string): Promise<EventBusyDate[]> => {
// get jelly availability
return [];
},
};
};

export default JellyVideoApiAdapter;
1 change: 1 addition & 0 deletions packages/app-store/jelly/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as VideoApiAdapter } from "./VideoApiAdapter";
14 changes: 14 additions & 0 deletions packages/app-store/jelly/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"name": "@calcom/jelly",
"version": "0.0.0",
"main": "./index.ts",
"dependencies": {
"@calcom/lib": "*"
},
"devDependencies": {
"@calcom/types": "*"
},
"description": "Jelly is a camera-free voice chat platform. No frills, no makeup needed, just good talking. Our AI magic handles the rest by highlighting and publishing key moments with rich visuals."
}
Binary file added packages/app-store/jelly/static/1.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/app-store/jelly/static/2.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/app-store/jelly/static/3.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions packages/app-store/jelly/static/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions packages/app-store/jelly/zod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from "zod";

export const appDataSchema = z.object({});

export const appKeysSchema = z.object({
client_id: z.string().min(1),
client_secret: z.string().min(1),
});