@upstash/ratelimit 0.4.4 → 0.4.5-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/redis/action.yaml +58 -0
- package/.github/img/dashboard.png +0 -0
- package/.github/workflows/release.yml +46 -0
- package/.github/workflows/stale.yaml +31 -0
- package/.github/workflows/tests.yaml +79 -0
- package/README.md +9 -2
- package/biome.json +37 -0
- package/bun.lockb +0 -0
- package/cmd/set-version.js +14 -0
- package/examples/cloudflare-workers/package.json +18 -0
- package/examples/cloudflare-workers/src/index.ts +35 -0
- package/examples/cloudflare-workers/tsconfig.json +105 -0
- package/examples/cloudflare-workers/wrangler.toml +3 -0
- package/examples/nextjs/LICENSE +21 -0
- package/examples/nextjs/README.md +17 -0
- package/examples/nextjs/components/Breadcrumb.tsx +67 -0
- package/examples/nextjs/components/Header.tsx +18 -0
- package/examples/nextjs/components/ReadBlogPost.tsx +9 -0
- package/examples/nextjs/components/StarButton.tsx +27 -0
- package/examples/nextjs/middleware.ts +35 -0
- package/examples/nextjs/next-env.d.ts +5 -0
- package/examples/nextjs/package.json +27 -0
- package/examples/nextjs/pages/_app.tsx +47 -0
- package/examples/nextjs/pages/api/blocked.ts +6 -0
- package/examples/nextjs/pages/api/hello.ts +5 -0
- package/examples/nextjs/pages/index.tsx +62 -0
- package/examples/nextjs/postcss.config.js +6 -0
- package/examples/nextjs/public/favicon.ico +0 -0
- package/examples/nextjs/public/github.svg +11 -0
- package/examples/nextjs/public/upstash.svg +27 -0
- package/examples/nextjs/styles/globals.css +76 -0
- package/examples/nextjs/tailwind.config.js +19 -0
- package/examples/nextjs/tsconfig.json +21 -0
- package/examples/nextjs13/README.md +38 -0
- package/examples/nextjs13/app/favicon.ico +0 -0
- package/examples/nextjs13/app/globals.css +107 -0
- package/examples/nextjs13/app/layout.tsx +18 -0
- package/examples/nextjs13/app/page.module.css +271 -0
- package/examples/nextjs13/app/route.tsx +14 -0
- package/examples/nextjs13/next.config.js +8 -0
- package/examples/nextjs13/package.json +22 -0
- package/examples/nextjs13/public/next.svg +1 -0
- package/examples/nextjs13/public/thirteen.svg +1 -0
- package/examples/nextjs13/public/vercel.svg +1 -0
- package/examples/nextjs13/tsconfig.json +28 -0
- package/examples/remix/.env.example +2 -0
- package/examples/remix/.eslintrc.js +4 -0
- package/examples/remix/README.md +59 -0
- package/examples/remix/app/root.tsx +25 -0
- package/examples/remix/app/routes/index.tsx +47 -0
- package/examples/remix/package.json +32 -0
- package/examples/remix/public/favicon.ico +0 -0
- package/examples/remix/remix.config.js +12 -0
- package/examples/remix/remix.env.d.ts +2 -0
- package/examples/remix/server.js +4 -0
- package/examples/remix/tsconfig.json +22 -0
- package/examples/with-vercel-kv/README.md +51 -0
- package/examples/with-vercel-kv/app/favicon.ico +0 -0
- package/examples/with-vercel-kv/app/globals.css +27 -0
- package/examples/with-vercel-kv/app/layout.tsx +21 -0
- package/examples/with-vercel-kv/app/page.tsx +71 -0
- package/examples/with-vercel-kv/next.config.js +8 -0
- package/examples/with-vercel-kv/package.json +25 -0
- package/examples/with-vercel-kv/postcss.config.js +6 -0
- package/examples/with-vercel-kv/public/next.svg +1 -0
- package/examples/with-vercel-kv/public/vercel.svg +1 -0
- package/examples/with-vercel-kv/tailwind.config.js +17 -0
- package/examples/with-vercel-kv/tsconfig.json +28 -0
- package/package.json +12 -33
- package/src/analytics.test.ts +23 -0
- package/src/analytics.ts +92 -0
- package/src/blockUntilReady.test.ts +56 -0
- package/src/cache.test.ts +41 -0
- package/src/cache.ts +43 -0
- package/src/duration.test.ts +23 -0
- package/src/duration.ts +30 -0
- package/src/index.ts +17 -0
- package/src/multi.ts +365 -0
- package/src/ratelimit.test.ts +155 -0
- package/src/ratelimit.ts +238 -0
- package/src/single.ts +487 -0
- package/src/test_utils.ts +65 -0
- package/src/tools/seed.ts +37 -0
- package/src/types.ts +78 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +103 -0
- package/tsup.config.js +11 -0
- package/turbo.json +16 -0
- package/dist/index.d.ts +0 -556
- package/dist/index.js +0 -832
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -803
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Ratelimit } from "@upstash/ratelimit";
|
|
2
|
+
import { Redis } from "@upstash/redis";
|
|
3
|
+
import { type NextFetchEvent, type NextRequest, NextResponse } from "next/server";
|
|
4
|
+
|
|
5
|
+
const ratelimit = new Ratelimit({
|
|
6
|
+
redis: Redis.fromEnv(),
|
|
7
|
+
limiter: Ratelimit.cachedFixedWindow(10, "10s"),
|
|
8
|
+
ephemeralCache: new Map(),
|
|
9
|
+
analytics: true,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default async function middleware(
|
|
13
|
+
request: NextRequest,
|
|
14
|
+
event: NextFetchEvent,
|
|
15
|
+
): Promise<Response | undefined> {
|
|
16
|
+
const ip = request.ip ?? "127.0.0.1";
|
|
17
|
+
|
|
18
|
+
const { success, pending, limit, reset, remaining } = await ratelimit.limit(
|
|
19
|
+
`ratelimit_middleware_${ip}`,
|
|
20
|
+
);
|
|
21
|
+
event.waitUntil(pending);
|
|
22
|
+
|
|
23
|
+
const res = success
|
|
24
|
+
? NextResponse.next()
|
|
25
|
+
: NextResponse.redirect(new URL("/api/blocked", request.url));
|
|
26
|
+
|
|
27
|
+
res.headers.set("X-RateLimit-Limit", limit.toString());
|
|
28
|
+
res.headers.set("X-RateLimit-Remaining", remaining.toString());
|
|
29
|
+
res.headers.set("X-RateLimit-Reset", reset.toString());
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const config = {
|
|
34
|
+
matcher: "/api/hello",
|
|
35
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "upstash-ratelimit-nextjs",
|
|
3
|
+
"private": true,
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "next dev",
|
|
6
|
+
"build": "next build",
|
|
7
|
+
"start": "next start"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@upstash/ratelimit": "workspace:*",
|
|
11
|
+
"@upstash/redis": "^1.20.6",
|
|
12
|
+
"next": "^13.4.1",
|
|
13
|
+
"react": "^18.2.0",
|
|
14
|
+
"react-dom": "^18.2.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@tailwindcss/forms": "^0.5.3",
|
|
18
|
+
"@types/node": "^20.1.2",
|
|
19
|
+
"@types/react": "^18.2.6",
|
|
20
|
+
"autoprefixer": "^10.4.14",
|
|
21
|
+
"postcss": "^8.4.23",
|
|
22
|
+
"prettier": "^2.8.8",
|
|
23
|
+
"prettier-plugin-tailwindcss": "^0.2.8",
|
|
24
|
+
"tailwindcss": "^3.3.2",
|
|
25
|
+
"typescript": "^5.0.4"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import "styles/globals.css";
|
|
2
|
+
|
|
3
|
+
import Header from "components/Header";
|
|
4
|
+
import ReadBlogPost from "components/ReadBlogPost";
|
|
5
|
+
import type { AppProps } from "next/app";
|
|
6
|
+
import Head from "next/head";
|
|
7
|
+
import React from "react";
|
|
8
|
+
|
|
9
|
+
function MyApp({ Component, pageProps }: AppProps) {
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
<Head>
|
|
13
|
+
<title>Create Next App</title>
|
|
14
|
+
<link rel="icon" href="/favicon.ico" />
|
|
15
|
+
</Head>
|
|
16
|
+
|
|
17
|
+
<Header
|
|
18
|
+
breadcrumbOptions={{
|
|
19
|
+
data: [
|
|
20
|
+
{
|
|
21
|
+
name: "ratelimit",
|
|
22
|
+
url: "https://github.com/upstash/ratelimit",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
{/*<ReadBlogPost>
|
|
29
|
+
This is a sample project for the blogpost{" "}
|
|
30
|
+
<a
|
|
31
|
+
className="text-primary-600"
|
|
32
|
+
target="_blank"
|
|
33
|
+
rel="noopener noreferrer"
|
|
34
|
+
href="https://blog.upstash.com/nextjs-caching-with-redis"
|
|
35
|
+
>
|
|
36
|
+
Example Post
|
|
37
|
+
</a>
|
|
38
|
+
</ReadBlogPost>*/}
|
|
39
|
+
|
|
40
|
+
<div className="w-full max-w-3xl px-6 mx-auto py-14">
|
|
41
|
+
<Component {...pageProps} />
|
|
42
|
+
</div>
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default MyApp;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { NextPage } from "next";
|
|
2
|
+
import { useRouter } from "next/router";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
const Home: NextPage = () => {
|
|
6
|
+
const [response, setResponse] = useState<Record<string, unknown> | null>(null);
|
|
7
|
+
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
useEffect(() => {}, []);
|
|
10
|
+
|
|
11
|
+
const generate = async () => {
|
|
12
|
+
const res = await fetch("/api/hello");
|
|
13
|
+
|
|
14
|
+
if (res.ok) {
|
|
15
|
+
setResponse({
|
|
16
|
+
status: res.status,
|
|
17
|
+
body: await res.json(),
|
|
18
|
+
headers: {
|
|
19
|
+
"X-RateLimit-Limit": res.headers.get("X-RateLimit-Limit"),
|
|
20
|
+
"X-RateLimit-Remaining": res.headers.get("X-RateLimit-Remaining"),
|
|
21
|
+
"X-RateLimit-Reset": res.headers.get("X-RateLimit-Reset"),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
setResponse(null);
|
|
26
|
+
|
|
27
|
+
alert("Ratelimit reached, try again later");
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<main>
|
|
33
|
+
<header>
|
|
34
|
+
<h1 className="text-4xl font-bold">
|
|
35
|
+
Welcome to <span className="text-primary-500">@upstash/ratelimit</span>
|
|
36
|
+
</h1>
|
|
37
|
+
|
|
38
|
+
<p className="mt-4">
|
|
39
|
+
This is an example of how to ratelimit your nextjs app at the edge using Vercel Edge and
|
|
40
|
+
Upstash Redis
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p className="mt-4">
|
|
44
|
+
Click the button below to make a request, that will be ratelimited by your IP.
|
|
45
|
+
</p>
|
|
46
|
+
</header>
|
|
47
|
+
|
|
48
|
+
<hr className="my-10" />
|
|
49
|
+
|
|
50
|
+
<div className="grid grid-cols-1 gap-6">
|
|
51
|
+
<div className="flex justify-center">
|
|
52
|
+
<button onClick={generate}>Make a request</button>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
{response ? <pre>{JSON.stringify(response, null, 2)}</pre> : null}
|
|
56
|
+
</div>
|
|
57
|
+
</main>
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default Home;
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
width="32"
|
|
3
|
+
height="32"
|
|
4
|
+
viewBox="0 0 16 16"
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
>
|
|
7
|
+
<path
|
|
8
|
+
fill-rule="evenodd"
|
|
9
|
+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
|
|
10
|
+
/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<svg width="90" height="26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#a)">
|
|
3
|
+
<path
|
|
4
|
+
d="M.023 22.749c4.303 4.302 11.278 4.302 15.58 0 4.303-4.303 4.303-11.278 0-15.58l-1.947 1.947A8.263 8.263 0 1 1 1.97 20.801L.023 22.75Z"
|
|
5
|
+
fill="#00C98D"/>
|
|
6
|
+
<path d="M3.918 18.854a5.509 5.509 0 0 0 7.79-7.79L9.761 13.01a2.754 2.754 0 1 1-3.895 3.895l-1.948 1.948Z"
|
|
7
|
+
fill="#00C98D"/>
|
|
8
|
+
<path
|
|
9
|
+
d="M19.498 3.273c-4.302-4.302-11.277-4.302-15.58 0-4.302 4.302-4.302 11.278 0 15.58l1.948-1.947A8.263 8.263 0 0 1 17.55 5.22l1.947-1.948Z"
|
|
10
|
+
fill="#00C98D"/>
|
|
11
|
+
<path d="M15.603 7.168a5.508 5.508 0 0 0-7.79 7.79l1.948-1.947a2.754 2.754 0 0 1 3.895-3.895l1.947-1.948Z"
|
|
12
|
+
fill="#00C98D"/>
|
|
13
|
+
<path
|
|
14
|
+
d="M19.498 3.273c-4.302-4.302-11.277-4.302-15.58 0-4.302 4.302-4.302 11.278 0 15.58l1.948-1.947A8.263 8.263 0 0 1 17.55 5.22l1.947-1.948Z"
|
|
15
|
+
fill="#fff" fill-opacity=".4"/>
|
|
16
|
+
<path d="M15.603 7.168a5.508 5.508 0 0 0-7.79 7.79l1.948-1.947a2.754 2.754 0 0 1 3.895-3.895l1.947-1.948Z"
|
|
17
|
+
fill="#fff" fill-opacity=".4"/>
|
|
18
|
+
</g>
|
|
19
|
+
<path
|
|
20
|
+
d="M32.396 14.523V9.3h2.124v9.014h-2.06V16.71h-.094a2.711 2.711 0 0 1-1.003 1.239c-.462.32-1.031.48-1.708.48-.59 0-1.113-.13-1.567-.392a2.733 2.733 0 0 1-1.056-1.156c-.254-.509-.381-1.123-.381-1.843v-5.74h2.124v5.411c0 .571.156 1.025.47 1.362.312.336.723.504 1.232.504.313 0 .616-.076.91-.228a1.85 1.85 0 0 0 .721-.681c.192-.305.288-.687.288-1.144Zm3.978 7.17V9.3h2.09v1.49h.122c.11-.219.264-.452.464-.698.2-.25.47-.464.81-.64.34-.18.775-.27 1.303-.27.696 0 1.324.178 1.883.534.564.352 1.01.875 1.338 1.567.333.689.5 1.534.5 2.535 0 .99-.163 1.831-.488 2.524-.325.692-.767 1.22-1.326 1.584a3.41 3.41 0 0 1-1.901.546c-.517 0-.945-.086-1.286-.258a2.615 2.615 0 0 1-.821-.622 4.028 4.028 0 0 1-.476-.699h-.088v4.8h-2.124Zm2.083-7.886c0 .582.082 1.093.247 1.531.168.438.409.78.722 1.027.316.243.7.364 1.15.364.47 0 .862-.125 1.18-.376.316-.254.555-.6.715-1.038.165-.442.247-.945.247-1.508 0-.56-.08-1.057-.24-1.491-.161-.434-.4-.775-.717-1.021-.317-.246-.712-.37-1.185-.37-.454 0-.84.12-1.156.358-.317.239-.558.573-.722 1.004-.16.43-.24.937-.24 1.52Zm15.039-2.125-1.937.211a1.555 1.555 0 0 0-.287-.551 1.426 1.426 0 0 0-.54-.417c-.227-.105-.505-.158-.833-.158-.442 0-.814.096-1.115.287-.298.192-.444.44-.44.746a.78.78 0 0 0 .287.64c.2.164.528.299.986.404l1.538.329c.852.184 1.486.475 1.9.874.42.4.63.921.635 1.567a2.451 2.451 0 0 1-.5 1.502c-.324.43-.776.767-1.355 1.01-.579.242-1.244.363-1.995.363-1.103 0-1.991-.23-2.664-.692-.673-.466-1.074-1.113-1.203-1.942l2.071-.2c.094.407.294.714.599.921.305.208.702.311 1.191.311.505 0 .91-.103 1.215-.31.309-.208.464-.464.464-.77a.8.8 0 0 0-.3-.639c-.195-.168-.5-.297-.915-.387l-1.538-.323c-.864-.18-1.504-.483-1.919-.91-.414-.43-.62-.974-.616-1.631-.004-.556.147-1.037.452-1.444.31-.41.737-.727 1.285-.95.552-.227 1.188-.34 1.907-.34 1.057 0 1.888.224 2.494.674.61.45.988 1.058 1.133 1.825ZM59.612 9.3v1.643H54.43V9.3h5.182Zm-3.903-2.16h2.125v8.462c0 .286.043.505.129.658.09.148.207.25.352.305.145.054.305.082.481.082.133 0 .255-.01.364-.03.114-.019.2-.037.258-.052l.358 1.66a4.76 4.76 0 0 1-.487.13 3.991 3.991 0 0 1-.763.082 3.463 3.463 0 0 1-1.438-.24 2.237 2.237 0 0 1-1.015-.834c-.246-.376-.367-.845-.364-1.409V7.14Zm7.943 11.355a3.76 3.76 0 0 1-1.544-.305 2.519 2.519 0 0 1-1.08-.915c-.261-.403-.392-.9-.392-1.49 0-.51.093-.93.281-1.262a2.18 2.18 0 0 1 .769-.799c.325-.2.69-.35 1.097-.451.411-.106.836-.182 1.274-.23.528-.054.956-.103 1.285-.146.329-.047.567-.117.716-.211.152-.098.229-.249.229-.452v-.035c0-.442-.131-.785-.393-1.027-.263-.243-.64-.364-1.133-.364-.52 0-.933.113-1.238.34-.302.227-.505.495-.61.804l-1.984-.281a3.166 3.166 0 0 1 .775-1.374c.36-.371.8-.65 1.32-.833a5.05 5.05 0 0 1 1.725-.282c.434 0 .867.051 1.297.153.43.102.824.27 1.18.505.356.23.641.545.856.944.22.4.33.898.33 1.497v6.033h-2.043v-1.239h-.07c-.13.25-.312.485-.546.704-.231.216-.522.39-.875.523-.348.129-.757.193-1.226.193Zm.552-1.56c.426 0 .796-.085 1.109-.253.313-.172.553-.399.721-.68.173-.282.259-.59.259-.922v-1.062a.997.997 0 0 1-.34.152 4.827 4.827 0 0 1-.529.124c-.196.035-.39.066-.58.093l-.5.071c-.317.043-.6.113-.85.211-.251.098-.449.235-.593.411-.145.172-.217.395-.217.669 0 .391.142.687.428.886.286.2.65.3 1.092.3Zm13.137-5.253-1.937.211a1.557 1.557 0 0 0-.287-.551 1.428 1.428 0 0 0-.54-.417c-.227-.105-.505-.158-.833-.158-.442 0-.814.096-1.115.287-.298.192-.445.44-.44.746a.78.78 0 0 0 .287.64c.2.164.528.299.986.404l1.537.329c.853.184 1.487.475 1.902.874.418.4.63.921.633 1.567a2.451 2.451 0 0 1-.498 1.502c-.325.43-.777.767-1.356 1.01-.579.242-1.244.363-1.995.363-1.103 0-1.991-.23-2.664-.692-.673-.466-1.074-1.113-1.203-1.942l2.071-.2c.094.407.294.714.599.921.305.208.702.311 1.191.311.505 0 .91-.103 1.215-.31.309-.208.463-.464.463-.77a.8.8 0 0 0-.299-.639c-.196-.168-.5-.297-.915-.387l-1.538-.323c-.865-.18-1.504-.483-1.919-.91-.415-.43-.62-.974-.616-1.631-.004-.556.147-1.037.452-1.444.309-.41.737-.727 1.285-.95.552-.227 1.187-.34 1.907-.34 1.056 0 1.888.224 2.494.674.61.45.988 1.058 1.133 1.825Zm3.74 1.35v5.282h-2.125V6.295h2.077v4.536h.106c.211-.508.538-.91.98-1.203.446-.297 1.013-.446 1.702-.446.626 0 1.171.131 1.637.393.465.263.825.646 1.08 1.15.258.505.387 1.122.387 1.85v5.739H84.8v-5.411c0-.607-.157-1.078-.47-1.414-.309-.34-.743-.511-1.303-.511-.375 0-.712.082-1.009.247-.293.16-.524.393-.693.698-.164.305-.246.675-.246 1.109Z"
|
|
21
|
+
fill="#050505"/>
|
|
22
|
+
<defs>
|
|
23
|
+
<clipPath id="a">
|
|
24
|
+
<path fill="#fff" d="M0 0h19.5v26H0z"/>
|
|
25
|
+
</clipPath>
|
|
26
|
+
</defs>
|
|
27
|
+
</svg>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
body {
|
|
7
|
+
@apply antialiased text-gray-900;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
a {
|
|
11
|
+
@apply transition;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pre {
|
|
15
|
+
@apply bg-gray-800 text-gray-50 p-6 rounded-lg text-sm;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@layer components {
|
|
20
|
+
button {
|
|
21
|
+
@apply flex
|
|
22
|
+
items-center
|
|
23
|
+
px-4
|
|
24
|
+
py-2
|
|
25
|
+
font-semibold
|
|
26
|
+
rounded
|
|
27
|
+
bg-emerald-500
|
|
28
|
+
text-white
|
|
29
|
+
hover:bg-emerald-600
|
|
30
|
+
focus:outline-none
|
|
31
|
+
focus:ring-2
|
|
32
|
+
focus:ring-primary-200
|
|
33
|
+
focus:ring-offset-2;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[type="text"],
|
|
37
|
+
[type="email"],
|
|
38
|
+
[type="url"],
|
|
39
|
+
[type="password"],
|
|
40
|
+
[type="number"],
|
|
41
|
+
[type="date"],
|
|
42
|
+
[type="datetime-local"],
|
|
43
|
+
[type="month"],
|
|
44
|
+
[type="search"],
|
|
45
|
+
[type="tel"],
|
|
46
|
+
[type="time"],
|
|
47
|
+
[type="week"],
|
|
48
|
+
textarea,
|
|
49
|
+
select {
|
|
50
|
+
@apply rounded
|
|
51
|
+
shadow-sm
|
|
52
|
+
border-gray-300
|
|
53
|
+
focus:ring
|
|
54
|
+
focus:ring-primary-200
|
|
55
|
+
focus:ring-opacity-50
|
|
56
|
+
focus:border-primary-300;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[type="checkbox"],
|
|
60
|
+
[type="radio"] {
|
|
61
|
+
@apply w-5
|
|
62
|
+
h-5
|
|
63
|
+
text-primary-500
|
|
64
|
+
border-gray-300
|
|
65
|
+
focus:ring
|
|
66
|
+
focus:ring-offset-0
|
|
67
|
+
focus:ring-primary-200
|
|
68
|
+
focus:ring-opacity-50
|
|
69
|
+
focus:border-primary-300;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
[type="checkbox"] {
|
|
73
|
+
@apply rounded
|
|
74
|
+
shadow-sm;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const colors = require("tailwindcss/colors");
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
|
|
5
|
+
theme: {
|
|
6
|
+
extend: {
|
|
7
|
+
colors: {
|
|
8
|
+
gray: colors.zinc,
|
|
9
|
+
primary: colors.emerald,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
plugins: [
|
|
14
|
+
require("@tailwindcss/forms")({
|
|
15
|
+
strategy: "base", // only generate global styles
|
|
16
|
+
// strategy: "class", // only generate classes
|
|
17
|
+
}),
|
|
18
|
+
],
|
|
19
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es5",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"forceConsistentCasingInFileNames": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"module": "esnext",
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"jsx": "preserve",
|
|
16
|
+
"incremental": true,
|
|
17
|
+
"baseUrl": "."
|
|
18
|
+
},
|
|
19
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
16
|
+
|
|
17
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
18
|
+
|
|
19
|
+
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
|
20
|
+
|
|
21
|
+
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
|
22
|
+
|
|
23
|
+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
|
24
|
+
|
|
25
|
+
## Learn More
|
|
26
|
+
|
|
27
|
+
To learn more about Next.js, take a look at the following resources:
|
|
28
|
+
|
|
29
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
30
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
31
|
+
|
|
32
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
|
33
|
+
|
|
34
|
+
## Deploy on Vercel
|
|
35
|
+
|
|
36
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
37
|
+
|
|
38
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
|
Binary file
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--max-width: 1100px;
|
|
3
|
+
--border-radius: 12px;
|
|
4
|
+
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
|
|
5
|
+
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
|
|
6
|
+
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
|
|
7
|
+
|
|
8
|
+
--foreground-rgb: 0, 0, 0;
|
|
9
|
+
--background-start-rgb: 214, 219, 220;
|
|
10
|
+
--background-end-rgb: 255, 255, 255;
|
|
11
|
+
|
|
12
|
+
--primary-glow: conic-gradient(
|
|
13
|
+
from 180deg at 50% 50%,
|
|
14
|
+
#16abff33 0deg,
|
|
15
|
+
#0885ff33 55deg,
|
|
16
|
+
#54d6ff33 120deg,
|
|
17
|
+
#0071ff33 160deg,
|
|
18
|
+
transparent 360deg
|
|
19
|
+
);
|
|
20
|
+
--secondary-glow: radial-gradient(
|
|
21
|
+
rgba(255, 255, 255, 1),
|
|
22
|
+
rgba(255, 255, 255, 0)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
--tile-start-rgb: 239, 245, 249;
|
|
26
|
+
--tile-end-rgb: 228, 232, 233;
|
|
27
|
+
--tile-border: conic-gradient(
|
|
28
|
+
#00000080,
|
|
29
|
+
#00000040,
|
|
30
|
+
#00000030,
|
|
31
|
+
#00000020,
|
|
32
|
+
#00000010,
|
|
33
|
+
#00000010,
|
|
34
|
+
#00000080
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
--callout-rgb: 238, 240, 241;
|
|
38
|
+
--callout-border-rgb: 172, 175, 176;
|
|
39
|
+
--card-rgb: 180, 185, 188;
|
|
40
|
+
--card-border-rgb: 131, 134, 135;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (prefers-color-scheme: dark) {
|
|
44
|
+
:root {
|
|
45
|
+
--foreground-rgb: 255, 255, 255;
|
|
46
|
+
--background-start-rgb: 0, 0, 0;
|
|
47
|
+
--background-end-rgb: 0, 0, 0;
|
|
48
|
+
|
|
49
|
+
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
|
50
|
+
--secondary-glow: linear-gradient(
|
|
51
|
+
to bottom right,
|
|
52
|
+
rgba(1, 65, 255, 0),
|
|
53
|
+
rgba(1, 65, 255, 0),
|
|
54
|
+
rgba(1, 65, 255, 0.3)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
--tile-start-rgb: 2, 13, 46;
|
|
58
|
+
--tile-end-rgb: 2, 5, 19;
|
|
59
|
+
--tile-border: conic-gradient(
|
|
60
|
+
#ffffff80,
|
|
61
|
+
#ffffff40,
|
|
62
|
+
#ffffff30,
|
|
63
|
+
#ffffff20,
|
|
64
|
+
#ffffff10,
|
|
65
|
+
#ffffff10,
|
|
66
|
+
#ffffff80
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
--callout-rgb: 20, 20, 20;
|
|
70
|
+
--callout-border-rgb: 108, 108, 108;
|
|
71
|
+
--card-rgb: 100, 100, 100;
|
|
72
|
+
--card-border-rgb: 200, 200, 200;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
* {
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
padding: 0;
|
|
79
|
+
margin: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
html,
|
|
83
|
+
body {
|
|
84
|
+
max-width: 100vw;
|
|
85
|
+
overflow-x: hidden;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
body {
|
|
89
|
+
color: rgb(var(--foreground-rgb));
|
|
90
|
+
background: linear-gradient(
|
|
91
|
+
to bottom,
|
|
92
|
+
transparent,
|
|
93
|
+
rgb(var(--background-end-rgb))
|
|
94
|
+
)
|
|
95
|
+
rgb(var(--background-start-rgb));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
a {
|
|
99
|
+
color: inherit;
|
|
100
|
+
text-decoration: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (prefers-color-scheme: dark) {
|
|
104
|
+
html {
|
|
105
|
+
color-scheme: dark;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "./globals.css";
|
|
2
|
+
|
|
3
|
+
export const metadata = {
|
|
4
|
+
title: "Create Next App",
|
|
5
|
+
description: "Generated by create next app",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default function RootLayout({
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<html lang="en">
|
|
15
|
+
<body>{children}</body>
|
|
16
|
+
</html>
|
|
17
|
+
);
|
|
18
|
+
}
|