* remove event type requires confirmation * allow sms sending also for team (frontend) * allow text to attendee for team (backend) * add rate limiting for SMS (WIP) * add functionality to lock and unlock SMS sending (+admin UI view) * rename to smsLockState * add ability to lock users and teams * don't send sms if locked * add missing imports * fix rate limit namespace * add translation * fix type error * remove prop from UsersTable * add smsLockState to buildUser * code clean up * create seperate sms rate limit function * fix type error * add missing userId and teamId to sendSMS * code improvements * add User Team type * fix marking as reviewed * check monthly sms limit only for user * don't lock orgs * fix type error * fix avatars * fix type error * fix type error * fix type error --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
80 lines
2.0 KiB
TypeScript
80 lines
2.0 KiB
TypeScript
import prisma from "@calcom/prisma";
|
|
import { SMSLockState } from "@calcom/prisma/enums";
|
|
import { TRPCError } from "@calcom/trpc/server";
|
|
|
|
import type { RateLimitHelper } from "./rateLimit";
|
|
import { rateLimiter } from "./rateLimit";
|
|
|
|
export async function checkRateLimitAndThrowError({
|
|
rateLimitingType = "core",
|
|
identifier,
|
|
onRateLimiterResponse,
|
|
opts,
|
|
}: RateLimitHelper) {
|
|
const response = await rateLimiter()({ rateLimitingType, identifier, opts });
|
|
const { success, reset } = response;
|
|
|
|
if (onRateLimiterResponse) onRateLimiterResponse(response);
|
|
|
|
if (!success) {
|
|
const convertToSeconds = (ms: number) => Math.floor(ms / 1000);
|
|
const secondsToWait = convertToSeconds(reset - Date.now());
|
|
throw new TRPCError({
|
|
code: "TOO_MANY_REQUESTS",
|
|
message: `Rate limit exceeded. Try again in ${secondsToWait} seconds.`,
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function checkSMSRateLimit({
|
|
rateLimitingType = "sms",
|
|
identifier,
|
|
onRateLimiterResponse,
|
|
opts,
|
|
}: RateLimitHelper) {
|
|
const response = await rateLimiter()({ rateLimitingType, identifier, opts });
|
|
const { success } = response;
|
|
|
|
if (onRateLimiterResponse) onRateLimiterResponse(response);
|
|
|
|
if (!success) {
|
|
await changeSMSLockState(
|
|
identifier,
|
|
rateLimitingType === "sms" ? SMSLockState.LOCKED : SMSLockState.REVIEW_NEEDED
|
|
);
|
|
}
|
|
}
|
|
|
|
async function changeSMSLockState(identifier: string, status: SMSLockState) {
|
|
let userId, teamId;
|
|
|
|
if (identifier.startsWith("sms:user:")) {
|
|
userId = Number(identifier.slice(9));
|
|
} else if (identifier.startsWith("sms:team:")) {
|
|
teamId = Number(identifier.slice(9));
|
|
}
|
|
|
|
if (userId) {
|
|
await prisma.user.update({
|
|
where: {
|
|
id: userId,
|
|
profiles: { none: {} },
|
|
},
|
|
data: {
|
|
smsLockState: status,
|
|
},
|
|
});
|
|
} else {
|
|
await prisma.team.update({
|
|
where: {
|
|
id: teamId,
|
|
parentId: null,
|
|
isOrganization: false,
|
|
},
|
|
data: {
|
|
smsLockState: status,
|
|
},
|
|
});
|
|
}
|
|
}
|