2023-02-27 18:47:21 +00:00
|
|
|
import type { DestinationCalendar, Prisma } from "@prisma/client";
|
|
|
|
import { AppCategories, BookingStatus, IdentityProvider } from "@prisma/client";
|
2023-03-02 20:42:41 +00:00
|
|
|
import { cityMapping } from "city-timezones";
|
2021-11-15 12:25:49 +00:00
|
|
|
import _ from "lodash";
|
2022-08-31 20:57:53 +00:00
|
|
|
import { authenticator } from "otplib";
|
2022-10-06 19:23:22 +00:00
|
|
|
import z from "zod";
|
2021-09-27 14:47:55 +00:00
|
|
|
|
2022-09-05 21:10:58 +00:00
|
|
|
import ethRouter from "@calcom/app-store/rainbow/trpc/router";
|
2023-03-09 22:58:02 +00:00
|
|
|
import app_RoutingForms from "@calcom/app-store/routing-forms/trpc-router";
|
2022-08-31 20:57:53 +00:00
|
|
|
import { deleteStripeCustomer } from "@calcom/app-store/stripepayment/lib/customer";
|
2023-02-08 20:36:22 +00:00
|
|
|
import stripe from "@calcom/app-store/stripepayment/lib/server";
|
2022-12-07 19:55:47 +00:00
|
|
|
import { getPremiumPlanProductId } from "@calcom/app-store/stripepayment/lib/utils";
|
2022-11-24 11:53:29 +00:00
|
|
|
import getApps, { getLocationGroupedOptions } from "@calcom/app-store/utils";
|
2022-08-15 20:18:41 +00:00
|
|
|
import { cancelScheduledJobs } from "@calcom/app-store/zapier/lib/nodeScheduler";
|
2022-03-23 22:00:30 +00:00
|
|
|
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
|
2022-08-26 00:48:50 +00:00
|
|
|
import { DailyLocationType } from "@calcom/core/location";
|
2023-03-05 12:59:07 +00:00
|
|
|
import {
|
|
|
|
getDownloadLinkOfCalVideoByRecordingId,
|
2023-03-08 22:04:33 +00:00
|
|
|
getRecordingsOfCalVideoByRoomName,
|
2023-03-05 12:59:07 +00:00
|
|
|
} from "@calcom/core/videoClient";
|
2022-06-28 20:40:58 +00:00
|
|
|
import dayjs from "@calcom/dayjs";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { sendCancelledEmails, sendFeedbackEmail } from "@calcom/emails";
|
2023-03-10 23:45:24 +00:00
|
|
|
import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
|
|
|
|
import { verifyPassword } from "@calcom/features/auth/lib/verifyPassword";
|
2022-10-18 20:34:32 +00:00
|
|
|
import { samlTenantProduct } from "@calcom/features/ee/sso/lib/saml";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
2022-12-07 21:47:02 +00:00
|
|
|
import getEnabledApps from "@calcom/lib/apps/getEnabledApps";
|
2023-03-10 14:18:03 +00:00
|
|
|
import { IS_SELF_HOSTED, WEBAPP_URL } from "@calcom/lib/constants";
|
2022-08-31 20:57:53 +00:00
|
|
|
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
2023-02-08 20:36:22 +00:00
|
|
|
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
2022-07-22 17:27:06 +00:00
|
|
|
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
2023-02-08 20:36:22 +00:00
|
|
|
import { deletePayment } from "@calcom/lib/payment/deletePayment";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { checkUsername } from "@calcom/lib/server/checkUsername";
|
|
|
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
2022-11-18 23:36:32 +00:00
|
|
|
import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image";
|
2022-07-22 17:27:06 +00:00
|
|
|
import slugify from "@calcom/lib/slugify";
|
2022-08-26 21:10:12 +00:00
|
|
|
import {
|
|
|
|
deleteWebUser as syncServicesDeleteWebUser,
|
2022-08-31 20:57:53 +00:00
|
|
|
updateWebUser as syncServicesUpdateWebUser,
|
2022-08-26 21:10:12 +00:00
|
|
|
} from "@calcom/lib/sync/SyncServiceManager";
|
2022-11-18 23:36:32 +00:00
|
|
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
2022-10-17 18:18:38 +00:00
|
|
|
import { EventTypeMetaDataSchema, userMetadata } from "@calcom/prisma/zod-utils";
|
2021-09-27 14:47:55 +00:00
|
|
|
|
2021-10-14 19:22:01 +00:00
|
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
|
2022-11-18 23:36:32 +00:00
|
|
|
import { authedProcedure, mergeRouters, publicProcedure, router } from "../trpc";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { apiKeysRouter } from "./viewer/apiKeys";
|
2022-12-07 21:47:02 +00:00
|
|
|
import { appsRouter } from "./viewer/apps";
|
2022-08-30 19:46:52 +00:00
|
|
|
import { authRouter } from "./viewer/auth";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { availabilityRouter } from "./viewer/availability";
|
|
|
|
import { bookingsRouter } from "./viewer/bookings";
|
Admin Wizard Choose License (#6574)
* Implementation
* i18n
* More i18n
* extracted i18n, needs api to get most recent price, added hint: update later
* Fixing i18n var
* Fix booking filters not working for admin (#6576)
* fix: react-select overflow issue in some modals. (#6587)
* feat: add a disable overflow prop
* feat: use the disable overflow prop
* Tailwind Merge (#6596)
* Tailwind Merge
* Fix merge classNames
* [CAL-808] /availability/single - UI issue on buttons beside time inputs (#6561)
* [CAL-808] /availability/single - UI issue on buttons beside time inputs
* Update apps/web/public/static/locales/en/common.json
* Update packages/features/schedules/components/Schedule.tsx
* create new translation for tooltip
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Bye bye submodules (#6585)
* WIP
* Uses ssh instead
* Update .gitignore
* Update .gitignore
* Update Makefile
* Update git-setup.sh
* Update git-setup.sh
* Replaced Makefile with bash script
* Update package.json
* fix: show button on empty string (#6601)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: add delete in dropdown (#6599)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update README.md
* Update README.md
* Changed a neutral- classes to gray (#6603)
* Changed a neutral- classes to gray
* Changed all border-1 to border
* Update package.json
* Test fixes
* Yarn lock fixes
* Fix string equality check in git-setup.sh
* [CAL-811] Avatar icon not redirecting user back to the main page (#6586)
* Remove cursor-pointer, remove old Avatar* files
* Fixed styling for checkedSelect + some cleanup
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Harsh/add member invite (#6598)
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
* Regenerated lockfile without upgrade (#6610)
* fix: remove annoying outline when <Button /> clicked (#6537)
* fix: remove annoying outline when <Button /> clicked
* Delete yarn.lock
* remove 1 on 1 icon (#6609)
* removed 1-on-1 badge
* changed user to users for group events
* fix: case-sensitivity in apps path (#6552)
* fix: lowercase slug
* fix: make fallback blocking
* Fix FAB (#6611)
* feat: add LocationSelect component (#6571)
* feat: add LocationSelect component
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* chore: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update booking filters design (#6543)
* Update booking filters
* Add filter on YOUR bookings
* Fix pending members showing up in list
* Reduce the avatar size to 'sm' for now
* Bugfix/dropdown menu trigger as child remove class names (#6614)
* Fix UsernameTextfield to take right height
* Remove className side-effect
* Incorrect resolution version fixed
* Converted mobile DropdownMenuTrigger styles into Button
* v2.5.3
* fix: use items-center (#6618)
* fix tooltip and modal stacking issues (#6491)
* fix tooltip and modal stacking issues
* use z-index in larger screens and less
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Temporary fix (#6626)
* Fix Ga4 tracking (#6630)
* generic <UpgradeScreen> component (#6594)
* first attempt of <UpgradeScreen>
* changes to icons
* reverted changes back to initial state, needs fix: teams not showing
* WIP
* Fix weird reactnode error
* Fix loading text
* added upgradeTip to routing forms
* icon colors
* create and use hook to check if user has team plan
* use useTeamPlan for upgradeTeamsBadge
* replace huge svg with compressed jpeg
* responsive fixes
* Update packages/ui/components/badge/UpgradeTeamsBadge.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Give team plan features to E2E tests
* Allow option to make a user part of team int ests
* Remove flash of paywall for team user
* Add team user for typeform tests as well
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* Removing env var to rely on db
* Restoring i18n keys, set loading moved
* Fixing tailwind-preset glob
* Wizard width fix for md+ screens
* Converting licenses options to radix radio
* Applying feedback + other tweaks
* Reverting this, not this PR related
* Unneeded code removal
* Reverting unneeded style change
* Applying feedback
* Removing licenseType
* Upgrades typescript
* Update yarn lock
* Typings
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712)
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712) (#6713)
* Adds deployment settings to DB (#6706)
* WIP
* Adds DeploymentTheme
* Add missing migrations
* Adds client extensions for deployment
* Cleanup
* Delete migration.sql
* Relying on both, env var and new model
* Restoring env example doc for backward compat
* Maximum call stack size exceeded fix?
* Revert upgrade
* Update index.ts
* Delete index.ts
* Not exposing license key, fixed radio behavior
* Covering undefined env var
* Self contained checkLicense
* Feedback
* Moar feedback
* Feedback
* Feedback
* Feedback
* Cleanup
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Harsh Singh <51085015+harshsinghatz@users.noreply.github.com>
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
Co-authored-by: Luis Cadillo <luiscaf3r@gmail.com>
Co-authored-by: Mohammed Cherfaoui <hi@cherfaoui.dev>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-02-08 00:23:42 +00:00
|
|
|
import { deploymentSetupRouter } from "./viewer/deploymentSetup";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { eventTypesRouter } from "./viewer/eventTypes";
|
|
|
|
import { slotsRouter } from "./viewer/slots";
|
2023-01-24 20:02:43 +00:00
|
|
|
import { ssoRouter } from "./viewer/sso";
|
2021-12-09 23:51:30 +00:00
|
|
|
import { viewerTeamsRouter } from "./viewer/teams";
|
2021-10-25 16:15:52 +00:00
|
|
|
import { webhookRouter } from "./viewer/webhook";
|
2022-07-22 17:27:06 +00:00
|
|
|
import { workflowsRouter } from "./viewer/workflows";
|
2021-09-28 08:57:30 +00:00
|
|
|
|
2021-10-14 10:57:49 +00:00
|
|
|
// things that unauthenticated users can query about themselves
|
2022-11-10 23:40:01 +00:00
|
|
|
const publicViewerRouter = router({
|
|
|
|
session: publicProcedure.query(({ ctx }) => {
|
|
|
|
return ctx.session;
|
|
|
|
}),
|
|
|
|
i18n: publicProcedure.query(async ({ ctx }) => {
|
|
|
|
const { locale, i18n } = ctx;
|
|
|
|
return {
|
|
|
|
i18n,
|
|
|
|
locale,
|
|
|
|
};
|
|
|
|
}),
|
2023-03-08 12:33:42 +00:00
|
|
|
countryCode: publicProcedure.query(({ ctx }) => {
|
|
|
|
const { req } = ctx;
|
|
|
|
|
|
|
|
const countryCode: string | string[] = req?.headers?.["x-vercel-ip-country"] ?? "";
|
|
|
|
return { countryCode: Array.isArray(countryCode) ? countryCode[0] : countryCode };
|
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
samlTenantProduct: publicProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
email: z.string().email(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2022-01-13 20:05:23 +00:00
|
|
|
const { prisma } = ctx;
|
|
|
|
const { email } = input;
|
|
|
|
|
|
|
|
return await samlTenantProduct(prisma, email);
|
2022-09-08 00:38:37 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
stripeCheckoutSession: publicProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
stripeCustomerId: z.string().optional(),
|
|
|
|
checkoutSessionId: z.string().optional(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.query(async ({ input }) => {
|
2022-09-08 00:38:37 +00:00
|
|
|
const { checkoutSessionId, stripeCustomerId } = input;
|
|
|
|
|
|
|
|
// TODO: Move the following data checks to superRefine
|
|
|
|
if (!checkoutSessionId && !stripeCustomerId) {
|
|
|
|
throw new Error("Missing checkoutSessionId or stripeCustomerId");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (checkoutSessionId && stripeCustomerId) {
|
|
|
|
throw new Error("Both checkoutSessionId and stripeCustomerId provided");
|
|
|
|
}
|
|
|
|
let customerId: string;
|
|
|
|
let isPremiumUsername = false;
|
|
|
|
let hasPaymentFailed = false;
|
|
|
|
if (checkoutSessionId) {
|
|
|
|
try {
|
|
|
|
const session = await stripe.checkout.sessions.retrieve(checkoutSessionId);
|
|
|
|
if (typeof session.customer !== "string") {
|
|
|
|
return {
|
|
|
|
valid: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
customerId = session.customer;
|
|
|
|
isPremiumUsername = true;
|
|
|
|
hasPaymentFailed = session.payment_status !== "paid";
|
|
|
|
} catch (e) {
|
|
|
|
return {
|
|
|
|
valid: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
|
|
customerId = stripeCustomerId!;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const customer = await stripe.customers.retrieve(customerId);
|
|
|
|
if (customer.deleted) {
|
|
|
|
return {
|
|
|
|
valid: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
valid: true,
|
|
|
|
hasPaymentFailed,
|
|
|
|
isPremiumUsername,
|
|
|
|
customer: {
|
|
|
|
username: customer.metadata.username,
|
|
|
|
email: customer.metadata.email,
|
|
|
|
stripeCustomerId: customerId,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
return {
|
|
|
|
valid: false,
|
|
|
|
};
|
|
|
|
}
|
2022-11-10 23:40:01 +00:00
|
|
|
}),
|
|
|
|
// REVIEW: This router is part of both the public and private viewer router?
|
|
|
|
slots: slotsRouter,
|
2023-03-02 20:42:41 +00:00
|
|
|
cityTimezones: publicProcedure.query(() => cityMapping),
|
2022-11-10 23:40:01 +00:00
|
|
|
});
|
2021-10-14 10:57:49 +00:00
|
|
|
|
2021-09-28 08:57:30 +00:00
|
|
|
// routes only available to authenticated users
|
2022-11-10 23:40:01 +00:00
|
|
|
const loggedInViewerRouter = router({
|
|
|
|
me: authedProcedure.query(async ({ ctx }) => {
|
|
|
|
const { user } = ctx;
|
|
|
|
// Destructuring here only makes it more illegible
|
|
|
|
// pick only the part we want to expose in the API
|
|
|
|
return {
|
|
|
|
id: user.id,
|
|
|
|
name: user.name,
|
|
|
|
username: user.username,
|
|
|
|
email: user.email,
|
|
|
|
startTime: user.startTime,
|
|
|
|
endTime: user.endTime,
|
|
|
|
bufferTime: user.bufferTime,
|
|
|
|
locale: user.locale,
|
|
|
|
timeFormat: user.timeFormat,
|
|
|
|
timeZone: user.timeZone,
|
2022-12-08 09:50:03 +00:00
|
|
|
avatar: user.avatar,
|
2022-11-10 23:40:01 +00:00
|
|
|
createdDate: user.createdDate,
|
|
|
|
trialEndsAt: user.trialEndsAt,
|
2023-02-06 14:01:54 +00:00
|
|
|
defaultScheduleId: user.defaultScheduleId,
|
2022-11-10 23:40:01 +00:00
|
|
|
completedOnboarding: user.completedOnboarding,
|
|
|
|
twoFactorEnabled: user.twoFactorEnabled,
|
|
|
|
disableImpersonation: user.disableImpersonation,
|
|
|
|
identityProvider: user.identityProvider,
|
|
|
|
brandColor: user.brandColor,
|
|
|
|
darkBrandColor: user.darkBrandColor,
|
|
|
|
away: user.away,
|
|
|
|
bio: user.bio,
|
|
|
|
weekStart: user.weekStart,
|
|
|
|
theme: user.theme,
|
|
|
|
hideBranding: user.hideBranding,
|
|
|
|
metadata: user.metadata,
|
|
|
|
};
|
|
|
|
}),
|
2022-12-08 09:50:03 +00:00
|
|
|
avatar: authedProcedure.query(({ ctx }) => ({
|
|
|
|
avatar: ctx.user.rawAvatar,
|
|
|
|
})),
|
2022-11-10 23:40:01 +00:00
|
|
|
deleteMe: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
password: z.string(),
|
|
|
|
totpCode: z.string().optional(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2022-08-31 20:57:53 +00:00
|
|
|
// Check if input.password is correct
|
|
|
|
const user = await prisma.user.findUnique({
|
2022-01-14 13:49:15 +00:00
|
|
|
where: {
|
2022-08-31 20:57:53 +00:00
|
|
|
email: ctx.user.email.toLowerCase(),
|
2022-01-14 13:49:15 +00:00
|
|
|
},
|
|
|
|
});
|
2022-08-31 20:57:53 +00:00
|
|
|
if (!user) {
|
|
|
|
throw new Error(ErrorCode.UserNotFound);
|
|
|
|
}
|
2022-08-26 21:10:12 +00:00
|
|
|
|
2022-08-31 20:57:53 +00:00
|
|
|
if (user.identityProvider !== IdentityProvider.CAL) {
|
|
|
|
throw new Error(ErrorCode.ThirdPartyIdentityProviderEnabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!user.password) {
|
|
|
|
throw new Error(ErrorCode.UserMissingPassword);
|
|
|
|
}
|
|
|
|
|
|
|
|
const isCorrectPassword = await verifyPassword(input.password, user.password);
|
|
|
|
if (!isCorrectPassword) {
|
|
|
|
throw new Error(ErrorCode.IncorrectPassword);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user.twoFactorEnabled) {
|
|
|
|
if (!input.totpCode) {
|
|
|
|
throw new Error(ErrorCode.SecondFactorRequired);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!user.twoFactorSecret) {
|
|
|
|
console.error(`Two factor is enabled for user ${user.id} but they have no secret`);
|
|
|
|
throw new Error(ErrorCode.InternalServerError);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!process.env.CALENDSO_ENCRYPTION_KEY) {
|
|
|
|
console.error(`"Missing encryption key; cannot proceed with two factor login."`);
|
|
|
|
throw new Error(ErrorCode.InternalServerError);
|
|
|
|
}
|
|
|
|
|
|
|
|
const secret = symmetricDecrypt(user.twoFactorSecret, process.env.CALENDSO_ENCRYPTION_KEY);
|
|
|
|
if (secret.length !== 32) {
|
|
|
|
console.error(
|
|
|
|
`Two factor secret decryption failed. Expected key with length 32 but got ${secret.length}`
|
|
|
|
);
|
|
|
|
throw new Error(ErrorCode.InternalServerError);
|
|
|
|
}
|
|
|
|
|
2022-12-07 20:12:14 +00:00
|
|
|
// If user has 2fa enabled, check if input.totpCode is correct
|
2022-08-31 20:57:53 +00:00
|
|
|
const isValidToken = authenticator.check(input.totpCode, secret);
|
|
|
|
if (!isValidToken) {
|
|
|
|
throw new Error(ErrorCode.IncorrectTwoFactorCode);
|
|
|
|
}
|
|
|
|
}
|
2022-08-26 21:10:12 +00:00
|
|
|
|
2022-12-07 20:12:14 +00:00
|
|
|
// If 2FA is disabled or totpCode is valid then delete the user from stripe and database
|
|
|
|
await deleteStripeCustomer(user).catch(console.warn);
|
|
|
|
// Remove my account
|
|
|
|
const deletedUser = await ctx.prisma.user.delete({
|
|
|
|
where: {
|
|
|
|
id: ctx.user.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Sync Services
|
|
|
|
syncServicesDeleteWebUser(deletedUser);
|
2022-01-14 13:49:15 +00:00
|
|
|
return;
|
2022-01-11 10:32:40 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
deleteMeWithoutPassword: authedProcedure.mutation(async ({ ctx }) => {
|
|
|
|
const user = await prisma.user.findUnique({
|
|
|
|
where: {
|
|
|
|
email: ctx.user.email.toLowerCase(),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (!user) {
|
|
|
|
throw new Error(ErrorCode.UserNotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user.identityProvider === IdentityProvider.CAL) {
|
|
|
|
throw new Error(ErrorCode.SocialIdentityProviderRequired);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user.twoFactorEnabled) {
|
|
|
|
throw new Error(ErrorCode.SocialIdentityProviderRequired);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove me from Stripe
|
|
|
|
await deleteStripeCustomer(user).catch(console.warn);
|
|
|
|
|
|
|
|
// Remove my account
|
|
|
|
const deletedUser = await ctx.prisma.user.delete({
|
|
|
|
where: {
|
|
|
|
id: ctx.user.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
// Sync Services
|
|
|
|
syncServicesDeleteWebUser(deletedUser);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}),
|
|
|
|
away: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
away: z.boolean(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2022-01-11 10:32:40 +00:00
|
|
|
await ctx.prisma.user.update({
|
|
|
|
where: {
|
|
|
|
email: ctx.user.email,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
away: input.away,
|
|
|
|
},
|
|
|
|
});
|
2021-09-30 10:46:39 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
connectedCalendars: authedProcedure.query(async ({ ctx }) => {
|
2022-12-07 21:47:02 +00:00
|
|
|
const { user, prisma } = ctx;
|
|
|
|
|
|
|
|
const userCredentials = await prisma.credential.findMany({
|
|
|
|
where: {
|
2022-12-09 17:43:19 +00:00
|
|
|
userId: ctx.user.id,
|
2022-12-07 21:47:02 +00:00
|
|
|
app: {
|
|
|
|
categories: { has: AppCategories.calendar },
|
|
|
|
enabled: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-11-10 23:40:01 +00:00
|
|
|
// get user's credentials + their connected integrations
|
2022-12-07 21:47:02 +00:00
|
|
|
const calendarCredentials = getCalendarCredentials(userCredentials);
|
2022-11-10 23:40:01 +00:00
|
|
|
|
|
|
|
// get all the connected integrations' calendars (from third party)
|
2023-02-27 18:47:21 +00:00
|
|
|
const { connectedCalendars, destinationCalendar } = await getConnectedCalendars(
|
|
|
|
calendarCredentials,
|
|
|
|
user.selectedCalendars,
|
|
|
|
user.destinationCalendar?.externalId
|
|
|
|
);
|
2023-02-08 12:19:02 +00:00
|
|
|
|
2022-11-10 23:40:01 +00:00
|
|
|
if (connectedCalendars.length === 0) {
|
|
|
|
/* As there are no connected calendars, delete the destination calendar if it exists */
|
|
|
|
if (user.destinationCalendar) {
|
|
|
|
await ctx.prisma.destinationCalendar.delete({
|
|
|
|
where: { userId: user.id },
|
|
|
|
});
|
|
|
|
user.destinationCalendar = null;
|
|
|
|
}
|
|
|
|
} else if (!user.destinationCalendar) {
|
|
|
|
/*
|
|
|
|
There are connected calendars, but no destination calendar
|
|
|
|
So create a default destination calendar with the first primary connected calendar
|
|
|
|
*/
|
2023-02-08 12:19:02 +00:00
|
|
|
const { integration = "", externalId = "", credentialId, email } = connectedCalendars[0].primary ?? {};
|
2022-11-10 23:40:01 +00:00
|
|
|
user.destinationCalendar = await ctx.prisma.destinationCalendar.create({
|
|
|
|
data: {
|
2022-10-10 17:00:09 +00:00
|
|
|
userId: user.id,
|
2022-11-10 23:40:01 +00:00
|
|
|
integration,
|
|
|
|
externalId,
|
|
|
|
credentialId,
|
2022-10-06 19:23:22 +00:00
|
|
|
},
|
|
|
|
});
|
2022-11-10 23:40:01 +00:00
|
|
|
} else {
|
|
|
|
/* There are connected calendars and a destination calendar */
|
2022-10-06 19:23:22 +00:00
|
|
|
|
2022-11-10 23:40:01 +00:00
|
|
|
// Check if destinationCalendar exists in connectedCalendars
|
|
|
|
const allCals = connectedCalendars.map((cal) => cal.calendars ?? []).flat();
|
|
|
|
const destinationCal = allCals.find(
|
|
|
|
(cal) =>
|
|
|
|
cal.externalId === user.destinationCalendar?.externalId &&
|
|
|
|
cal.integration === user.destinationCalendar?.integration
|
2022-11-04 16:43:02 +00:00
|
|
|
);
|
2023-02-08 12:19:02 +00:00
|
|
|
|
2022-11-10 23:40:01 +00:00
|
|
|
if (!destinationCal) {
|
|
|
|
// If destinationCalendar is out of date, update it with the first primary connected calendar
|
|
|
|
const { integration = "", externalId = "" } = connectedCalendars[0].primary ?? {};
|
|
|
|
user.destinationCalendar = await ctx.prisma.destinationCalendar.update({
|
|
|
|
where: { userId: user.id },
|
2022-03-31 17:26:26 +00:00
|
|
|
data: {
|
|
|
|
integration,
|
|
|
|
externalId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2023-02-08 21:13:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-10 23:40:01 +00:00
|
|
|
return {
|
|
|
|
connectedCalendars,
|
2023-02-08 21:13:10 +00:00
|
|
|
destinationCalendar: {
|
|
|
|
...(user.destinationCalendar as DestinationCalendar),
|
2023-02-27 18:47:21 +00:00
|
|
|
...destinationCalendar,
|
2023-02-08 21:13:10 +00:00
|
|
|
},
|
2022-11-10 23:40:01 +00:00
|
|
|
};
|
|
|
|
}),
|
|
|
|
setDestinationCalendar: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
integration: z.string(),
|
|
|
|
externalId: z.string(),
|
|
|
|
eventTypeId: z.number().nullish(),
|
|
|
|
bookingId: z.number().nullish(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2021-12-09 15:51:37 +00:00
|
|
|
const { user } = ctx;
|
2022-07-18 15:37:47 +00:00
|
|
|
const { integration, externalId, eventTypeId } = input;
|
2022-09-29 12:58:53 +00:00
|
|
|
const calendarCredentials = getCalendarCredentials(user.credentials);
|
2023-02-28 03:11:35 +00:00
|
|
|
const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
|
2021-12-09 15:51:37 +00:00
|
|
|
const allCals = connectedCalendars.map((cal) => cal.calendars ?? []).flat();
|
|
|
|
|
2022-07-01 20:55:27 +00:00
|
|
|
const credentialId = allCals.find(
|
|
|
|
(cal) => cal.externalId === externalId && cal.integration === integration && cal.readOnly === false
|
|
|
|
)?.credentialId;
|
|
|
|
|
|
|
|
if (!credentialId) {
|
2021-12-09 15:51:37 +00:00
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: `Could not find calendar ${input.externalId}` });
|
|
|
|
}
|
2022-01-21 21:35:31 +00:00
|
|
|
|
|
|
|
let where;
|
|
|
|
|
2023-01-05 19:55:55 +00:00
|
|
|
if (eventTypeId) {
|
|
|
|
if (
|
|
|
|
!(await prisma.eventType.findFirst({
|
|
|
|
where: {
|
|
|
|
id: eventTypeId,
|
|
|
|
userId: user.id,
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
) {
|
|
|
|
throw new TRPCError({
|
|
|
|
code: "UNAUTHORIZED",
|
|
|
|
message: `You don't have access to event type ${eventTypeId}`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
where = { eventTypeId };
|
|
|
|
} else where = { userId: user.id };
|
2022-01-21 21:35:31 +00:00
|
|
|
|
2021-12-09 15:51:37 +00:00
|
|
|
await ctx.prisma.destinationCalendar.upsert({
|
2022-01-21 21:35:31 +00:00
|
|
|
where,
|
2021-12-09 15:51:37 +00:00
|
|
|
update: {
|
2022-01-21 21:35:31 +00:00
|
|
|
integration,
|
|
|
|
externalId,
|
2022-07-01 20:55:27 +00:00
|
|
|
credentialId,
|
2021-12-09 15:51:37 +00:00
|
|
|
},
|
|
|
|
create: {
|
2022-01-21 21:35:31 +00:00
|
|
|
...where,
|
|
|
|
integration,
|
|
|
|
externalId,
|
2022-07-01 20:55:27 +00:00
|
|
|
credentialId,
|
2021-12-09 15:51:37 +00:00
|
|
|
},
|
|
|
|
});
|
2022-06-01 17:24:41 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
integrations: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
variant: z.string().optional(),
|
|
|
|
exclude: z.array(z.string()).optional(),
|
|
|
|
onlyInstalled: z.boolean().optional(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.query(async ({ ctx, input }) => {
|
2021-10-12 09:35:44 +00:00
|
|
|
const { user } = ctx;
|
2022-09-15 19:53:09 +00:00
|
|
|
const { variant, exclude, onlyInstalled } = input;
|
2021-10-12 09:35:44 +00:00
|
|
|
const { credentials } = user;
|
2022-12-07 21:47:02 +00:00
|
|
|
|
|
|
|
const enabledApps = await getEnabledApps(credentials);
|
2023-02-27 19:39:26 +00:00
|
|
|
//TODO: Refactor this to pick up only needed fields and prevent more leaking
|
2022-12-07 21:47:02 +00:00
|
|
|
let apps = enabledApps.map(
|
2023-02-27 19:39:26 +00:00
|
|
|
({ credentials: _, credential: _1, key: _2 /* don't leak to frontend */, ...app }) => {
|
2022-10-31 22:06:03 +00:00
|
|
|
const credentialIds = credentials.filter((c) => c.type === app.type).map((c) => c.id);
|
|
|
|
const invalidCredentialIds = credentials
|
|
|
|
.filter((c) => c.type === app.type && c.invalid)
|
|
|
|
.map((c) => c.id);
|
|
|
|
return {
|
|
|
|
...app,
|
|
|
|
credentialIds,
|
|
|
|
invalidCredentialIds,
|
|
|
|
};
|
|
|
|
}
|
2022-03-23 22:00:30 +00:00
|
|
|
);
|
2022-10-31 22:06:03 +00:00
|
|
|
|
2022-06-01 17:24:41 +00:00
|
|
|
if (variant) {
|
|
|
|
// `flatMap()` these work like `.filter()` but infers the types correctly
|
|
|
|
apps = apps
|
|
|
|
// variant check
|
|
|
|
.flatMap((item) => (item.variant.startsWith(variant) ? [item] : []));
|
|
|
|
}
|
2022-12-07 21:47:02 +00:00
|
|
|
|
|
|
|
if (exclude) {
|
|
|
|
// exclusion filter
|
|
|
|
apps = apps.filter((item) => (exclude ? !exclude.includes(item.variant) : true));
|
|
|
|
}
|
|
|
|
|
2022-06-01 17:24:41 +00:00
|
|
|
if (onlyInstalled) {
|
|
|
|
apps = apps.flatMap((item) => (item.credentialIds.length > 0 || item.isGlobal ? [item] : []));
|
|
|
|
}
|
2021-10-12 09:35:44 +00:00
|
|
|
return {
|
2022-06-01 17:24:41 +00:00
|
|
|
items: apps,
|
2021-10-12 09:35:44 +00:00
|
|
|
};
|
2022-07-14 12:40:53 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
appById: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
appId: z.string(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.query(async ({ ctx, input }) => {
|
2022-07-14 12:40:53 +00:00
|
|
|
const { user } = ctx;
|
|
|
|
const appId = input.appId;
|
|
|
|
const { credentials } = user;
|
|
|
|
const apps = getApps(credentials);
|
2022-10-14 16:24:43 +00:00
|
|
|
const appFromDb = apps.find((app) => app.slug === appId);
|
2022-07-14 12:40:53 +00:00
|
|
|
if (!appFromDb) {
|
2022-10-14 16:24:43 +00:00
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: `Could not find app ${appId}` });
|
2022-07-14 12:40:53 +00:00
|
|
|
}
|
2022-10-14 16:24:43 +00:00
|
|
|
|
2022-07-14 12:40:53 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
const { credential: _, credentials: _1, ...app } = appFromDb;
|
2022-10-14 16:24:43 +00:00
|
|
|
return {
|
|
|
|
isInstalled: appFromDb.credentials.length,
|
|
|
|
...app,
|
|
|
|
};
|
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
apps: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
extendsFeature: z.literal("EventType"),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.query(async ({ ctx, input }) => {
|
2022-10-14 16:24:43 +00:00
|
|
|
const { user } = ctx;
|
|
|
|
const { credentials } = user;
|
|
|
|
|
2022-12-07 21:47:02 +00:00
|
|
|
const apps = await getEnabledApps(credentials);
|
2022-10-14 16:24:43 +00:00
|
|
|
return apps
|
|
|
|
.filter((app) => app.extendsFeature?.includes(input.extendsFeature))
|
|
|
|
.map((app) => ({
|
|
|
|
...app,
|
2022-12-07 21:47:02 +00:00
|
|
|
isInstalled: !!app.credentials?.length,
|
2022-10-14 16:24:43 +00:00
|
|
|
}));
|
2022-09-15 19:53:09 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
appCredentialsByType: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
appType: z.string(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.query(async ({ ctx, input }) => {
|
2022-09-15 19:53:09 +00:00
|
|
|
const { user } = ctx;
|
|
|
|
return user.credentials.filter((app) => app.type == input.appType).map((credential) => credential.id);
|
2021-09-28 08:57:30 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
stripeCustomer: authedProcedure.query(async ({ ctx }) => {
|
|
|
|
const {
|
|
|
|
user: { id: userId },
|
|
|
|
prisma,
|
|
|
|
} = ctx;
|
|
|
|
|
|
|
|
const user = await prisma.user.findUnique({
|
|
|
|
where: {
|
|
|
|
id: userId,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
metadata: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "User not found" });
|
|
|
|
}
|
|
|
|
|
|
|
|
const metadata = userMetadata.parse(user.metadata);
|
|
|
|
|
2022-12-07 19:55:47 +00:00
|
|
|
if (!metadata?.stripeCustomerId) {
|
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: "No stripe customer id" });
|
2022-11-10 23:40:01 +00:00
|
|
|
}
|
2022-12-07 19:55:47 +00:00
|
|
|
// Fetch stripe customer
|
|
|
|
const stripeCustomerId = metadata?.stripeCustomerId;
|
|
|
|
const customer = await stripe.customers.retrieve(stripeCustomerId);
|
|
|
|
if (customer.deleted) {
|
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: "No stripe customer found" });
|
|
|
|
}
|
|
|
|
|
|
|
|
const username = customer?.metadata?.username || null;
|
2022-11-10 23:40:01 +00:00
|
|
|
|
|
|
|
return {
|
2022-12-07 19:55:47 +00:00
|
|
|
isPremium: !!metadata?.isPremium,
|
|
|
|
username,
|
2022-11-10 23:40:01 +00:00
|
|
|
};
|
|
|
|
}),
|
|
|
|
updateProfile: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
username: z.string().optional(),
|
|
|
|
name: z.string().optional(),
|
|
|
|
email: z.string().optional(),
|
|
|
|
bio: z.string().optional(),
|
|
|
|
avatar: z.string().optional(),
|
|
|
|
timeZone: z.string().optional(),
|
|
|
|
weekStart: z.string().optional(),
|
|
|
|
hideBranding: z.boolean().optional(),
|
|
|
|
allowDynamicBooking: z.boolean().optional(),
|
|
|
|
brandColor: z.string().optional(),
|
|
|
|
darkBrandColor: z.string().optional(),
|
|
|
|
theme: z.string().optional().nullable(),
|
|
|
|
completedOnboarding: z.boolean().optional(),
|
|
|
|
locale: z.string().optional(),
|
|
|
|
timeFormat: z.number().optional(),
|
|
|
|
disableImpersonation: z.boolean().optional(),
|
2023-01-31 20:44:14 +00:00
|
|
|
metadata: userMetadata.optional(),
|
2022-11-10 23:40:01 +00:00
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2021-09-28 08:57:30 +00:00
|
|
|
const { user, prisma } = ctx;
|
|
|
|
const data: Prisma.UserUpdateInput = {
|
|
|
|
...input,
|
2023-01-31 20:44:14 +00:00
|
|
|
metadata: input.metadata as Prisma.InputJsonValue,
|
2021-09-28 08:57:30 +00:00
|
|
|
};
|
2022-09-08 00:38:37 +00:00
|
|
|
let isPremiumUsername = false;
|
2021-09-28 08:57:30 +00:00
|
|
|
if (input.username) {
|
|
|
|
const username = slugify(input.username);
|
|
|
|
// Only validate if we're changing usernames
|
|
|
|
if (username !== user.username) {
|
|
|
|
data.username = username;
|
|
|
|
const response = await checkUsername(username);
|
2022-09-08 00:38:37 +00:00
|
|
|
isPremiumUsername = response.premium;
|
2022-07-06 19:31:07 +00:00
|
|
|
if (!response.available) {
|
2021-09-28 08:57:30 +00:00
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: response.message });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-30 20:37:29 +00:00
|
|
|
if (input.avatar) {
|
|
|
|
data.avatar = await resizeBase64Image(input.avatar);
|
|
|
|
}
|
2022-09-08 00:38:37 +00:00
|
|
|
const userToUpdate = await prisma.user.findUnique({
|
|
|
|
where: {
|
|
|
|
id: user.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!userToUpdate) {
|
|
|
|
throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
|
|
}
|
|
|
|
const metadata = userMetadata.parse(userToUpdate.metadata);
|
2022-12-07 19:55:47 +00:00
|
|
|
|
|
|
|
const isPremium = metadata?.isPremium;
|
2022-10-31 23:07:51 +00:00
|
|
|
if (isPremiumUsername) {
|
2022-12-07 19:55:47 +00:00
|
|
|
const stripeCustomerId = metadata?.stripeCustomerId;
|
|
|
|
if (!isPremium || !stripeCustomerId) {
|
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: "User is not premium" });
|
|
|
|
}
|
|
|
|
|
|
|
|
const stripeSubscriptions = await stripe.subscriptions.list({ customer: stripeCustomerId });
|
|
|
|
|
|
|
|
if (!stripeSubscriptions || !stripeSubscriptions.data.length) {
|
2022-10-31 23:07:51 +00:00
|
|
|
throw new TRPCError({
|
2022-12-07 19:55:47 +00:00
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
message: "No stripeSubscription found",
|
2022-10-31 23:07:51 +00:00
|
|
|
});
|
|
|
|
}
|
2022-12-07 19:55:47 +00:00
|
|
|
|
|
|
|
// Iterate over subscriptions and look for premium product id and status active
|
|
|
|
// @TODO: iterate if stripeSubscriptions.hasMore is true
|
|
|
|
const isPremiumUsernameSubscriptionActive = stripeSubscriptions.data.some(
|
|
|
|
(subscription) =>
|
|
|
|
subscription.items.data[0].price.product === getPremiumPlanProductId() &&
|
|
|
|
subscription.status === "active"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!isPremiumUsernameSubscriptionActive) {
|
2022-09-08 00:38:37 +00:00
|
|
|
throw new TRPCError({
|
|
|
|
code: "BAD_REQUEST",
|
2022-12-07 19:55:47 +00:00
|
|
|
message: "You need to pay for premium username",
|
2022-09-08 00:38:37 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2021-09-28 08:57:30 +00:00
|
|
|
|
2022-07-07 21:24:42 +00:00
|
|
|
const updatedUser = await prisma.user.update({
|
2021-09-28 08:57:30 +00:00
|
|
|
where: {
|
|
|
|
id: user.id,
|
|
|
|
},
|
|
|
|
data,
|
2022-07-07 21:24:42 +00:00
|
|
|
select: {
|
|
|
|
id: true,
|
|
|
|
username: true,
|
|
|
|
email: true,
|
|
|
|
metadata: true,
|
2022-08-26 21:10:12 +00:00
|
|
|
name: true,
|
|
|
|
createdDate: true,
|
2022-07-07 21:24:42 +00:00
|
|
|
},
|
2021-09-28 08:57:30 +00:00
|
|
|
});
|
2022-07-07 21:24:42 +00:00
|
|
|
|
2022-08-26 21:10:12 +00:00
|
|
|
// Sync Services
|
|
|
|
await syncServicesUpdateWebUser(updatedUser);
|
|
|
|
|
2022-07-07 21:24:42 +00:00
|
|
|
// Notify stripe about the change
|
|
|
|
if (updatedUser && updatedUser.metadata && hasKeyInMetadata(updatedUser, "stripeCustomerId")) {
|
|
|
|
const stripeCustomerId = `${updatedUser.metadata.stripeCustomerId}`;
|
|
|
|
await stripe.customers.update(stripeCustomerId, {
|
|
|
|
metadata: {
|
|
|
|
username: updatedUser.username,
|
|
|
|
email: updatedUser.email,
|
|
|
|
userId: updatedUser.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2021-11-15 12:25:49 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
eventTypeOrder: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
ids: z.array(z.number()),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2021-11-15 12:25:49 +00:00
|
|
|
const { prisma, user } = ctx;
|
|
|
|
const allEventTypes = await ctx.prisma.eventType.findMany({
|
|
|
|
select: {
|
|
|
|
id: true,
|
|
|
|
},
|
|
|
|
where: {
|
|
|
|
id: {
|
|
|
|
in: input.ids,
|
|
|
|
},
|
|
|
|
OR: [
|
|
|
|
{
|
|
|
|
userId: user.id,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
users: {
|
|
|
|
some: {
|
|
|
|
id: user.id,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
team: {
|
|
|
|
members: {
|
|
|
|
some: {
|
|
|
|
userId: user.id,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const allEventTypeIds = new Set(allEventTypes.map((type) => type.id));
|
|
|
|
if (input.ids.some((id) => !allEventTypeIds.has(id))) {
|
|
|
|
throw new TRPCError({
|
|
|
|
code: "UNAUTHORIZED",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
await Promise.all(
|
|
|
|
_.reverse(input.ids).map((id, position) => {
|
|
|
|
return prisma.eventType.update({
|
|
|
|
where: {
|
|
|
|
id,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}),
|
2023-01-05 19:55:55 +00:00
|
|
|
//Comment for PR: eventTypePosition is not used anywhere
|
2022-11-10 23:40:01 +00:00
|
|
|
submitFeedback: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
rating: z.string(),
|
|
|
|
comment: z.string(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2022-05-24 13:29:39 +00:00
|
|
|
const { rating, comment } = input;
|
|
|
|
|
|
|
|
const feedback = {
|
2022-06-16 16:40:14 +00:00
|
|
|
username: ctx.user.username || "Nameless",
|
2022-06-07 14:39:47 +00:00
|
|
|
email: ctx.user.email || "No email address",
|
2022-05-24 13:29:39 +00:00
|
|
|
rating: rating,
|
|
|
|
comment: comment,
|
|
|
|
};
|
|
|
|
|
|
|
|
await ctx.prisma.feedback.create({
|
|
|
|
data: {
|
|
|
|
date: dayjs().toISOString(),
|
|
|
|
userId: ctx.user.id,
|
|
|
|
rating: rating,
|
|
|
|
comment: comment,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (process.env.SEND_FEEDBACK_EMAIL && comment) sendFeedbackEmail(feedback);
|
2022-06-20 17:52:50 +00:00
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
locationOptions: authedProcedure.query(async ({ ctx }) => {
|
|
|
|
const credentials = await prisma.credential.findMany({
|
|
|
|
where: {
|
|
|
|
userId: ctx.user.id,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
id: true,
|
|
|
|
type: true,
|
|
|
|
key: true,
|
|
|
|
userId: true,
|
|
|
|
appId: true,
|
2022-12-07 21:47:02 +00:00
|
|
|
invalid: true,
|
2022-11-10 23:40:01 +00:00
|
|
|
},
|
|
|
|
});
|
2022-12-07 21:47:02 +00:00
|
|
|
|
|
|
|
const integrations = await getEnabledApps(credentials);
|
2022-11-10 23:40:01 +00:00
|
|
|
|
|
|
|
const t = await getTranslation(ctx.user.locale ?? "en", "common");
|
|
|
|
|
2022-11-24 11:53:29 +00:00
|
|
|
const locationOptions = getLocationGroupedOptions(integrations, t);
|
2022-11-10 23:40:01 +00:00
|
|
|
|
|
|
|
return locationOptions;
|
|
|
|
}),
|
|
|
|
deleteCredential: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
id: z.number(),
|
|
|
|
externalId: z.string().optional(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
2022-07-15 20:28:24 +00:00
|
|
|
const { id, externalId } = input;
|
2022-06-20 17:52:50 +00:00
|
|
|
|
|
|
|
const credential = await prisma.credential.findFirst({
|
|
|
|
where: {
|
|
|
|
id: id,
|
|
|
|
userId: ctx.user.id,
|
|
|
|
},
|
2023-02-08 20:36:22 +00:00
|
|
|
select: {
|
|
|
|
key: true,
|
|
|
|
appId: true,
|
2022-06-20 17:52:50 +00:00
|
|
|
app: {
|
|
|
|
select: {
|
|
|
|
slug: true,
|
|
|
|
categories: true,
|
2023-02-08 20:36:22 +00:00
|
|
|
dirName: true,
|
2022-06-20 17:52:50 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!credential) {
|
|
|
|
throw new TRPCError({ code: "NOT_FOUND" });
|
|
|
|
}
|
|
|
|
|
|
|
|
const eventTypes = await prisma.eventType.findMany({
|
|
|
|
where: {
|
|
|
|
userId: ctx.user.id,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
id: true,
|
|
|
|
locations: true,
|
2023-02-08 20:36:22 +00:00
|
|
|
destinationCalendar: {
|
|
|
|
include: {
|
|
|
|
credential: true,
|
|
|
|
},
|
|
|
|
},
|
2022-06-20 17:52:50 +00:00
|
|
|
price: true,
|
2022-10-14 16:24:43 +00:00
|
|
|
currency: true,
|
|
|
|
metadata: true,
|
2022-06-20 17:52:50 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-10-14 16:24:43 +00:00
|
|
|
// TODO: Improve this uninstallation cleanup per event by keeping a relation of EventType to App which has the data.
|
2022-06-20 17:52:50 +00:00
|
|
|
for (const eventType of eventTypes) {
|
|
|
|
if (eventType.locations) {
|
|
|
|
// If it's a video, replace the location with Cal video
|
|
|
|
if (credential.app?.categories.includes(AppCategories.video)) {
|
|
|
|
// Find the user's event types
|
|
|
|
|
|
|
|
// Look for integration name from app slug
|
|
|
|
const integrationQuery =
|
|
|
|
credential.app?.slug === "msteams" ? "office365_video" : credential.app?.slug.split("-")[0];
|
|
|
|
|
|
|
|
// Check if the event type uses the deleted integration
|
|
|
|
|
|
|
|
// To avoid type errors, need to stringify and parse JSON to use array methods
|
|
|
|
const locationsSchema = z.array(z.object({ type: z.string() }));
|
|
|
|
const locations = locationsSchema.parse(eventType.locations);
|
|
|
|
|
|
|
|
const updatedLocations = locations.map((location: { type: string }) => {
|
|
|
|
if (location.type.includes(integrationQuery)) {
|
2022-08-26 00:48:50 +00:00
|
|
|
return { type: DailyLocationType };
|
2022-06-20 17:52:50 +00:00
|
|
|
}
|
|
|
|
return location;
|
|
|
|
});
|
|
|
|
|
|
|
|
await prisma.eventType.update({
|
|
|
|
where: {
|
|
|
|
id: eventType.id,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
locations: updatedLocations,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-15 20:28:24 +00:00
|
|
|
// If it's a calendar, remove the destination calendar from the event type
|
2022-06-20 17:52:50 +00:00
|
|
|
if (credential.app?.categories.includes(AppCategories.calendar)) {
|
2023-02-08 20:36:22 +00:00
|
|
|
if (eventType.destinationCalendar?.credential?.appId === credential.appId) {
|
2022-07-15 20:28:24 +00:00
|
|
|
const destinationCalendar = await prisma.destinationCalendar.findFirst({
|
2022-06-20 17:52:50 +00:00
|
|
|
where: {
|
2022-07-15 20:28:24 +00:00
|
|
|
id: eventType.destinationCalendar?.id,
|
2022-06-20 17:52:50 +00:00
|
|
|
},
|
|
|
|
});
|
2022-07-15 20:28:24 +00:00
|
|
|
if (destinationCalendar) {
|
|
|
|
await prisma.destinationCalendar.delete({
|
|
|
|
where: {
|
|
|
|
id: destinationCalendar.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (externalId) {
|
|
|
|
const existingSelectedCalendar = await prisma.selectedCalendar.findFirst({
|
|
|
|
where: {
|
|
|
|
externalId: externalId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
// @TODO: SelectedCalendar doesn't have unique ID so we should only delete one item
|
|
|
|
if (existingSelectedCalendar) {
|
|
|
|
await prisma.selectedCalendar.delete({
|
|
|
|
where: {
|
|
|
|
userId_integration_externalId: {
|
|
|
|
userId: existingSelectedCalendar.userId,
|
|
|
|
externalId: existingSelectedCalendar.externalId,
|
|
|
|
integration: existingSelectedCalendar.integration,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2022-06-20 17:52:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-14 16:24:43 +00:00
|
|
|
const metadata = EventTypeMetaDataSchema.parse(eventType.metadata);
|
|
|
|
|
2023-02-08 20:36:22 +00:00
|
|
|
const stripeAppData = getPaymentAppData({ ...eventType, metadata });
|
2022-10-14 16:24:43 +00:00
|
|
|
|
2022-06-20 17:52:50 +00:00
|
|
|
// If it's a payment, hide the event type and set the price to 0. Also cancel all pending bookings
|
|
|
|
if (credential.app?.categories.includes(AppCategories.payment)) {
|
2022-10-14 16:24:43 +00:00
|
|
|
if (stripeAppData.price) {
|
2022-06-20 17:52:50 +00:00
|
|
|
await prisma.$transaction(async () => {
|
|
|
|
await prisma.eventType.update({
|
|
|
|
where: {
|
|
|
|
id: eventType.id,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
hidden: true,
|
2022-10-14 16:24:43 +00:00
|
|
|
metadata: {
|
|
|
|
...metadata,
|
|
|
|
apps: {
|
|
|
|
...metadata?.apps,
|
|
|
|
stripe: {
|
|
|
|
...metadata?.apps?.stripe,
|
|
|
|
price: 0,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2022-06-20 17:52:50 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Assuming that all bookings under this eventType need to be paid
|
|
|
|
const unpaidBookings = await prisma.booking.findMany({
|
|
|
|
where: {
|
|
|
|
userId: ctx.user.id,
|
|
|
|
eventTypeId: eventType.id,
|
|
|
|
status: "PENDING",
|
|
|
|
paid: false,
|
|
|
|
payment: {
|
|
|
|
every: {
|
|
|
|
success: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
...bookingMinimalSelect,
|
|
|
|
recurringEventId: true,
|
|
|
|
userId: true,
|
|
|
|
user: {
|
|
|
|
select: {
|
|
|
|
id: true,
|
|
|
|
credentials: true,
|
|
|
|
email: true,
|
|
|
|
timeZone: true,
|
|
|
|
name: true,
|
|
|
|
destinationCalendar: true,
|
|
|
|
locale: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
location: true,
|
|
|
|
references: {
|
|
|
|
select: {
|
|
|
|
uid: true,
|
|
|
|
type: true,
|
|
|
|
externalCalendarId: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
payment: true,
|
|
|
|
paid: true,
|
|
|
|
eventType: {
|
|
|
|
select: {
|
|
|
|
recurringEvent: true,
|
|
|
|
title: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
uid: true,
|
|
|
|
eventTypeId: true,
|
|
|
|
destinationCalendar: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const booking of unpaidBookings) {
|
|
|
|
await prisma.booking.update({
|
|
|
|
where: {
|
|
|
|
id: booking.id,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
status: BookingStatus.CANCELLED,
|
|
|
|
cancellationReason: "Payment method removed",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const payment of booking.payment) {
|
2023-02-08 20:36:22 +00:00
|
|
|
try {
|
|
|
|
await deletePayment(payment.id, credential);
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
}
|
2022-06-20 17:52:50 +00:00
|
|
|
await prisma.payment.delete({
|
|
|
|
where: {
|
|
|
|
id: payment.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
await prisma.attendee.deleteMany({
|
|
|
|
where: {
|
|
|
|
bookingId: booking.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
await prisma.bookingReference.deleteMany({
|
|
|
|
where: {
|
|
|
|
bookingId: booking.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const attendeesListPromises = booking.attendees.map(async (attendee) => {
|
|
|
|
return {
|
|
|
|
name: attendee.name,
|
|
|
|
email: attendee.email,
|
|
|
|
timeZone: attendee.timeZone,
|
|
|
|
language: {
|
|
|
|
translate: await getTranslation(attendee.locale ?? "en", "common"),
|
|
|
|
locale: attendee.locale ?? "en",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const attendeesList = await Promise.all(attendeesListPromises);
|
|
|
|
const tOrganizer = await getTranslation(booking?.user?.locale ?? "en", "common");
|
|
|
|
|
|
|
|
await sendCancelledEmails({
|
|
|
|
type: booking?.eventType?.title as string,
|
|
|
|
title: booking.title,
|
|
|
|
description: booking.description,
|
|
|
|
customInputs: isPrismaObjOrUndefined(booking.customInputs),
|
|
|
|
startTime: booking.startTime.toISOString(),
|
|
|
|
endTime: booking.endTime.toISOString(),
|
|
|
|
organizer: {
|
|
|
|
email: booking?.user?.email as string,
|
|
|
|
name: booking?.user?.name ?? "Nameless",
|
|
|
|
timeZone: booking?.user?.timeZone as string,
|
|
|
|
language: { translate: tOrganizer, locale: booking?.user?.locale ?? "en" },
|
|
|
|
},
|
|
|
|
attendees: attendeesList,
|
|
|
|
uid: booking.uid,
|
|
|
|
recurringEvent: parseRecurringEvent(booking.eventType?.recurringEvent),
|
|
|
|
location: booking.location,
|
|
|
|
destinationCalendar: booking.destinationCalendar || booking.user?.destinationCalendar,
|
|
|
|
cancellationReason: "Payment method removed by organizer",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-15 20:18:41 +00:00
|
|
|
// if zapier get disconnected, delete zapier apiKey, delete zapier webhooks and cancel all scheduled jobs from zapier
|
|
|
|
if (credential.app?.slug === "zapier") {
|
|
|
|
await prisma.apiKey.deleteMany({
|
|
|
|
where: {
|
|
|
|
userId: ctx.user.id,
|
|
|
|
appId: "zapier",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
await prisma.webhook.deleteMany({
|
|
|
|
where: {
|
2023-02-16 20:22:14 +00:00
|
|
|
userId: ctx.user.id,
|
2022-08-15 20:18:41 +00:00
|
|
|
appId: "zapier",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const bookingsWithScheduledJobs = await prisma.booking.findMany({
|
|
|
|
where: {
|
|
|
|
userId: ctx.user.id,
|
|
|
|
scheduledJobs: {
|
|
|
|
isEmpty: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
for (const booking of bookingsWithScheduledJobs) {
|
|
|
|
cancelScheduledJobs(booking, credential.appId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-20 17:52:50 +00:00
|
|
|
// Validated that credential is user's above
|
|
|
|
await prisma.credential.delete({
|
|
|
|
where: {
|
|
|
|
id: id,
|
|
|
|
},
|
|
|
|
});
|
Generate SSG Page used as cache for user's third-party calendar (#6775)
* Generate SSG Page used as cache for user's third-party calendars
* remove next-build-id dependency
* yarn.lock from main
* add support to get cached data from multiple months
* Update apps/web/pages/[user]/calendar-cache/[month].tsx
Co-authored-by: Omar López <zomars@me.com>
* Update apps/web/pages/[user]/calendar-cache/[month].tsx
Co-authored-by: Omar López <zomars@me.com>
* Update packages/core/CalendarManager.ts
Co-authored-by: Omar López <zomars@me.com>
* Add api endpoint that revalidates the current month and 3 more ahead
* Revalidate calendar cache when user connect new calendar.
* Revalidate calendar cache when user remove a calendar.
* Revalidate calendar cache when user change de selected calendars-
* Change revalidateCalendarCache function to @calcom/lib/server
* Remove the memory cache from getCachedResults
* refetch availability slots in a 3 seconds interval
* Hotfix: Event Name (#6863) (#6864)
* feat: make key unique (#6861)
* version 2.5.9 (#6868)
* Use Calendar component view=day for drop-in replacement troubleshooter (#6869)
* Use Calendar component view=day for drop-in replacement troubleshooter
* Setting the id to undefined makes the busy time selected
* Updated event title to include title+source
* lots of small changes by me and ciaran (#6871)
* Hotfix: For old Plausible installs enabled in an EventType, give a default value (#6860)
* Add default for trackingId for old plausible installs in event-types
* Fix types
* fix: filter booking in upcoming (#6406)
* fix: filter booking in upcoming
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: test
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: wipe-my-cal failing test
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
* fix workflows when duplicating event types (#6873)
* fix: get location url from metadata (#6774)
* fix: get location url from metadata
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: replace location
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: use zod
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Updates heroku deployment template (#6879)
* Hide button (#6895)
* Fixed some inconsistencies within single event type page (#6887)
* Fixed some inconsistencies within single event type page
* Fix layout shift on AvailabilityTab
* fix(date-overrides): alignment of edit & delete btns (#6892)
* When unchecking the common schedule, schedule should be nulled (#6898)
* theme for storybook
* nit border change (#6899)
* fix: metadata not saved while creating a booking. (#6866)
* feat: add metadata to booking creation
* fix: bug
* Beginning of Strict CSP Compliance (#6841)
* Add CSP Support and enable it initially for Login page
* Update README
* Make sure that CSP is not enabled if CSP_POLICY isnt set
* Add a new value for x-csp header that tells if instance has opted-in to CSP or not
* Add more src to CSP
* Fix typo in header name
* Remove duplicate headers fn
* Add https://eu.ui-avatars.com/api/
* Add CSP_POLICY to env.example
* v2.5.10
* fix: add req.headers (#6921)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* add-console-vars (#6929)
* Admin Wizard Choose License (#6574)
* Implementation
* i18n
* More i18n
* extracted i18n, needs api to get most recent price, added hint: update later
* Fixing i18n var
* Fix booking filters not working for admin (#6576)
* fix: react-select overflow issue in some modals. (#6587)
* feat: add a disable overflow prop
* feat: use the disable overflow prop
* Tailwind Merge (#6596)
* Tailwind Merge
* Fix merge classNames
* [CAL-808] /availability/single - UI issue on buttons beside time inputs (#6561)
* [CAL-808] /availability/single - UI issue on buttons beside time inputs
* Update apps/web/public/static/locales/en/common.json
* Update packages/features/schedules/components/Schedule.tsx
* create new translation for tooltip
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Bye bye submodules (#6585)
* WIP
* Uses ssh instead
* Update .gitignore
* Update .gitignore
* Update Makefile
* Update git-setup.sh
* Update git-setup.sh
* Replaced Makefile with bash script
* Update package.json
* fix: show button on empty string (#6601)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: add delete in dropdown (#6599)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update README.md
* Update README.md
* Changed a neutral- classes to gray (#6603)
* Changed a neutral- classes to gray
* Changed all border-1 to border
* Update package.json
* Test fixes
* Yarn lock fixes
* Fix string equality check in git-setup.sh
* [CAL-811] Avatar icon not redirecting user back to the main page (#6586)
* Remove cursor-pointer, remove old Avatar* files
* Fixed styling for checkedSelect + some cleanup
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Harsh/add member invite (#6598)
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
* Regenerated lockfile without upgrade (#6610)
* fix: remove annoying outline when <Button /> clicked (#6537)
* fix: remove annoying outline when <Button /> clicked
* Delete yarn.lock
* remove 1 on 1 icon (#6609)
* removed 1-on-1 badge
* changed user to users for group events
* fix: case-sensitivity in apps path (#6552)
* fix: lowercase slug
* fix: make fallback blocking
* Fix FAB (#6611)
* feat: add LocationSelect component (#6571)
* feat: add LocationSelect component
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* chore: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update booking filters design (#6543)
* Update booking filters
* Add filter on YOUR bookings
* Fix pending members showing up in list
* Reduce the avatar size to 'sm' for now
* Bugfix/dropdown menu trigger as child remove class names (#6614)
* Fix UsernameTextfield to take right height
* Remove className side-effect
* Incorrect resolution version fixed
* Converted mobile DropdownMenuTrigger styles into Button
* v2.5.3
* fix: use items-center (#6618)
* fix tooltip and modal stacking issues (#6491)
* fix tooltip and modal stacking issues
* use z-index in larger screens and less
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Temporary fix (#6626)
* Fix Ga4 tracking (#6630)
* generic <UpgradeScreen> component (#6594)
* first attempt of <UpgradeScreen>
* changes to icons
* reverted changes back to initial state, needs fix: teams not showing
* WIP
* Fix weird reactnode error
* Fix loading text
* added upgradeTip to routing forms
* icon colors
* create and use hook to check if user has team plan
* use useTeamPlan for upgradeTeamsBadge
* replace huge svg with compressed jpeg
* responsive fixes
* Update packages/ui/components/badge/UpgradeTeamsBadge.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Give team plan features to E2E tests
* Allow option to make a user part of team int ests
* Remove flash of paywall for team user
* Add team user for typeform tests as well
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* Removing env var to rely on db
* Restoring i18n keys, set loading moved
* Fixing tailwind-preset glob
* Wizard width fix for md+ screens
* Converting licenses options to radix radio
* Applying feedback + other tweaks
* Reverting this, not this PR related
* Unneeded code removal
* Reverting unneeded style change
* Applying feedback
* Removing licenseType
* Upgrades typescript
* Update yarn lock
* Typings
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712)
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712) (#6713)
* Adds deployment settings to DB (#6706)
* WIP
* Adds DeploymentTheme
* Add missing migrations
* Adds client extensions for deployment
* Cleanup
* Delete migration.sql
* Relying on both, env var and new model
* Restoring env example doc for backward compat
* Maximum call stack size exceeded fix?
* Revert upgrade
* Update index.ts
* Delete index.ts
* Not exposing license key, fixed radio behavior
* Covering undefined env var
* Self contained checkLicense
* Feedback
* Moar feedback
* Feedback
* Feedback
* Feedback
* Cleanup
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Harsh Singh <51085015+harshsinghatz@users.noreply.github.com>
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
Co-authored-by: Luis Cadillo <luiscaf3r@gmail.com>
Co-authored-by: Mohammed Cherfaoui <hi@cherfaoui.dev>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
* added two new tips (#6915)
* [CAL-488] Timezone selection has a weird double dropdown (#6851)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
* fix: color and line height of icon (#6913)
* fix: use destination calendar email (#6886)
* fix: use destination calendar email
to display correct primary email
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: simplify logic
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: dropdown title in bookings page (#6924)
* fixes the broken max size of members on teams page (#6926)
* fix: display provider name instead of url (#6914)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: add sortByLabel (#6797)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Email Variables Bug (#6943)
* Remove _subject translations for zh-CN, needs retranslation
* minor timezone-select improvements (#6944)
* fixed timezone select positioning and hover
* fixed timezone select positioning and hover
* Give trackingId a default value because if user doesnt interact with trackingId input it is not set (#6945)
* Block /auth/:path, nothing else. (#6949)
* Block /auth/:path, nothing else.
* Also add /signup
* fix start icon in button (#6950)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Fixes localisation of {EVENT_DATE} in workflows (#6907)
* translate {EVENT_DATE} variable to correct language
* fix locale for cron schedule reminder emails/sms
* fix type error
* add missing locale to attendees
* fix type error
* code clean up
* revert last commit
* using Intl for date translations
---------
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Allow account linking for Google and SAML providers (#6874)
* allow account linking for self-hosted instances, both Google and SAML are verified emails
* allow account linking for Google and SSO if emails match with existing username/password account
* Tweaked find user by email since we now have multiple providers (other than credentials provider)
* feat/payment-service-6438-cal-767 (#6677)
* WIP paymentService
* Changes for payment Service
* Fix for stripe payment flow
* Remove logs/comments
* Refactored refund for stripe app
* Move stripe handlePayment to own lib
* Move stripe delete payments to paymentService
* lint fix
* Change handleRefundError as generic function
* remove log
* remove logs
* remove logs
* Return stripe default export to lib/server
* Fixing types
* Fix types
* Upgrades typescript
* Update yarn lock
* Typings
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712)
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712) (#6713)
* Adds deployment settings to DB (#6706)
* WIP
* Adds DeploymentTheme
* Add missing migrations
* Adds client extensions for deployment
* Cleanup
* Revert "lint fix"
This reverts commit e1a2e4a357e58e6673c47399888ae2e00d1351a6.
* Add validation
* Revert changes removed in force push
* Removing abstract class and just leaving interface implementation
* Fix types for handlePayments
* Fix payment test appStore import
* Fix stripe metadata in event type
* Move migration to separate PR
* Revert "Move migration to separate PR"
This reverts commit 48aa64e0724a522d3cc2fefaaaee5792ee9cd9e6.
* Update packages/prisma/migrations/20230125175109_remove_type_from_payment_and_add_app_relationship/migration.sql
Co-authored-by: Omar López <zomars@me.com>
---------
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* Small UI fixes for seats & destination calendars (#6859)
* Do not add former time for events on seats
* Default display destination calendar
* Add seats badge to event type item
* Add string
* Actively watch seats enabled option for requires confirmation
* Only show former time when there is a rescheduleUid
* fix: use typedquery hook in duplicate dialog (#6730)
* fix: use typedquery hook in duplicate dialog
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update packages/features/eventtypes/components/DuplicateDialog.tsx
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Omar López <zomars@me.com>
* Fixing admin wizard step done (#6954)
* Feature/maintenance mode (#6930)
* Implement maintenance mode with Vercel Edge Config
* Error log is spam during development/ added \n in .env.example
* Exclude _next, /api for /maintenance page
* Re-instate previous config
* rtl: begone
* Added note to explain why /auth/login covers the maintenance page.
---------
Co-authored-by: Omar López <zomars@me.com>
* Update package.json
* I18N Caching (#6823)
* Caching Logic Changes
Enabled this function to change its cache value based on incoming paths value
* Invalidate I18N Cache
Invalidating the I18N cache when a user saves changes to their General settings
* Removes deprecated useLocale location
* Overriding the default getSchedule cache to have a revalidation time of 1 second
* Update apps/web/pages/api/trpc/[trpc].ts
* Updated cache values to match the comment
---------
Co-authored-by: zomars <zomars@me.com>
* feat: return `x-vercel-ip-timezone` in headers (#6849)
* feat: add trpc to matcher and pass vercel timezone header
* feat: pass request to context
* feat: return timezone in header
* refactor: split context
* fix: remove tsignore comment
* Update [trpc].ts
---------
Co-authored-by: zomars <zomars@me.com>
* log the json url for testing
* use WEBAPP_URL constant instead env.NEXT_PUBLIC_WEBAPP_URL
* remove the commented selectedCalendars var, it is not necessary
* Caching fixes
* Separate selectedDate slots from month slots
* Update [trpc].ts
* Log headers
* Update [trpc].ts
* Update package.json
* Fixes/trpc headers (#7045)
* Cache fixes
* Testing
* SWR breaks embed tests
* Prevent refetching day on month switch
* Skeleton fixes
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: Ben Hybert <53020786+Hybes@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sai Deepesh <saideepesh000@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Harsh Singh <51085015+harshsinghatz@users.noreply.github.com>
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
Co-authored-by: Luis Cadillo <luiscaf3r@gmail.com>
Co-authored-by: Mohammed Cherfaoui <hi@cherfaoui.dev>
Co-authored-by: Joe Shajan <joeshajan1551@gmail.com>
Co-authored-by: Deepak Prabhakara <deepak@boxyhq.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Aaron Presley <155617+AaronPresley@users.noreply.github.com>
2023-02-13 18:42:53 +00:00
|
|
|
// Revalidate user calendar cache.
|
|
|
|
if (credential.app?.slug.includes("calendar")) {
|
2023-03-10 14:18:03 +00:00
|
|
|
await fetch(`${WEBAPP_URL}/api/revalidate-calendar-cache/${ctx?.user?.username}`);
|
Generate SSG Page used as cache for user's third-party calendar (#6775)
* Generate SSG Page used as cache for user's third-party calendars
* remove next-build-id dependency
* yarn.lock from main
* add support to get cached data from multiple months
* Update apps/web/pages/[user]/calendar-cache/[month].tsx
Co-authored-by: Omar López <zomars@me.com>
* Update apps/web/pages/[user]/calendar-cache/[month].tsx
Co-authored-by: Omar López <zomars@me.com>
* Update packages/core/CalendarManager.ts
Co-authored-by: Omar López <zomars@me.com>
* Add api endpoint that revalidates the current month and 3 more ahead
* Revalidate calendar cache when user connect new calendar.
* Revalidate calendar cache when user remove a calendar.
* Revalidate calendar cache when user change de selected calendars-
* Change revalidateCalendarCache function to @calcom/lib/server
* Remove the memory cache from getCachedResults
* refetch availability slots in a 3 seconds interval
* Hotfix: Event Name (#6863) (#6864)
* feat: make key unique (#6861)
* version 2.5.9 (#6868)
* Use Calendar component view=day for drop-in replacement troubleshooter (#6869)
* Use Calendar component view=day for drop-in replacement troubleshooter
* Setting the id to undefined makes the busy time selected
* Updated event title to include title+source
* lots of small changes by me and ciaran (#6871)
* Hotfix: For old Plausible installs enabled in an EventType, give a default value (#6860)
* Add default for trackingId for old plausible installs in event-types
* Fix types
* fix: filter booking in upcoming (#6406)
* fix: filter booking in upcoming
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: test
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: wipe-my-cal failing test
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
* fix workflows when duplicating event types (#6873)
* fix: get location url from metadata (#6774)
* fix: get location url from metadata
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: replace location
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: use zod
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Updates heroku deployment template (#6879)
* Hide button (#6895)
* Fixed some inconsistencies within single event type page (#6887)
* Fixed some inconsistencies within single event type page
* Fix layout shift on AvailabilityTab
* fix(date-overrides): alignment of edit & delete btns (#6892)
* When unchecking the common schedule, schedule should be nulled (#6898)
* theme for storybook
* nit border change (#6899)
* fix: metadata not saved while creating a booking. (#6866)
* feat: add metadata to booking creation
* fix: bug
* Beginning of Strict CSP Compliance (#6841)
* Add CSP Support and enable it initially for Login page
* Update README
* Make sure that CSP is not enabled if CSP_POLICY isnt set
* Add a new value for x-csp header that tells if instance has opted-in to CSP or not
* Add more src to CSP
* Fix typo in header name
* Remove duplicate headers fn
* Add https://eu.ui-avatars.com/api/
* Add CSP_POLICY to env.example
* v2.5.10
* fix: add req.headers (#6921)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* add-console-vars (#6929)
* Admin Wizard Choose License (#6574)
* Implementation
* i18n
* More i18n
* extracted i18n, needs api to get most recent price, added hint: update later
* Fixing i18n var
* Fix booking filters not working for admin (#6576)
* fix: react-select overflow issue in some modals. (#6587)
* feat: add a disable overflow prop
* feat: use the disable overflow prop
* Tailwind Merge (#6596)
* Tailwind Merge
* Fix merge classNames
* [CAL-808] /availability/single - UI issue on buttons beside time inputs (#6561)
* [CAL-808] /availability/single - UI issue on buttons beside time inputs
* Update apps/web/public/static/locales/en/common.json
* Update packages/features/schedules/components/Schedule.tsx
* create new translation for tooltip
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Bye bye submodules (#6585)
* WIP
* Uses ssh instead
* Update .gitignore
* Update .gitignore
* Update Makefile
* Update git-setup.sh
* Update git-setup.sh
* Replaced Makefile with bash script
* Update package.json
* fix: show button on empty string (#6601)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: add delete in dropdown (#6599)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update README.md
* Update README.md
* Changed a neutral- classes to gray (#6603)
* Changed a neutral- classes to gray
* Changed all border-1 to border
* Update package.json
* Test fixes
* Yarn lock fixes
* Fix string equality check in git-setup.sh
* [CAL-811] Avatar icon not redirecting user back to the main page (#6586)
* Remove cursor-pointer, remove old Avatar* files
* Fixed styling for checkedSelect + some cleanup
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Harsh/add member invite (#6598)
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
* Regenerated lockfile without upgrade (#6610)
* fix: remove annoying outline when <Button /> clicked (#6537)
* fix: remove annoying outline when <Button /> clicked
* Delete yarn.lock
* remove 1 on 1 icon (#6609)
* removed 1-on-1 badge
* changed user to users for group events
* fix: case-sensitivity in apps path (#6552)
* fix: lowercase slug
* fix: make fallback blocking
* Fix FAB (#6611)
* feat: add LocationSelect component (#6571)
* feat: add LocationSelect component
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* chore: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update booking filters design (#6543)
* Update booking filters
* Add filter on YOUR bookings
* Fix pending members showing up in list
* Reduce the avatar size to 'sm' for now
* Bugfix/dropdown menu trigger as child remove class names (#6614)
* Fix UsernameTextfield to take right height
* Remove className side-effect
* Incorrect resolution version fixed
* Converted mobile DropdownMenuTrigger styles into Button
* v2.5.3
* fix: use items-center (#6618)
* fix tooltip and modal stacking issues (#6491)
* fix tooltip and modal stacking issues
* use z-index in larger screens and less
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Temporary fix (#6626)
* Fix Ga4 tracking (#6630)
* generic <UpgradeScreen> component (#6594)
* first attempt of <UpgradeScreen>
* changes to icons
* reverted changes back to initial state, needs fix: teams not showing
* WIP
* Fix weird reactnode error
* Fix loading text
* added upgradeTip to routing forms
* icon colors
* create and use hook to check if user has team plan
* use useTeamPlan for upgradeTeamsBadge
* replace huge svg with compressed jpeg
* responsive fixes
* Update packages/ui/components/badge/UpgradeTeamsBadge.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Give team plan features to E2E tests
* Allow option to make a user part of team int ests
* Remove flash of paywall for team user
* Add team user for typeform tests as well
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* Removing env var to rely on db
* Restoring i18n keys, set loading moved
* Fixing tailwind-preset glob
* Wizard width fix for md+ screens
* Converting licenses options to radix radio
* Applying feedback + other tweaks
* Reverting this, not this PR related
* Unneeded code removal
* Reverting unneeded style change
* Applying feedback
* Removing licenseType
* Upgrades typescript
* Update yarn lock
* Typings
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712)
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712) (#6713)
* Adds deployment settings to DB (#6706)
* WIP
* Adds DeploymentTheme
* Add missing migrations
* Adds client extensions for deployment
* Cleanup
* Delete migration.sql
* Relying on both, env var and new model
* Restoring env example doc for backward compat
* Maximum call stack size exceeded fix?
* Revert upgrade
* Update index.ts
* Delete index.ts
* Not exposing license key, fixed radio behavior
* Covering undefined env var
* Self contained checkLicense
* Feedback
* Moar feedback
* Feedback
* Feedback
* Feedback
* Cleanup
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Harsh Singh <51085015+harshsinghatz@users.noreply.github.com>
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
Co-authored-by: Luis Cadillo <luiscaf3r@gmail.com>
Co-authored-by: Mohammed Cherfaoui <hi@cherfaoui.dev>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
* added two new tips (#6915)
* [CAL-488] Timezone selection has a weird double dropdown (#6851)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
* fix: color and line height of icon (#6913)
* fix: use destination calendar email (#6886)
* fix: use destination calendar email
to display correct primary email
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: simplify logic
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: dropdown title in bookings page (#6924)
* fixes the broken max size of members on teams page (#6926)
* fix: display provider name instead of url (#6914)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: add sortByLabel (#6797)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Email Variables Bug (#6943)
* Remove _subject translations for zh-CN, needs retranslation
* minor timezone-select improvements (#6944)
* fixed timezone select positioning and hover
* fixed timezone select positioning and hover
* Give trackingId a default value because if user doesnt interact with trackingId input it is not set (#6945)
* Block /auth/:path, nothing else. (#6949)
* Block /auth/:path, nothing else.
* Also add /signup
* fix start icon in button (#6950)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Fixes localisation of {EVENT_DATE} in workflows (#6907)
* translate {EVENT_DATE} variable to correct language
* fix locale for cron schedule reminder emails/sms
* fix type error
* add missing locale to attendees
* fix type error
* code clean up
* revert last commit
* using Intl for date translations
---------
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Allow account linking for Google and SAML providers (#6874)
* allow account linking for self-hosted instances, both Google and SAML are verified emails
* allow account linking for Google and SSO if emails match with existing username/password account
* Tweaked find user by email since we now have multiple providers (other than credentials provider)
* feat/payment-service-6438-cal-767 (#6677)
* WIP paymentService
* Changes for payment Service
* Fix for stripe payment flow
* Remove logs/comments
* Refactored refund for stripe app
* Move stripe handlePayment to own lib
* Move stripe delete payments to paymentService
* lint fix
* Change handleRefundError as generic function
* remove log
* remove logs
* remove logs
* Return stripe default export to lib/server
* Fixing types
* Fix types
* Upgrades typescript
* Update yarn lock
* Typings
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712)
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712) (#6713)
* Adds deployment settings to DB (#6706)
* WIP
* Adds DeploymentTheme
* Add missing migrations
* Adds client extensions for deployment
* Cleanup
* Revert "lint fix"
This reverts commit e1a2e4a357e58e6673c47399888ae2e00d1351a6.
* Add validation
* Revert changes removed in force push
* Removing abstract class and just leaving interface implementation
* Fix types for handlePayments
* Fix payment test appStore import
* Fix stripe metadata in event type
* Move migration to separate PR
* Revert "Move migration to separate PR"
This reverts commit 48aa64e0724a522d3cc2fefaaaee5792ee9cd9e6.
* Update packages/prisma/migrations/20230125175109_remove_type_from_payment_and_add_app_relationship/migration.sql
Co-authored-by: Omar López <zomars@me.com>
---------
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* Small UI fixes for seats & destination calendars (#6859)
* Do not add former time for events on seats
* Default display destination calendar
* Add seats badge to event type item
* Add string
* Actively watch seats enabled option for requires confirmation
* Only show former time when there is a rescheduleUid
* fix: use typedquery hook in duplicate dialog (#6730)
* fix: use typedquery hook in duplicate dialog
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update packages/features/eventtypes/components/DuplicateDialog.tsx
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Omar López <zomars@me.com>
* Fixing admin wizard step done (#6954)
* Feature/maintenance mode (#6930)
* Implement maintenance mode with Vercel Edge Config
* Error log is spam during development/ added \n in .env.example
* Exclude _next, /api for /maintenance page
* Re-instate previous config
* rtl: begone
* Added note to explain why /auth/login covers the maintenance page.
---------
Co-authored-by: Omar López <zomars@me.com>
* Update package.json
* I18N Caching (#6823)
* Caching Logic Changes
Enabled this function to change its cache value based on incoming paths value
* Invalidate I18N Cache
Invalidating the I18N cache when a user saves changes to their General settings
* Removes deprecated useLocale location
* Overriding the default getSchedule cache to have a revalidation time of 1 second
* Update apps/web/pages/api/trpc/[trpc].ts
* Updated cache values to match the comment
---------
Co-authored-by: zomars <zomars@me.com>
* feat: return `x-vercel-ip-timezone` in headers (#6849)
* feat: add trpc to matcher and pass vercel timezone header
* feat: pass request to context
* feat: return timezone in header
* refactor: split context
* fix: remove tsignore comment
* Update [trpc].ts
---------
Co-authored-by: zomars <zomars@me.com>
* log the json url for testing
* use WEBAPP_URL constant instead env.NEXT_PUBLIC_WEBAPP_URL
* remove the commented selectedCalendars var, it is not necessary
* Caching fixes
* Separate selectedDate slots from month slots
* Update [trpc].ts
* Log headers
* Update [trpc].ts
* Update package.json
* Fixes/trpc headers (#7045)
* Cache fixes
* Testing
* SWR breaks embed tests
* Prevent refetching day on month switch
* Skeleton fixes
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: Ben Hybert <53020786+Hybes@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sai Deepesh <saideepesh000@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Harsh Singh <51085015+harshsinghatz@users.noreply.github.com>
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
Co-authored-by: Luis Cadillo <luiscaf3r@gmail.com>
Co-authored-by: Mohammed Cherfaoui <hi@cherfaoui.dev>
Co-authored-by: Joe Shajan <joeshajan1551@gmail.com>
Co-authored-by: Deepak Prabhakara <deepak@boxyhq.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Aaron Presley <155617+AaronPresley@users.noreply.github.com>
2023-02-13 18:42:53 +00:00
|
|
|
}
|
2022-11-10 23:40:01 +00:00
|
|
|
}),
|
|
|
|
bookingUnconfirmedCount: authedProcedure.query(async ({ ctx }) => {
|
|
|
|
const { prisma, user } = ctx;
|
|
|
|
const count = await prisma.booking.count({
|
|
|
|
where: {
|
|
|
|
status: BookingStatus.PENDING,
|
|
|
|
userId: user.id,
|
|
|
|
endTime: { gt: new Date() },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const recurringGrouping = await prisma.booking.groupBy({
|
|
|
|
by: ["recurringEventId"],
|
|
|
|
_count: {
|
|
|
|
recurringEventId: true,
|
|
|
|
},
|
|
|
|
where: {
|
|
|
|
recurringEventId: { not: { equals: null } },
|
|
|
|
status: { equals: "PENDING" },
|
|
|
|
userId: user.id,
|
|
|
|
endTime: { gt: new Date() },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return recurringGrouping.reduce((prev, current) => {
|
|
|
|
// recurringEventId is the total number of recurring instances for a booking
|
2022-12-27 21:03:39 +00:00
|
|
|
// we need to subtract all but one, to represent a single recurring booking
|
2022-11-10 23:40:01 +00:00
|
|
|
return prev - (current._count?.recurringEventId - 1);
|
|
|
|
}, count);
|
|
|
|
}),
|
2022-12-27 21:03:39 +00:00
|
|
|
getCalVideoRecordings: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
roomName: z.string(),
|
|
|
|
})
|
|
|
|
)
|
2023-01-31 20:44:14 +00:00
|
|
|
.query(async ({ input }) => {
|
2022-12-27 21:03:39 +00:00
|
|
|
const { roomName } = input;
|
2023-03-05 12:59:07 +00:00
|
|
|
|
2022-12-27 21:03:39 +00:00
|
|
|
try {
|
|
|
|
const res = await getRecordingsOfCalVideoByRoomName(roomName);
|
|
|
|
return res;
|
|
|
|
} catch (err) {
|
|
|
|
throw new TRPCError({
|
|
|
|
code: "BAD_REQUEST",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}),
|
2023-03-05 12:59:07 +00:00
|
|
|
getDownloadLinkOfCalVideoRecordings: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
recordingId: z.string(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.query(async ({ input, ctx }) => {
|
|
|
|
const { recordingId } = input;
|
|
|
|
const { session } = ctx;
|
|
|
|
|
|
|
|
const isDownloadAllowed = IS_SELF_HOSTED || session.user.belongsToActiveTeam;
|
|
|
|
|
|
|
|
if (!isDownloadAllowed) {
|
|
|
|
throw new TRPCError({
|
|
|
|
code: "FORBIDDEN",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const res = await getDownloadLinkOfCalVideoByRecordingId(recordingId);
|
|
|
|
return res;
|
|
|
|
} catch (err) {
|
|
|
|
throw new TRPCError({
|
|
|
|
code: "BAD_REQUEST",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}),
|
2023-02-13 22:55:32 +00:00
|
|
|
getUsersDefaultConferencingApp: authedProcedure.query(async ({ ctx }) => {
|
|
|
|
return userMetadata.parse(ctx.user.metadata)?.defaultConferencingApp;
|
|
|
|
}),
|
|
|
|
updateUserDefaultConferencingApp: authedProcedure
|
|
|
|
.input(
|
|
|
|
z.object({
|
|
|
|
appSlug: z.string().optional(),
|
|
|
|
appLink: z.string().optional(),
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.mutation(async ({ ctx, input }) => {
|
|
|
|
const currentMetadata = userMetadata.parse(ctx.user.metadata);
|
2023-02-14 13:19:45 +00:00
|
|
|
const credentials = ctx.user.credentials;
|
|
|
|
const foundApp = getApps(credentials).filter((app) => app.slug === input.appSlug)[0];
|
|
|
|
const appLocation = foundApp?.appData?.location;
|
|
|
|
|
|
|
|
if (!foundApp || !appLocation)
|
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: "App not installed" });
|
|
|
|
|
|
|
|
if (appLocation.linkType === "static" && !input.appLink) {
|
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: "App link is required" });
|
|
|
|
}
|
|
|
|
|
|
|
|
if (appLocation.linkType === "static" && appLocation.urlRegExp) {
|
|
|
|
const validLink = z
|
|
|
|
.string()
|
|
|
|
.regex(new RegExp(appLocation.urlRegExp), "Invalid App Link")
|
|
|
|
.parse(input.appLink);
|
|
|
|
if (!validLink) {
|
|
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid app link" });
|
|
|
|
}
|
|
|
|
}
|
2023-02-13 22:55:32 +00:00
|
|
|
|
|
|
|
await ctx.prisma.user.update({
|
|
|
|
where: {
|
|
|
|
id: ctx.user.id,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
metadata: {
|
|
|
|
...currentMetadata,
|
|
|
|
defaultConferencingApp: {
|
|
|
|
appSlug: input.appSlug,
|
|
|
|
appLink: input.appLink,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return input;
|
|
|
|
}),
|
2022-11-10 23:40:01 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
export const viewerRouter = mergeRouters(
|
|
|
|
loggedInViewerRouter,
|
|
|
|
router({
|
|
|
|
loggedInViewerRouter,
|
|
|
|
public: publicViewerRouter,
|
|
|
|
auth: authRouter,
|
Admin Wizard Choose License (#6574)
* Implementation
* i18n
* More i18n
* extracted i18n, needs api to get most recent price, added hint: update later
* Fixing i18n var
* Fix booking filters not working for admin (#6576)
* fix: react-select overflow issue in some modals. (#6587)
* feat: add a disable overflow prop
* feat: use the disable overflow prop
* Tailwind Merge (#6596)
* Tailwind Merge
* Fix merge classNames
* [CAL-808] /availability/single - UI issue on buttons beside time inputs (#6561)
* [CAL-808] /availability/single - UI issue on buttons beside time inputs
* Update apps/web/public/static/locales/en/common.json
* Update packages/features/schedules/components/Schedule.tsx
* create new translation for tooltip
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* Bye bye submodules (#6585)
* WIP
* Uses ssh instead
* Update .gitignore
* Update .gitignore
* Update Makefile
* Update git-setup.sh
* Update git-setup.sh
* Replaced Makefile with bash script
* Update package.json
* fix: show button on empty string (#6601)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: add delete in dropdown (#6599)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update README.md
* Update README.md
* Changed a neutral- classes to gray (#6603)
* Changed a neutral- classes to gray
* Changed all border-1 to border
* Update package.json
* Test fixes
* Yarn lock fixes
* Fix string equality check in git-setup.sh
* [CAL-811] Avatar icon not redirecting user back to the main page (#6586)
* Remove cursor-pointer, remove old Avatar* files
* Fixed styling for checkedSelect + some cleanup
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Harsh/add member invite (#6598)
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
* Regenerated lockfile without upgrade (#6610)
* fix: remove annoying outline when <Button /> clicked (#6537)
* fix: remove annoying outline when <Button /> clicked
* Delete yarn.lock
* remove 1 on 1 icon (#6609)
* removed 1-on-1 badge
* changed user to users for group events
* fix: case-sensitivity in apps path (#6552)
* fix: lowercase slug
* fix: make fallback blocking
* Fix FAB (#6611)
* feat: add LocationSelect component (#6571)
* feat: add LocationSelect component
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* fix: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* chore: type error
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Update booking filters design (#6543)
* Update booking filters
* Add filter on YOUR bookings
* Fix pending members showing up in list
* Reduce the avatar size to 'sm' for now
* Bugfix/dropdown menu trigger as child remove class names (#6614)
* Fix UsernameTextfield to take right height
* Remove className side-effect
* Incorrect resolution version fixed
* Converted mobile DropdownMenuTrigger styles into Button
* v2.5.3
* fix: use items-center (#6618)
* fix tooltip and modal stacking issues (#6491)
* fix tooltip and modal stacking issues
* use z-index in larger screens and less
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Temporary fix (#6626)
* Fix Ga4 tracking (#6630)
* generic <UpgradeScreen> component (#6594)
* first attempt of <UpgradeScreen>
* changes to icons
* reverted changes back to initial state, needs fix: teams not showing
* WIP
* Fix weird reactnode error
* Fix loading text
* added upgradeTip to routing forms
* icon colors
* create and use hook to check if user has team plan
* use useTeamPlan for upgradeTeamsBadge
* replace huge svg with compressed jpeg
* responsive fixes
* Update packages/ui/components/badge/UpgradeTeamsBadge.tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Give team plan features to E2E tests
* Allow option to make a user part of team int ests
* Remove flash of paywall for team user
* Add team user for typeform tests as well
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* Removing env var to rely on db
* Restoring i18n keys, set loading moved
* Fixing tailwind-preset glob
* Wizard width fix for md+ screens
* Converting licenses options to radix radio
* Applying feedback + other tweaks
* Reverting this, not this PR related
* Unneeded code removal
* Reverting unneeded style change
* Applying feedback
* Removing licenseType
* Upgrades typescript
* Update yarn lock
* Typings
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712)
* Hotfix: ping,riverside,whereby and around not showing up in list (#6712) (#6713)
* Adds deployment settings to DB (#6706)
* WIP
* Adds DeploymentTheme
* Add missing migrations
* Adds client extensions for deployment
* Cleanup
* Delete migration.sql
* Relying on both, env var and new model
* Restoring env example doc for backward compat
* Maximum call stack size exceeded fix?
* Revert upgrade
* Update index.ts
* Delete index.ts
* Not exposing license key, fixed radio behavior
* Covering undefined env var
* Self contained checkLicense
* Feedback
* Moar feedback
* Feedback
* Feedback
* Feedback
* Cleanup
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Harsh Singh <51085015+harshsinghatz@users.noreply.github.com>
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <harsh.singh@gocomet.com>
Co-authored-by: Luis Cadillo <luiscaf3r@gmail.com>
Co-authored-by: Mohammed Cherfaoui <hi@cherfaoui.dev>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-02-08 00:23:42 +00:00
|
|
|
deploymentSetup: deploymentSetupRouter,
|
2022-11-10 23:40:01 +00:00
|
|
|
bookings: bookingsRouter,
|
|
|
|
eventTypes: eventTypesRouter,
|
|
|
|
availability: availabilityRouter,
|
|
|
|
teams: viewerTeamsRouter,
|
|
|
|
webhook: webhookRouter,
|
|
|
|
apiKeys: apiKeysRouter,
|
|
|
|
slots: slotsRouter,
|
|
|
|
workflows: workflowsRouter,
|
2023-01-24 20:02:43 +00:00
|
|
|
saml: ssoRouter,
|
2022-11-10 23:40:01 +00:00
|
|
|
// NOTE: Add all app related routes in the bottom till the problem described in @calcom/app-store/trpc-routers.ts is solved.
|
|
|
|
// After that there would just one merge call here for all the apps.
|
|
|
|
appRoutingForms: app_RoutingForms,
|
|
|
|
eth: ethRouter,
|
2022-12-07 21:47:02 +00:00
|
|
|
appsRouter,
|
2022-10-10 17:00:09 +00:00
|
|
|
})
|
2022-11-10 23:40:01 +00:00
|
|
|
);
|