Compare commits

..

No commits in common. "181595f19541b0c01f5027ebe9d45cea0b413ed2" and "d5f36eababd10809db24c1744371a6deda7adbd4" have entirely different histories.

11 changed files with 2324 additions and 9976 deletions

3
.env
View File

@ -1,3 +0,0 @@
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD= # если используется

2
.env.example Normal file
View File

@ -0,0 +1,2 @@
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

View File

@ -32,13 +32,13 @@ export const Navigation: React.FC = () => {
href="/projects"
className="duration-200 text-zinc-400 hover:text-zinc-100"
>
Проекты
Projects
</Link>
<Link
href="/contact"
className="duration-200 text-zinc-400 hover:text-zinc-100"
>
Контакты
Contact
</Link>
</div>

View File

@ -3,8 +3,8 @@ import React from "react";
import Particles from "./components/particles";
const navigation = [
{ name: "Проекты", href: "/projects" },
{ name: "Контакты", href: "/contact" },
{ name: "Projects", href: "/projects" },
{ name: "Contact", href: "/contact" },
];
export default function Home() {
@ -29,7 +29,7 @@ export default function Home() {
quantity={100}
/>
<h1 className="py-3.5 px-0.5 z-10 text-4xl text-transparent duration-1000 bg-white cursor-default text-edge-outline animate-title font-display sm:text-6xl md:text-9xl whitespace-nowrap bg-clip-text ">
aderk.tech
chronark
</h1>
<div className="hidden w-screen h-px animate-glow md:block animate-fade-right bg-gradient-to-r from-zinc-300/0 via-zinc-300/50 to-zinc-300/0" />

View File

@ -4,7 +4,7 @@ import { Mdx } from "@/app/components/mdx";
import { Header } from "./header";
import "./mdx.css";
import { ReportView } from "./view";
import Redis from "ioredis";
import { Redis } from "@upstash/redis";
export const revalidate = 60;
@ -14,12 +14,7 @@ type Props = {
};
};
// Настройка подключения к локальному Redis
const redis = new Redis({
host: process.env.REDIS_HOST || "localhost",
port: parseInt(process.env.REDIS_PORT || "6379"),
password: process.env.REDIS_PASSWORD,
});
const redis = Redis.fromEnv();
export async function generateStaticParams(): Promise<Props["params"][]> {
return allProjects
@ -37,11 +32,8 @@ export default async function PostPage({ params }: Props) {
notFound();
}
// Получаем и преобразуем значение просмотров
const views = parseInt(
(await redis.get(`projects:${slug}:views`)) || "0",
10
);
const views =
(await redis.get<number>(["pageviews", "projects", slug].join(":"))) ?? 0;
return (
<div className="bg-zinc-50 min-h-screen">

View File

@ -4,24 +4,19 @@ import { allProjects } from "contentlayer/generated";
import { Navigation } from "../components/nav";
import { Card } from "../components/card";
import { Article } from "./article";
import Redis from "ioredis"; // Заменяем @upstash/redis на ioredis
import { Redis } from "@upstash/redis";
import { Eye } from "lucide-react";
// Создаем подключение к локальному Redis
const redis = new Redis({
host: process.env.REDIS_HOST || "localhost",
port: parseInt(process.env.REDIS_PORT || "6379"),
password: process.env.REDIS_PASSWORD, // если есть пароль
});
const redis = Redis.fromEnv();
export const revalidate = 60;
export default async function ProjectsPage() {
const viewsEntries = await redis.mget(
...allProjects.map((p) => `projects:${p.slug}:views`)
);
const views = allProjects.reduce((acc, project, index) => {
acc[project.slug] = parseInt(viewsEntries[index] as string) || 0;
const views = (
await redis.mget<number[]>(
...allProjects.map((p) => ["pageviews", "projects", p.slug].join(":")),
)
).reduce((acc, v, i) => {
acc[allProjects[i].slug] = v ?? 0;
return acc;
}, {} as Record<string, number>);

View File

@ -1,11 +0,0 @@
---
title: bitofgame.net
description: bitofgame.net is an open source API Key management solution. It allows you to create, manage and validate API Keys for your users.
date: "2025-07-01"
url: https://unkey.dev
published: true
repository: chronark/unkey
---
Unkey is an open source API Key management solution. It allows you to create, manage and validate API Keys for your users. Its built with security and speed in mind.

6976
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@
"@upstash/redis": "^1.23.3",
"contentlayer": "^0.3.4",
"framer-motion": "^10.16.4",
"ioredis": "^5.6.1",
"lucide-react": "^0.284.0",
"markdown-wasm": "^1.2.0",
"next": "^13.5.4",

View File

@ -1,15 +1,9 @@
import Redis from "ioredis";
import { Redis } from "@upstash/redis";
import { NextRequest, NextResponse } from "next/server";
// Настройка подключения к локальному Redis
const redis = new Redis({
host: process.env.REDIS_HOST || "localhost",
port: parseInt(process.env.REDIS_PORT || "6379"),
password: process.env.REDIS_PASSWORD,
});
const redis = Redis.fromEnv();
export const config = {
runtime: "edge", // Возможно потребуется изменить на "nodejs" если возникнут проблемы
runtime: "edge",
};
export default async function incr(req: NextRequest): Promise<NextResponse> {
@ -21,33 +15,33 @@ export default async function incr(req: NextRequest): Promise<NextResponse> {
}
const body = await req.json();
const slug = body.slug;
let slug: string | undefined = undefined;
if ("slug" in body) {
slug = body.slug;
}
if (!slug) {
return new NextResponse("Slug not found", { status: 400 });
}
const ip = req.ip;
if (ip) {
// Хеширование IP-адреса
// Hash the IP in order to not store it directly in your db.
const buf = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(ip)
new TextEncoder().encode(ip),
);
const hash = Array.from(new Uint8Array(buf))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
// Проверка уникальности посещения
const key = `deduplicate:${hash}:${slug}`;
const isNew = await redis.set(key, "1", "EX", 86400, "NX");
// deduplicate the ip for each slug
const isNew = await redis.set(["deduplicate", hash, slug].join(":"), true, {
nx: true,
ex: 24 * 60 * 60,
});
if (!isNew) {
return new NextResponse(null, { status: 202 });
new NextResponse(null, { status: 202 });
}
}
// Увеличиваем счетчик просмотров
await redis.incr(`projects:${slug}:views`);
await redis.incr(["pageviews", "projects", slug].join(":"));
return new NextResponse(null, { status: 202 });
}

5094
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff