Skip to content

Commit

Permalink
Added rate limiting
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey committed Sep 13, 2022
1 parent e1107f1 commit 5306d51
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 36 deletions.
2 changes: 1 addition & 1 deletion lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const FRAMER_MOTION_LIST_ITEM_VARIANTS = {
show: { scale: 1, opacity: 1, transition: { type: "spring" } },
};

export const RESERVED_KEYS = ["pricing", "about", "stats"];
export const RESERVED_KEYS = ["pricing", "about", "stats", "placeholder"];

export const COUNTRIES: { [key: string]: string } = {
AF: "Afghanistan",
Expand Down
42 changes: 33 additions & 9 deletions lib/middleware/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,53 @@ import { NextRequest, NextFetchEvent, NextResponse } from "next/server";
import { redis, recordClick } from "@/lib/upstash";
import { parse } from "@/lib/middleware/utils";
import { LinkProps } from "../types";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
});

export default async function LinkMiddleware(
req: NextRequest,
ev: NextFetchEvent
) {
const url = req.nextUrl.clone();
const { hostname, key } = parse(req);

if (!hostname || !key) {
return NextResponse.next();
}

const response = await redis.hget<Omit<LinkProps, "key">>(
`${hostname}:links`,
key
const ip = req.ip ?? "127.0.0.1";
const { success, limit, reset, remaining } = await ratelimit.limit(
`dub_${ip}`
);
const target = response?.url;

if (target) {
ev.waitUntil(recordClick(hostname, key, req)); // increment click count
return NextResponse.redirect(target);
let res;

if (success) {
// if ratelimit is not exceeded
const response = await redis.hget<Omit<LinkProps, "key">>(
`${hostname}:links`,
key
);
const target = response?.url;

if (target) {
ev.waitUntil(recordClick(hostname, key, req)); // increment click count
res = NextResponse.redirect(target);
} else {
url.pathname = "/";
res = NextResponse.redirect(url);
}
} else {
const url = req.nextUrl.clone();
url.pathname = "/";
return NextResponse.redirect(url);
res = NextResponse.redirect(url); // TODO: add a rate limit page
res.headers.set("X-RateLimit-Limit", limit.toString());
res.headers.set("X-RateLimit-Remaining", remaining.toString());
res.headers.set("X-RateLimit-Reset", reset.toString());
}
return res;
}
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@prisma/client": "^4.3.1",
"@radix-ui/react-tooltip": "^1.0.0",
"@tailwindcss/forms": "^0.5.3",
"@upstash/ratelimit": "^0.1.5",
"@upstash/redis": "^1.12.0",
"@visx/axis": "^2.12.2",
"@visx/event": "^2.6.0",
Expand Down
33 changes: 29 additions & 4 deletions pages/api/projects/[slug]/domains/[domain]/verify.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { withProjectAuth } from "@/lib/auth";
import { NextApiRequest, NextApiResponse } from "next";
import { DomainVerificationStatusProps } from "@/lib/types";
import prisma from "@/lib/prisma";

export default withProjectAuth(
async (req: NextApiRequest, res: NextApiResponse) => {
const { domain } = req.query as { domain: string };
const { slug, domain } = req.query as { slug: string; domain: string };
let status: DomainVerificationStatusProps = "Valid Configuration";

const [domainResponse, configResponse] = await Promise.all([
Expand Down Expand Up @@ -67,15 +68,39 @@ export default withProjectAuth(

return res.status(200).json({
status,
response: { configJson, domainJson, verificationResponse },
response: {
configJson,
domainJson,
verificationResponse,
},
});
}

status = configJson.misconfigured ? "Invalid Configuration" : status;
let prismaResponse = null;
if (!configJson.misconfigured) {
prismaResponse = await prisma.project.update({
where: {
slug,
},
data: {
domainVerified: true,
},
});
} else {
status = "Invalid Configuration";
prismaResponse = await prisma.project.update({
where: {
slug,
},
data: {
domainVerified: false,
},
});
}

return res.status(200).json({
status,
response: { configJson, domainJson },
response: { configJson, domainJson, prismaResponse },
});
}
);
20 changes: 7 additions & 13 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import PlaceholderCard from "@/components/home/placeholder-card";
import { LoadingDots } from "@/components/shared/icons";
import { motion } from "framer-motion";
import { LinkProps } from "@/lib/types";
import Globe from "@/components/home/about/globe";

export default function Home() {
const [saving, setSaving] = useState(false);
Expand All @@ -17,20 +18,12 @@ export default function Home() {
<HomeLayout>
<main className="my-36 max-w-md mx-auto sm:px-0 px-2.5">
<div className="my-5 text-center">
<h1 className="text-6xl font-bold text-black dark:text-white">Dub</h1>
<h1 className="text-6xl font-display font-bold text-black dark:text-white">
Dub
</h1>
<p className="text-gray-600 dark:text-white text-xl mt-5">
An open-source link shortener built with <br />
<VercelEdgeFunctions /> and{" "}
<a
className="text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-green-700
"
href="https://upstash.com/redis"
target="_blank"
rel="noreferrer"
>
Upstash Redis
</a>
.
An open-source link shortener with built-in analytics and free
custom domains.
</p>
</div>
<form
Expand Down Expand Up @@ -103,6 +96,7 @@ export default function Home() {
))}
</motion.ul>
</main>
<Globe />
</HomeLayout>
);
}
18 changes: 18 additions & 0 deletions pages/placeholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import HomeLayout from "@/components/layout/home";
import MaxWidthWrapper from "@/components/shared/max-width-wrapper";
import Globe from "@/components/home/about/globe";

export default function Placeholder() {
return (
<HomeLayout>
<MaxWidthWrapper>
<div className="mt-36 text-center">
<h1 className="font-display font-semibold text-3xl text-gray-700">
This is a custom domain from Dub.sh
</h1>
</div>
</MaxWidthWrapper>
<Globe />
</HomeLayout>
);
}
35 changes: 27 additions & 8 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"],
"@/pages/*": ["pages/*"],
"@/lib/*": ["lib/*"],
"@/styles/*": ["styles/*"]
"@/components/*": [
"components/*"
],
"@/pages/*": [
"pages/*"
],
"@/lib/*": [
"lib/*"
],
"@/styles/*": [
"styles/*"
]
},
"forceConsistentCasingInFileNames": true,
"noEmit": true,
Expand All @@ -19,8 +31,15 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"strict": false
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
7 changes: 6 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,12 @@
dependencies:
"@types/geojson" "*"

"@upstash/redis@^1.12.0":
"@upstash/ratelimit@^0.1.5":
"integrity" "sha512-v0zq3Xpq42HQBvXDvm0C4djYvKC5TjHG29uCpbIQcOw0znJNroHewI8y4Yk4tBeEF1rvj2RjDkFihjCp6uTExQ=="
"resolved" "https://registry.npmjs.org/@upstash/ratelimit/-/ratelimit-0.1.5.tgz"
"version" "0.1.5"

"@upstash/redis@^1.12.0", "@upstash/redis@^1.4.0":
"integrity" "sha512-i6oarviHs7xOi9aLe8N52bFSg8FAC8JeArINLqtlEuLGNTkIhdNIJYEs4Sk/xq0EDmHSXFGPWdp+6R8JL9XoIg=="
"resolved" "https://registry.npmjs.org/@upstash/redis/-/redis-1.12.0.tgz"
"version" "1.12.0"
Expand Down

0 comments on commit 5306d51

Please sign in to comment.