mirror of
https://github.com/AderKonstantin/aderktech-chronark.com-.git
synced 2025-06-08 21:58:41 +03:00
Compare commits
3 Commits
d5f36eabab
...
181595f195
Author | SHA1 | Date | |
---|---|---|---|
|
181595f195 | ||
|
3c6b449280 | ||
|
139ac3356d |
3
.env
Normal file
3
.env
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
REDIS_HOST=localhost
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD= # если используется
|
@ -1,2 +0,0 @@
|
|||||||
UPSTASH_REDIS_REST_URL=
|
|
||||||
UPSTASH_REDIS_REST_TOKEN=
|
|
@ -32,13 +32,13 @@ export const Navigation: React.FC = () => {
|
|||||||
href="/projects"
|
href="/projects"
|
||||||
className="duration-200 text-zinc-400 hover:text-zinc-100"
|
className="duration-200 text-zinc-400 hover:text-zinc-100"
|
||||||
>
|
>
|
||||||
Projects
|
Проекты
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/contact"
|
href="/contact"
|
||||||
className="duration-200 text-zinc-400 hover:text-zinc-100"
|
className="duration-200 text-zinc-400 hover:text-zinc-100"
|
||||||
>
|
>
|
||||||
Contact
|
Контакты
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ import React from "react";
|
|||||||
import Particles from "./components/particles";
|
import Particles from "./components/particles";
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ name: "Projects", href: "/projects" },
|
{ name: "Проекты", href: "/projects" },
|
||||||
{ name: "Contact", href: "/contact" },
|
{ name: "Контакты", href: "/contact" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
@ -29,7 +29,7 @@ export default function Home() {
|
|||||||
quantity={100}
|
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 ">
|
<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 ">
|
||||||
chronark
|
aderk.tech
|
||||||
</h1>
|
</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" />
|
<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" />
|
||||||
|
@ -4,7 +4,7 @@ import { Mdx } from "@/app/components/mdx";
|
|||||||
import { Header } from "./header";
|
import { Header } from "./header";
|
||||||
import "./mdx.css";
|
import "./mdx.css";
|
||||||
import { ReportView } from "./view";
|
import { ReportView } from "./view";
|
||||||
import { Redis } from "@upstash/redis";
|
import Redis from "ioredis";
|
||||||
|
|
||||||
export const revalidate = 60;
|
export const revalidate = 60;
|
||||||
|
|
||||||
@ -14,7 +14,12 @@ type Props = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const redis = Redis.fromEnv();
|
// Настройка подключения к локальному Redis
|
||||||
|
const redis = new Redis({
|
||||||
|
host: process.env.REDIS_HOST || "localhost",
|
||||||
|
port: parseInt(process.env.REDIS_PORT || "6379"),
|
||||||
|
password: process.env.REDIS_PASSWORD,
|
||||||
|
});
|
||||||
|
|
||||||
export async function generateStaticParams(): Promise<Props["params"][]> {
|
export async function generateStaticParams(): Promise<Props["params"][]> {
|
||||||
return allProjects
|
return allProjects
|
||||||
@ -32,8 +37,11 @@ export default async function PostPage({ params }: Props) {
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const views =
|
// Получаем и преобразуем значение просмотров
|
||||||
(await redis.get<number>(["pageviews", "projects", slug].join(":"))) ?? 0;
|
const views = parseInt(
|
||||||
|
(await redis.get(`projects:${slug}:views`)) || "0",
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-zinc-50 min-h-screen">
|
<div className="bg-zinc-50 min-h-screen">
|
||||||
|
@ -4,19 +4,24 @@ import { allProjects } from "contentlayer/generated";
|
|||||||
import { Navigation } from "../components/nav";
|
import { Navigation } from "../components/nav";
|
||||||
import { Card } from "../components/card";
|
import { Card } from "../components/card";
|
||||||
import { Article } from "./article";
|
import { Article } from "./article";
|
||||||
import { Redis } from "@upstash/redis";
|
import Redis from "ioredis"; // Заменяем @upstash/redis на ioredis
|
||||||
import { Eye } from "lucide-react";
|
import { Eye } from "lucide-react";
|
||||||
|
|
||||||
const redis = Redis.fromEnv();
|
// Создаем подключение к локальному Redis
|
||||||
|
const redis = new Redis({
|
||||||
|
host: process.env.REDIS_HOST || "localhost",
|
||||||
|
port: parseInt(process.env.REDIS_PORT || "6379"),
|
||||||
|
password: process.env.REDIS_PASSWORD, // если есть пароль
|
||||||
|
});
|
||||||
|
|
||||||
export const revalidate = 60;
|
export const revalidate = 60;
|
||||||
export default async function ProjectsPage() {
|
export default async function ProjectsPage() {
|
||||||
const views = (
|
const viewsEntries = await redis.mget(
|
||||||
await redis.mget<number[]>(
|
...allProjects.map((p) => `projects:${p.slug}:views`)
|
||||||
...allProjects.map((p) => ["pageviews", "projects", p.slug].join(":")),
|
);
|
||||||
)
|
|
||||||
).reduce((acc, v, i) => {
|
const views = allProjects.reduce((acc, project, index) => {
|
||||||
acc[allProjects[i].slug] = v ?? 0;
|
acc[project.slug] = parseInt(viewsEntries[index] as string) || 0;
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>);
|
}, {} as Record<string, number>);
|
||||||
|
|
||||||
|
11
content/projects/bog.mdx
Normal file
11
content/projects/bog.mdx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
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. It’s built with security and speed in mind.
|
6976
package-lock.json
generated
Normal file
6976
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
|||||||
"@upstash/redis": "^1.23.3",
|
"@upstash/redis": "^1.23.3",
|
||||||
"contentlayer": "^0.3.4",
|
"contentlayer": "^0.3.4",
|
||||||
"framer-motion": "^10.16.4",
|
"framer-motion": "^10.16.4",
|
||||||
|
"ioredis": "^5.6.1",
|
||||||
"lucide-react": "^0.284.0",
|
"lucide-react": "^0.284.0",
|
||||||
"markdown-wasm": "^1.2.0",
|
"markdown-wasm": "^1.2.0",
|
||||||
"next": "^13.5.4",
|
"next": "^13.5.4",
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
import { Redis } from "@upstash/redis";
|
import Redis from "ioredis";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
const redis = Redis.fromEnv();
|
// Настройка подключения к локальному Redis
|
||||||
|
const redis = new Redis({
|
||||||
|
host: process.env.REDIS_HOST || "localhost",
|
||||||
|
port: parseInt(process.env.REDIS_PORT || "6379"),
|
||||||
|
password: process.env.REDIS_PASSWORD,
|
||||||
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
runtime: "edge",
|
runtime: "edge", // Возможно потребуется изменить на "nodejs" если возникнут проблемы
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function incr(req: NextRequest): Promise<NextResponse> {
|
export default async function incr(req: NextRequest): Promise<NextResponse> {
|
||||||
@ -15,33 +21,33 @@ export default async function incr(req: NextRequest): Promise<NextResponse> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
let slug: string | undefined = undefined;
|
const slug = body.slug;
|
||||||
if ("slug" in body) {
|
|
||||||
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.
|
// Хеширование IP-адреса
|
||||||
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
|
// Проверка уникальности посещения
|
||||||
const isNew = await redis.set(["deduplicate", hash, slug].join(":"), true, {
|
const key = `deduplicate:${hash}:${slug}`;
|
||||||
nx: true,
|
const isNew = await redis.set(key, "1", "EX", 86400, "NX");
|
||||||
ex: 24 * 60 * 60,
|
|
||||||
});
|
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
new NextResponse(null, { status: 202 });
|
return new NextResponse(null, { status: 202 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await redis.incr(["pageviews", "projects", slug].join(":"));
|
|
||||||
|
// Увеличиваем счетчик просмотров
|
||||||
|
await redis.incr(`projects:${slug}:views`);
|
||||||
return new NextResponse(null, { status: 202 });
|
return new NextResponse(null, { status: 202 });
|
||||||
}
|
}
|
5092
pnpm-lock.yaml
generated
5092
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user