summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAhmet Kilinc <akx9@icloud.com>2025-07-16 01:00:40 +0100
committerAhmet Kilinc <akx9@icloud.com>2025-07-16 01:00:40 +0100
commiteb49500592e5c50c5637c09ea585b065545f2135 (patch)
tree8cf0df336f2af737ef775c516c1f3380b418bc7e
parent5178732c4c1205698d1846dd82896d9342558ac6 (diff)
downloadopencut-eb49500592e5c50c5637c09ea585b065545f2135.tar.gz
adds Vercel BotId
-rw-r--r--apps/web/next.config.ts3
-rw-r--r--apps/web/package.json1
-rw-r--r--apps/web/src/app/api/waitlist/route.ts7
-rw-r--r--apps/web/src/app/layout.tsx11
-rw-r--r--bun.lock3
5 files changed, 24 insertions, 1 deletions
diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts
index a0a0d31b..02261068 100644
--- a/apps/web/next.config.ts
+++ b/apps/web/next.config.ts
@@ -1,4 +1,5 @@
import type { NextConfig } from "next";
+import { withBotId } from "botid/next/config";
const nextConfig: NextConfig = {
compiler: {
@@ -21,4 +22,4 @@ const nextConfig: NextConfig = {
},
};
-export default nextConfig;
+export default withBotId(nextConfig);
diff --git a/apps/web/package.json b/apps/web/package.json
index 07b36dcb..193eb244 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -27,6 +27,7 @@
"@upstash/redis": "^1.35.0",
"@vercel/analytics": "^1.4.1",
"better-auth": "^1.2.7",
+ "botid": "^1.4.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
diff --git a/apps/web/src/app/api/waitlist/route.ts b/apps/web/src/app/api/waitlist/route.ts
index 4d64be77..031ebd5a 100644
--- a/apps/web/src/app/api/waitlist/route.ts
+++ b/apps/web/src/app/api/waitlist/route.ts
@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from "next/server";
import { db, eq } from "@opencut/db";
import { waitlist } from "@opencut/db/schema";
+import { checkBotId } from "botid/server";
import { nanoid } from "nanoid";
import { waitlistRateLimit } from "@/lib/rate-limit";
import { z } from "zod";
@@ -143,6 +144,12 @@ async function validateCSRFToken(request: NextRequest): Promise<boolean> {
}
export async function POST(request: NextRequest) {
+ const verification = await checkBotId();
+
+ if (verification.isBot) {
+ return NextResponse.json({ error: "Access denied" }, { status: 403 });
+ }
+
const identifier = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success } = await waitlistRateLimit.limit(identifier);
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 9ac54262..1cbc3f2e 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -7,9 +7,17 @@ import { TooltipProvider } from "../components/ui/tooltip";
import { StorageProvider } from "../components/storage-provider";
import { baseMetaData } from "./metadata";
import { defaultFont } from "../lib/font-config";
+import { BotIdClient } from "botid/client";
export const metadata = baseMetaData;
+const protectedRoutes = [
+ {
+ path: "/api/waitlist",
+ method: "POST",
+ },
+];
+
export default function RootLayout({
children,
}: Readonly<{
@@ -17,6 +25,9 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
+ <head>
+ <BotIdClient protect={protectedRoutes} />
+ </head>
<body className={`${defaultFont.className} font-sans antialiased`}>
<ThemeProvider attribute="class" forcedTheme="dark">
<TooltipProvider>
diff --git a/bun.lock b/bun.lock
index 0e22ec75..7102ca46 100644
--- a/bun.lock
+++ b/bun.lock
@@ -28,6 +28,7 @@
"@upstash/redis": "^1.35.0",
"@vercel/analytics": "^1.4.1",
"better-auth": "^1.2.7",
+ "botid": "^1.4.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
@@ -507,6 +508,8 @@
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
+ "botid": ["botid@1.4.2", "", { "peerDependencies": { "next": "*", "react": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["next"] }, "sha512-yiRWEdxXa5QhxzJW4lTk0lRZkbqPsVWdGrhnHLLihZf0xBEtsTUGtxLqK++IY80FX/Ye/rNMnGqBp2pl4yYU8w=="],
+
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],