mirror of
https://github.com/AderKonstantin/aderktech-chronark.com-.git
synced 2025-06-08 13:48:42 +03:00
fix: og text
This commit is contained in:
parent
8f14ec4fbe
commit
68228c44fc
@ -1,2 +1,2 @@
|
|||||||
UPSTASH_REDIS_REST_URL=
|
UPSTASH_REDIS_REST_URL=
|
||||||
UPSTASH_REDIS_REST_TOKEN=
|
UPSTASH_REDIS_REST_TOKEN=
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://chronark.com"><h1 align="center">chronark.com</h1></a>
|
<a href="https://chronark.com"><h1 align="center">chronark.com</h1></a>
|
||||||
|
|
||||||
My personal website, built with [Next.js](https://nextjs.org/), [Tailwind CSS](https://tailwindcss.com/), [Upstash](https://upstash.com?ref=chronark.com), [Contentlayer](https://www.contentlayer.dev/) and deployed to [Vercel](https://vercel.com/).
|
My personal website, built with [Next.js](https://nextjs.org/), [Tailwind CSS](https://tailwindcss.com/), [Upstash](https://upstash.com?ref=chronark.com), [Contentlayer](https://www.contentlayer.dev/) and deployed to [Vercel](https://vercel.com/).
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -30,4 +30,4 @@ pnpm dev
|
|||||||
|
|
||||||
## Cloning / Forking
|
## Cloning / Forking
|
||||||
|
|
||||||
Please remove all of my personal information (projects, images, etc.) before deploying your own version of this site.
|
Please remove all of my personal information (projects, images, etc.) before deploying your own version of this site.
|
||||||
|
117
app/layout.tsx
117
app/layout.tsx
@ -5,73 +5,72 @@ import { Metadata } from "next";
|
|||||||
import { Analytics } from "./components/analytics";
|
import { Analytics } from "./components/analytics";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
default: "chronark.com",
|
default: "chronark.com",
|
||||||
template: "%s | chronark.com",
|
template: "%s | chronark.com",
|
||||||
},
|
},
|
||||||
description: "Software engineer at upstash.com and founder of planetfall.io",
|
description: "Co-founder of unkey.dev and founder of planetfall.io",
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "chronark.com",
|
title: "chronark.com",
|
||||||
description:
|
description:
|
||||||
"Software engineer at upstash.com and founder of planetfall.io",
|
"Co-founder of unkey.dev and founder of planetfall.io",
|
||||||
url: "https://chronark.com",
|
url: "https://chronark.com",
|
||||||
siteName: "chronark.com",
|
siteName: "chronark.com",
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: "https://chronark.com/og.png",
|
url: "https://chronark.com/og.png",
|
||||||
width: 1920,
|
width: 1920,
|
||||||
height: 1080,
|
height: 1080,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
locale: "en-US",
|
locale: "en-US",
|
||||||
type: "website",
|
type: "website",
|
||||||
},
|
},
|
||||||
robots: {
|
robots: {
|
||||||
index: true,
|
index: true,
|
||||||
follow: true,
|
follow: true,
|
||||||
googleBot: {
|
googleBot: {
|
||||||
index: true,
|
index: true,
|
||||||
follow: true,
|
follow: true,
|
||||||
"max-video-preview": -1,
|
"max-video-preview": -1,
|
||||||
"max-image-preview": "large",
|
"max-image-preview": "large",
|
||||||
"max-snippet": -1,
|
"max-snippet": -1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: "Chronark",
|
title: "Chronark",
|
||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
},
|
},
|
||||||
icons: {
|
icons: {
|
||||||
shortcut: "/favicon.png",
|
shortcut: "/favicon.png",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
variable: "--font-inter",
|
variable: "--font-inter",
|
||||||
});
|
});
|
||||||
|
|
||||||
const calSans = LocalFont({
|
const calSans = LocalFont({
|
||||||
src: "../public/fonts/CalSans-SemiBold.ttf",
|
src: "../public/fonts/CalSans-SemiBold.ttf",
|
||||||
variable: "--font-calsans",
|
variable: "--font-calsans",
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={[inter.variable, calSans.variable].join(" ")}>
|
<html lang="en" className={[inter.variable, calSans.variable].join(" ")}>
|
||||||
<head>
|
<head>
|
||||||
<Analytics />
|
<Analytics />
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
className={`bg-black ${
|
className={`bg-black ${process.env.NODE_ENV === "development" ? "debug-screens" : undefined
|
||||||
process.env.NODE_ENV === "development" ? "debug-screens" : undefined
|
}`}
|
||||||
}`}
|
>
|
||||||
>
|
{children}
|
||||||
{children}
|
</body>
|
||||||
</body>
|
</html>
|
||||||
</html>
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -9,40 +9,40 @@ import { Redis } from "@upstash/redis";
|
|||||||
export const revalidate = 60;
|
export const revalidate = 60;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
params: {
|
params: {
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const redis = Redis.fromEnv();
|
const redis = Redis.fromEnv();
|
||||||
|
|
||||||
export async function generateStaticParams(): Promise<Props["params"][]> {
|
export async function generateStaticParams(): Promise<Props["params"][]> {
|
||||||
return allProjects
|
return allProjects
|
||||||
.filter((p) => p.published)
|
.filter((p) => p.published)
|
||||||
.map((p) => ({
|
.map((p) => ({
|
||||||
slug: p.slug,
|
slug: p.slug,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function PostPage({ params }: Props) {
|
export default async function PostPage({ params }: Props) {
|
||||||
const slug = params?.slug;
|
const slug = params?.slug;
|
||||||
const project = allProjects.find((project) => project.slug === slug);
|
const project = allProjects.find((project) => project.slug === slug);
|
||||||
|
|
||||||
if (!project) {
|
if (!project) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const views =
|
const views =
|
||||||
(await redis.get<number>(["pageviews", "projects", slug].join(":"))) ?? 0;
|
(await redis.get<number>(["pageviews", "projects", slug].join(":"))) ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-zinc-50 min-h-screen">
|
<div className="bg-zinc-50 min-h-screen">
|
||||||
<Header project={project} views={views} />
|
<Header project={project} views={views} />
|
||||||
<ReportView slug={project.slug} />
|
<ReportView slug={project.slug} />
|
||||||
|
|
||||||
<article className="px-4 py-12 mx-auto prose prose-zinc prose-quoteless">
|
<article className="px-4 py-12 mx-auto prose prose-zinc prose-quoteless">
|
||||||
<Mdx code={project.body.code} />
|
<Mdx code={project.body.code} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,128 +11,128 @@ const redis = Redis.fromEnv();
|
|||||||
|
|
||||||
export const revalidate = 60;
|
export const revalidate = 60;
|
||||||
export default async function ProjectsPage() {
|
export default async function ProjectsPage() {
|
||||||
const views = (
|
const views = (
|
||||||
await redis.mget<number[]>(
|
await redis.mget<number[]>(
|
||||||
...allProjects.map((p) => ["pageviews", "projects", p.slug].join(":")),
|
...allProjects.map((p) => ["pageviews", "projects", p.slug].join(":")),
|
||||||
)
|
)
|
||||||
).reduce((acc, v, i) => {
|
).reduce((acc, v, i) => {
|
||||||
acc[allProjects[i].slug] = v ?? 0;
|
acc[allProjects[i].slug] = v ?? 0;
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>);
|
}, {} as Record<string, number>);
|
||||||
|
|
||||||
const featured = allProjects.find((project) => project.slug === "unkey")!;
|
const featured = allProjects.find((project) => project.slug === "unkey")!;
|
||||||
const top2 = allProjects.find((project) => project.slug === "planetfall")!;
|
const top2 = allProjects.find((project) => project.slug === "planetfall")!;
|
||||||
const top3 = allProjects.find((project) => project.slug === "highstorm")!;
|
const top3 = allProjects.find((project) => project.slug === "highstorm")!;
|
||||||
const sorted = allProjects
|
const sorted = allProjects
|
||||||
.filter((p) => p.published)
|
.filter((p) => p.published)
|
||||||
.filter(
|
.filter(
|
||||||
(project) =>
|
(project) =>
|
||||||
project.slug !== featured.slug &&
|
project.slug !== featured.slug &&
|
||||||
project.slug !== top2.slug &&
|
project.slug !== top2.slug &&
|
||||||
project.slug !== top3.slug,
|
project.slug !== top3.slug,
|
||||||
)
|
)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
new Date(b.date ?? Number.POSITIVE_INFINITY).getTime() -
|
new Date(b.date ?? Number.POSITIVE_INFINITY).getTime() -
|
||||||
new Date(a.date ?? Number.POSITIVE_INFINITY).getTime(),
|
new Date(a.date ?? Number.POSITIVE_INFINITY).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative pb-16">
|
<div className="relative pb-16">
|
||||||
<Navigation />
|
<Navigation />
|
||||||
<div className="px-6 pt-20 mx-auto space-y-8 max-w-7xl lg:px-8 md:space-y-16 md:pt-24 lg:pt-32">
|
<div className="px-6 pt-20 mx-auto space-y-8 max-w-7xl lg:px-8 md:space-y-16 md:pt-24 lg:pt-32">
|
||||||
<div className="max-w-2xl mx-auto lg:mx-0">
|
<div className="max-w-2xl mx-auto lg:mx-0">
|
||||||
<h2 className="text-3xl font-bold tracking-tight text-zinc-100 sm:text-4xl">
|
<h2 className="text-3xl font-bold tracking-tight text-zinc-100 sm:text-4xl">
|
||||||
Projects
|
Projects
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 text-zinc-400">
|
<p className="mt-4 text-zinc-400">
|
||||||
Some of the projects are from work and some are on my own time.
|
Some of the projects are from work and some are on my own time.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-px bg-zinc-800" />
|
<div className="w-full h-px bg-zinc-800" />
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-8 mx-auto lg:grid-cols-2 ">
|
<div className="grid grid-cols-1 gap-8 mx-auto lg:grid-cols-2 ">
|
||||||
<Card>
|
<Card>
|
||||||
<Link href={`/projects/${featured.slug}`}>
|
<Link href={`/projects/${featured.slug}`}>
|
||||||
<article className="relative w-full h-full p-4 md:p-8">
|
<article className="relative w-full h-full p-4 md:p-8">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="text-xs text-zinc-100">
|
<div className="text-xs text-zinc-100">
|
||||||
{featured.date ? (
|
{featured.date ? (
|
||||||
<time dateTime={new Date(featured.date).toISOString()}>
|
<time dateTime={new Date(featured.date).toISOString()}>
|
||||||
{Intl.DateTimeFormat(undefined, {
|
{Intl.DateTimeFormat(undefined, {
|
||||||
dateStyle: "medium",
|
dateStyle: "medium",
|
||||||
}).format(new Date(featured.date))}
|
}).format(new Date(featured.date))}
|
||||||
</time>
|
</time>
|
||||||
) : (
|
) : (
|
||||||
<span>SOON</span>
|
<span>SOON</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="flex items-center gap-1 text-xs text-zinc-500">
|
<span className="flex items-center gap-1 text-xs text-zinc-500">
|
||||||
<Eye className="w-4 h-4" />{" "}
|
<Eye className="w-4 h-4" />{" "}
|
||||||
{Intl.NumberFormat("en-US", { notation: "compact" }).format(
|
{Intl.NumberFormat("en-US", { notation: "compact" }).format(
|
||||||
views[featured.slug] ?? 0,
|
views[featured.slug] ?? 0,
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2
|
<h2
|
||||||
id="featured-post"
|
id="featured-post"
|
||||||
className="mt-4 text-3xl font-bold text-zinc-100 group-hover:text-white sm:text-4xl font-display"
|
className="mt-4 text-3xl font-bold text-zinc-100 group-hover:text-white sm:text-4xl font-display"
|
||||||
>
|
>
|
||||||
{featured.title}
|
{featured.title}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 leading-8 duration-150 text-zinc-400 group-hover:text-zinc-300">
|
<p className="mt-4 leading-8 duration-150 text-zinc-400 group-hover:text-zinc-300">
|
||||||
{featured.description}
|
{featured.description}
|
||||||
</p>
|
</p>
|
||||||
<div className="absolute bottom-4 md:bottom-8">
|
<div className="absolute bottom-4 md:bottom-8">
|
||||||
<p className="hidden text-zinc-200 hover:text-zinc-50 lg:block">
|
<p className="hidden text-zinc-200 hover:text-zinc-50 lg:block">
|
||||||
Read more <span aria-hidden="true">→</span>
|
Read more <span aria-hidden="true">→</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</Link>
|
</Link>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div className="flex flex-col w-full gap-8 mx-auto border-t border-gray-900/10 lg:mx-0 lg:border-t-0 ">
|
<div className="flex flex-col w-full gap-8 mx-auto border-t border-gray-900/10 lg:mx-0 lg:border-t-0 ">
|
||||||
{[top2, top3].map((project) => (
|
{[top2, top3].map((project) => (
|
||||||
<Card key={project.slug}>
|
<Card key={project.slug}>
|
||||||
<Article project={project} views={views[project.slug] ?? 0} />
|
<Article project={project} views={views[project.slug] ?? 0} />
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden w-full h-px md:block bg-zinc-800" />
|
<div className="hidden w-full h-px md:block bg-zinc-800" />
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4 mx-auto lg:mx-0 md:grid-cols-3">
|
<div className="grid grid-cols-1 gap-4 mx-auto lg:mx-0 md:grid-cols-3">
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
{sorted
|
{sorted
|
||||||
.filter((_, i) => i % 3 === 0)
|
.filter((_, i) => i % 3 === 0)
|
||||||
.map((project) => (
|
.map((project) => (
|
||||||
<Card key={project.slug}>
|
<Card key={project.slug}>
|
||||||
<Article project={project} views={views[project.slug] ?? 0} />
|
<Article project={project} views={views[project.slug] ?? 0} />
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
{sorted
|
{sorted
|
||||||
.filter((_, i) => i % 3 === 1)
|
.filter((_, i) => i % 3 === 1)
|
||||||
.map((project) => (
|
.map((project) => (
|
||||||
<Card key={project.slug}>
|
<Card key={project.slug}>
|
||||||
<Article project={project} views={views[project.slug] ?? 0} />
|
<Article project={project} views={views[project.slug] ?? 0} />
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
{sorted
|
{sorted
|
||||||
.filter((_, i) => i % 3 === 2)
|
.filter((_, i) => i % 3 === 2)
|
||||||
.map((project) => (
|
.map((project) => (
|
||||||
<Card key={project.slug}>
|
<Card key={project.slug}>
|
||||||
<Article project={project} views={views[project.slug] ?? 0} />
|
<Article project={project} views={views[project.slug] ?? 0} />
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -113,4 +113,4 @@ $ curl -s https://envshare.dev/api/v1/secret/HdPbXgpvUvNk43oxSdK97u
|
|||||||
"remainingReads": 1
|
"remainingReads": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -4,7 +4,7 @@ description: QStash is a fully managed serverless queue and messaging service de
|
|||||||
date: "2022-07-18"
|
date: "2022-07-18"
|
||||||
url: https://upstash.com/qstash
|
url: https://upstash.com/qstash
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
QStash is an HTTP based messaging and scheduling solution for the serverless and edge runtimes.
|
QStash is an HTTP based messaging and scheduling solution for the serverless and edge runtimes.
|
||||||
@ -33,4 +33,4 @@ If you use QStash in the above example, you simply send a request to QStash from
|
|||||||
With QStash, you can add delays to the requests. Send an email 3 days after the shipment to remind the customer to add a review. You can also schedule tasks. You can send the requests with a CRON expression, so it will be run repetitively.
|
With QStash, you can add delays to the requests. Send an email 3 days after the shipment to remind the customer to add a review. You can also schedule tasks. You can send the requests with a CRON expression, so it will be run repetitively.
|
||||||
|
|
||||||
|
|
||||||
To learn more about QStash, visit [upstash.com/qstash](upstash.com/qstash).
|
To learn more about QStash, visit [upstash.com/qstash](upstash.com/qstash).
|
||||||
|
@ -5,4 +5,4 @@ url: https://console.upstash.com/ratelimit
|
|||||||
repository: upstash/auth-analytics
|
repository: upstash/auth-analytics
|
||||||
---
|
---
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
@ -4,7 +4,7 @@ description: A CLI to provision and manage Upstash resources, including Redis an
|
|||||||
date: "2022-05-16"
|
date: "2022-05-16"
|
||||||
repository: upstash/cli
|
repository: upstash/cli
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ for windows, linux and mac (both intel and m1).
|
|||||||
```bash
|
```bash
|
||||||
> upstash
|
> upstash
|
||||||
|
|
||||||
Usage: upstash
|
Usage: upstash
|
||||||
Version: development
|
Version: development
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
@ -47,15 +47,15 @@ for windows, linux and mac (both intel and m1).
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
-h, --help - Show this help.
|
-h, --help - Show this help.
|
||||||
-V, --version - Show the version number for this program.
|
-V, --version - Show the version number for this program.
|
||||||
-c, --config <string> - Path to .upstash.json file
|
-c, --config <string> - Path to .upstash.json file
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
|
|
||||||
auth - Login and logout
|
auth - Login and logout
|
||||||
redis - Manage redis database instances
|
redis - Manage redis database instances
|
||||||
kafka - Manage kafka clusters and topics
|
kafka - Manage kafka clusters and topics
|
||||||
team - Manage your teams and their members
|
team - Manage your teams and their members
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
|
@ -5,7 +5,7 @@ date: "2023-02-13"
|
|||||||
url: https://console.upstash.com/ratelimit
|
url: https://console.upstash.com/ratelimit
|
||||||
repository: upstash/core-analytics
|
repository: upstash/core-analytics
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ date: "2022-12-12"
|
|||||||
url: https://upstash.com/blog/edge-flags-beta
|
url: https://upstash.com/blog/edge-flags-beta
|
||||||
repository: upstash/edge-flags
|
repository: upstash/edge-flags
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ Whether you want to ship without breaking things, run A/B tests or just want to
|
|||||||
feature flags are a great way to dynamically change the behaviour of your app without redeploying. We're excited to announce the public
|
feature flags are a great way to dynamically change the behaviour of your app without redeploying. We're excited to announce the public
|
||||||
beta release of our new feature flagging library: [@upstash/edge-flags](https://github.com/upstash/edge-flags).
|
beta release of our new feature flagging library: [@upstash/edge-flags](https://github.com/upstash/edge-flags).
|
||||||
|
|
||||||
*Edge Flags*, as the name implies, is a feature flag solution built to run at the edge. It is using [Upstash Redis](https://upstash.com/), a globally replicated serverless Redis service, to store configuration and is
|
*Edge Flags*, as the name implies, is a feature flag solution built to run at the edge. It is using [Upstash Redis](https://upstash.com/), a globally replicated serverless Redis service, to store configuration and is
|
||||||
designed to work with [Next.js](https://nextjs.org) and [Vercel](https://vercel.com). We want to support other frameworks in the future, so if you have a suggestion, please let us know!
|
designed to work with [Next.js](https://nextjs.org) and [Vercel](https://vercel.com). We want to support other frameworks in the future, so if you have a suggestion, please let us know!
|
||||||
|
|
||||||
With the ability to toggle features on and off at the edge, you can quickly respond
|
With the ability to toggle features on and off at the edge, you can quickly respond
|
||||||
|
@ -5,7 +5,7 @@ date: "2022-01-08"
|
|||||||
url: https://upstash.com/kafka
|
url: https://upstash.com/kafka
|
||||||
repository: upstash/upstash-kafka
|
repository: upstash/upstash-kafka
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ description: Near realtime analytics for your ratelimits. Integrated into the @u
|
|||||||
repository: upstash/ratelimit"
|
repository: upstash/ratelimit"
|
||||||
url: https://console.upstash.com/ratelimit
|
url: https://console.upstash.com/ratelimit
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
@ -5,7 +5,7 @@ date: "2022-06-06"
|
|||||||
url: https://upstash.com/blog/upstash-ratelimit
|
url: https://upstash.com/blog/upstash-ratelimit
|
||||||
repository: upstash/ratelimit
|
repository: upstash/ratelimit
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
In today's digital age, serverless computing has become increasingly popular due to its scalability and cost-efficiency. One of the challenges of serverless computing is to manage resources efficiently, and one critical aspect of this is rate limiting. Rate limiting is a technique that limits the number of requests a client can make to a server over a given period. This technique can prevent abuse, improve performance, and reduce costs. One npm package that helps implement rate limiting for serverless applications is @upstash/ratelimit, built on top of Upstash Redis.
|
In today's digital age, serverless computing has become increasingly popular due to its scalability and cost-efficiency. One of the challenges of serverless computing is to manage resources efficiently, and one critical aspect of this is rate limiting. Rate limiting is a technique that limits the number of requests a client can make to a server over a given period. This technique can prevent abuse, improve performance, and reduce costs. One npm package that helps implement rate limiting for serverless applications is @upstash/ratelimit, built on top of Upstash Redis.
|
||||||
@ -46,4 +46,4 @@ const { success } = await ratelimit.limit("identifier")
|
|||||||
|
|
||||||
In the code above, we initialize Upstash with our Upstash Redis credentials and define our rate limiting rules. We then call the `limit` function, passing the identifier. The function returns a Promise that resolves with `success` and some other useful data.
|
In the code above, we initialize Upstash with our Upstash Redis credentials and define our rate limiting rules. We then call the `limit` function, passing the identifier. The function returns a Promise that resolves with `success` and some other useful data.
|
||||||
|
|
||||||
`@upstash/ratelimit` is a useful npm package for serverless rate limiting that simplifies the process of implementing rate limiting for serverless applications. The package is built on top of Upstash Redis, which provides a complete solution for serverless applications. With `@upstash/ratelimit`, serverless developers can easily implement rate limiting, which can help prevent abuse, improve performance, and reduce costs.
|
`@upstash/ratelimit` is a useful npm package for serverless rate limiting that simplifies the process of implementing rate limiting for serverless applications. The package is built on top of Upstash Redis, which provides a complete solution for serverless applications. With `@upstash/ratelimit`, serverless developers can easily implement rate limiting, which can help prevent abuse, improve performance, and reduce costs.
|
||||||
|
@ -5,8 +5,8 @@ date: "2023-02-05"
|
|||||||
url: https://upstash.com
|
url: https://upstash.com
|
||||||
repository: upstash/react-ui
|
repository: upstash/react-ui
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
`@upstash/react-ui` is powering the CLI in your browser on [console.upstash.com](https://console.upstash.com). It allows you to interact with your Upstash Redis database in a simple and intuitive way.
|
`@upstash/react-ui` is powering the CLI in your browser on [console.upstash.com](https://console.upstash.com). It allows you to interact with your Upstash Redis database in a simple and intuitive way.
|
||||||
|
@ -5,7 +5,7 @@ date: "2022-03-14"
|
|||||||
url: https://upstash.com/redis
|
url: https://upstash.com/redis
|
||||||
repository: upstash/upstash-redis
|
repository: upstash/upstash-redis
|
||||||
published: true
|
published: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Upstash is a cloud-based service provider that offers a Redis-compatible service. In addition to that, we have also created an npm package called `@upstash/redis`. This package provides a strongly typed Redis client that uses HTTP instead of TCP to communicate with the database, making it perfect for serverless and edge runtimes.
|
Upstash is a cloud-based service provider that offers a Redis-compatible service. In addition to that, we have also created an npm package called `@upstash/redis`. This package provides a strongly typed Redis client that uses HTTP instead of TCP to communicate with the database, making it perfect for serverless and edge runtimes.
|
||||||
@ -42,4 +42,4 @@ console.log(data)
|
|||||||
await redis.lpush('elements', 'magnesium')
|
await redis.lpush('elements', 'magnesium')
|
||||||
data = await redis.lrange('elements', 0, 100 )
|
data = await redis.lrange('elements', 0, 100 )
|
||||||
console.log(data)
|
console.log(data)
|
||||||
```
|
```
|
||||||
|
@ -5,4 +5,4 @@ description: A library to record and analyse web page traffic and user behaviour
|
|||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
|
|
||||||
Coming soon
|
Coming soon
|
||||||
|
@ -52,4 +52,4 @@
|
|||||||
"@opentelemetry/semantic-conventions": "1.13.0"
|
"@opentelemetry/semantic-conventions": "1.13.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,45 +3,45 @@ import { NextRequest, NextResponse } from "next/server";
|
|||||||
|
|
||||||
const redis = Redis.fromEnv();
|
const redis = Redis.fromEnv();
|
||||||
export const config = {
|
export const config = {
|
||||||
runtime: "edge",
|
runtime: "edge",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function incr(req: NextRequest): Promise<NextResponse> {
|
export default async function incr(req: NextRequest): Promise<NextResponse> {
|
||||||
if (req.method !== "POST") {
|
if (req.method !== "POST") {
|
||||||
return new NextResponse("use POST", { status: 405 });
|
return new NextResponse("use POST", { status: 405 });
|
||||||
}
|
}
|
||||||
if (req.headers.get("Content-Type") !== "application/json") {
|
if (req.headers.get("Content-Type") !== "application/json") {
|
||||||
return new NextResponse("must be json", { status: 400 });
|
return new NextResponse("must be json", { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
let slug: string | undefined = undefined;
|
let slug: string | undefined = undefined;
|
||||||
if ("slug" in body) {
|
if ("slug" in body) {
|
||||||
slug = body.slug;
|
slug = body.slug;
|
||||||
}
|
}
|
||||||
if (!slug) {
|
if (!slug) {
|
||||||
return new NextResponse("Slug not found", { status: 400 });
|
return new NextResponse("Slug not found", { status: 400 });
|
||||||
}
|
}
|
||||||
const ip = req.ip;
|
const ip = req.ip;
|
||||||
if (ip) {
|
if (ip) {
|
||||||
// Hash the IP in order to not store it directly in your db.
|
// Hash the IP in order to not store it directly in your db.
|
||||||
const buf = await crypto.subtle.digest(
|
const buf = await crypto.subtle.digest(
|
||||||
"SHA-256",
|
"SHA-256",
|
||||||
new TextEncoder().encode(ip),
|
new TextEncoder().encode(ip),
|
||||||
);
|
);
|
||||||
const hash = Array.from(new Uint8Array(buf))
|
const hash = Array.from(new Uint8Array(buf))
|
||||||
.map((b) => b.toString(16).padStart(2, "0"))
|
.map((b) => b.toString(16).padStart(2, "0"))
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
// deduplicate the ip for each slug
|
// deduplicate the ip for each slug
|
||||||
const isNew = await redis.set(["deduplicate", hash, slug].join(":"), true, {
|
const isNew = await redis.set(["deduplicate", hash, slug].join(":"), true, {
|
||||||
nx: true,
|
nx: true,
|
||||||
ex: 24 * 60 * 60,
|
ex: 24 * 60 * 60,
|
||||||
});
|
});
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
new NextResponse(null, { status: 202 });
|
new NextResponse(null, { status: 202 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await redis.incr(["pageviews", "projects", slug].join(":"));
|
await redis.incr(["pageviews", "projects", slug].join(":"));
|
||||||
return new NextResponse(null, { status: 202 });
|
return new NextResponse(null, { status: 202 });
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user