Files
calendar/apps/web/app/api/auth/signup/route.ts
T
Amit SharmaGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
f9d40e083f feat: store utm tags in stripe on signup (#26838)
* feat: store utm params in stripe on signup

* fix: fallback to cookie when query params don't contain valid UTM data

Changed from else-if to separate if statement so that when query
params exist but don't contain valid UTM data, the cookie fallback
is still tried. Previously, any request with non-UTM query params
would skip the stored cookie data entirely.

Co-Authored-By: unknown <>

* fix: e2e

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-22 08:13:47 -03:00

80 lines
3.0 KiB
TypeScript

import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseRequestData } from "app/api/parseRequestData";
import { NextResponse, type NextRequest } from "next/server";
import calcomSignupHandler from "./handlers/calcomSignupHandler";
import selfHostedSignupHandler from "./handlers/selfHostedHandler";
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
import { IS_PREMIUM_USERNAME_ENABLED } from "@calcom/lib/constants";
import getIP from "@calcom/lib/getIP";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { piiHasher } from "@calcom/lib/server/PiiHasher";
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";
import { prisma } from "@calcom/prisma";
import { signupSchema } from "@calcom/prisma/zod-utils";
async function ensureSignupIsEnabled(body: Record<string, string>) {
const { token } = signupSchema
.pick({
token: true,
})
.parse(body);
// Still allow signups if there is a team invite
if (token) return;
const featuresRepository = new FeaturesRepository(prisma);
const signupDisabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("disable-signup");
if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" || signupDisabled) {
throw new HttpError({
statusCode: 403,
message: "Signup is disabled",
});
}
}
async function handler(req: NextRequest) {
const remoteIp = getIP(req);
// Use a try catch instead of returning res every time
try {
// Rate limit: 10 signups per 60 seconds per IP
await checkRateLimitAndThrowError({
rateLimitingType: "core",
identifier: `api:signup:${piiHasher.hash(remoteIp)}`,
});
const body = await parseRequestData(req);
const query = Object.fromEntries(req.nextUrl.searchParams.entries());
await checkCfTurnstileToken({
token: req.headers.get("cf-access-token") as string,
remoteIp,
});
await ensureSignupIsEnabled(body);
/**
* Im not sure its worth merging these two handlers. They are different enough to be separate.
* Calcom handles things like creating a stripe customer - which we don't need to do for self hosted.
* It also handles things like premium username.
* TODO: (SEAN) - Extract a lot of the logic from calcomHandler into a separate file and import it into both handlers.
* @zomars: We need to be able to test this with E2E. They way it's done RN it will never run on CI.
*/
if (IS_PREMIUM_USERNAME_ENABLED) {
return await calcomSignupHandler(body, query);
}
return await selfHostedSignupHandler(body);
} catch (e) {
if (e instanceof HttpError) {
return NextResponse.json({ message: e.message }, { status: e.statusCode });
}
logger.error(e);
return NextResponse.json({ message: "Internal server error" }, { status: 500 });
}
}
export const POST = defaultResponderForAppDir(handler);