From 65db21283880311408d9f6d5987773dbe8cc4a81 Mon Sep 17 00:00:00 2001 From: AderKonstantin Date: Mon, 19 May 2025 12:25:05 +0300 Subject: [PATCH] Update .dockerignore, .env, route.ts, and 8 more files --- .dockerignore | 7 ----- .env | 4 +-- app/api/views/route.ts | 25 ---------------- app/layout.tsx | 11 ------- app/projects/[slug]/header.tsx | 27 ++--------------- app/projects/[slug]/page.tsx | 16 +--------- app/projects/article.tsx | 7 +---- app/projects/page.tsx | 36 +++-------------------- docker-compose.yml | 47 ++++-------------------------- lib/redis.ts | 18 ------------ pages/api/incr.ts | 53 ---------------------------------- 11 files changed, 16 insertions(+), 235 deletions(-) delete mode 100644 .dockerignore delete mode 100644 app/api/views/route.ts delete mode 100644 lib/redis.ts delete mode 100644 pages/api/incr.ts diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 3e83e6a..0000000 --- a/.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules -.git -.next -.vscode - -Dockerfile -.dockerignore diff --git a/.env b/.env index 3c0b501..fefdc96 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -REDIS_HOST=localhost -REDIS_PORT=6379 +REDIS_HOST="172.18.0.2" +REDIS_PORT="6379" REDIS_PASSWORD= # если используется \ No newline at end of file diff --git a/app/api/views/route.ts b/app/api/views/route.ts deleted file mode 100644 index 388891f..0000000 --- a/app/api/views/route.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NextResponse } from "next/server"; -import { getRedisClient } from "../../../lib/redis"; - -export const dynamic = 'force-dynamic'; -export const fetchCache = 'force-no-store'; - -export async function GET() { - try { - const redis = getRedisClient(); - const keys = await redis.keys('projects:*:views'); - if (keys.length === 0) return NextResponse.json({}); - - const values = await redis.mget(...keys); - const views = keys.reduce((acc, key, i) => { - const slug = key.split(':')[1]; - acc[slug] = parseInt(values[i] as string) || 0; - return acc; - }, {} as Record); - - return NextResponse.json(views); - } catch (error) { - console.error('Redis error:', error); - return NextResponse.json({}, { status: 500 }); - } -} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index aec88d0..22db2c1 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -19,17 +19,6 @@ export const metadata: Metadata = { locale: "ru-RU", type: "website", }, - robots: { - index: true, - follow: true, - googleBot: { - index: true, - follow: true, - "max-video-preview": -1, - "max-image-preview": "large", - "max-snippet": -1, - }, - }, icons: { shortcut: "/favicon.png", }, diff --git a/app/projects/[slug]/header.tsx b/app/projects/[slug]/header.tsx index 04267bb..8ab02af 100644 --- a/app/projects/[slug]/header.tsx +++ b/app/projects/[slug]/header.tsx @@ -10,9 +10,8 @@ type Props = { description: string; repository?: string; }; - views: number; }; -export const Header: React.FC = ({ project, views }) => { +export const Header: React.FC = ({ project}) => { const ref = useRef(null); const [isIntersecting, setIntersecting] = useState(true); @@ -53,29 +52,7 @@ export const Header: React.FC = ({ project, views }) => { >
- - {" "} - {Intl.NumberFormat("en-US", { notation: "compact" }).format( - views, - )} - - - - - + { return allProjects .filter((p) => p.published) @@ -37,15 +29,9 @@ export default async function PostPage({ params }: Props) { notFound(); } - // Получаем и преобразуем значение просмотров - const views = parseInt( - (await redis.get(`projects:${slug}:views`)) || "0", - 10 - ); - return (
-
+
diff --git a/app/projects/article.tsx b/app/projects/article.tsx index ae11ce5..e38944e 100644 --- a/app/projects/article.tsx +++ b/app/projects/article.tsx @@ -4,10 +4,9 @@ import { Eye, View } from "lucide-react"; type Props = { project: Project; - views: number; }; -export const Article: React.FC = ({ project, views }) => { +export const Article: React.FC = ({ project }) => { return (
@@ -23,10 +22,6 @@ export const Article: React.FC = ({ project, views }) => { SOON )} - - {" "} - {Intl.NumberFormat("en-US", { notation: "compact" }).format(views)} -

{project.title} diff --git a/app/projects/page.tsx b/app/projects/page.tsx index b2ea28a..24597bd 100644 --- a/app/projects/page.tsx +++ b/app/projects/page.tsx @@ -8,30 +8,8 @@ import { Card } from "../components/card"; import { Article } from "./article"; import { Eye } from "lucide-react"; -type ViewsData = Record; export default function ProjectsPage() { - const [views, setViews] = useState({}); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchViews = async () => { - try { - const response = await fetch('/api/views'); - if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); - const data = await response.json(); - setViews(data); - } catch (err) { - setError(err instanceof Error ? err.message : 'Unknown error'); - } finally { - setLoading(false); - } - }; - - fetchViews(); - }, []); - const featured = allProjects.find((project) => project.slug === "cbg")!; const top2 = allProjects.find((project) => project.slug === "blog")!; const top3 = allProjects.find((project) => project.slug === "bimkaspace")!; @@ -80,12 +58,6 @@ export default function ProjectsPage() { SOON )}

- - {" "} - {Intl.NumberFormat("en-US", { notation: "compact" }).format( - views[featured.slug] ?? 0, - )} -

{[top2, top3].map((project) => ( -
+
))} @@ -122,7 +94,7 @@ export default function ProjectsPage() { .filter((_, i) => i % 3 === 0) .map((project) => ( -
+
))} @@ -131,7 +103,7 @@ export default function ProjectsPage() { .filter((_, i) => i % 3 === 1) .map((project) => ( -
+
))} @@ -140,7 +112,7 @@ export default function ProjectsPage() { .filter((_, i) => i % 3 === 2) .map((project) => ( -
+
))} diff --git a/docker-compose.yml b/docker-compose.yml index fd24908..1044d49 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,47 +1,12 @@ services: - redis: - image: redis:alpine - container_name: main-aderk-redis - volumes: - - redis_data:/data - environment: - - REDIS_PASSWORD=${REDIS_PASSWORD} - command: [ "redis-server", "--requirepass", "${REDIS_PASSWORD}" ] - networks: - - backend - - healthcheck: - test: [ "CMD", "redis-cli", "ping" ] - interval: 5s - timeout: 3s - retries: 3 next: build: context: . dockerfile: Dockerfile container_name: main-aderk-next - labels: - - "traefik.enable=true" - - "traefik.http.routers.next.rule=Host(`aderk.tech`)" - - "traefik.http.routers.next.entrypoints=https" - - "traefik.http.routers.next.tls=true" - - "traefik.http.services.next.loadbalancer.server.port=3000" - environment: - - REDIS_HOST=${REDIS_HOST} - - REDIS_PORT=${REDIS_PORT} - - REDIS_PASSWORD=${REDIS_PASSWORD} - depends_on: - - redis - networks: - - proxy - - backend - -volumes: - redis_data: - - -networks: - proxy: - external: true - backend: - internal: true + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.next.rule=Host(`aderk.tech`)" + # - "traefik.http.routers.next.entrypoints=https" + # - "traefik.http.routers.next.tls=true" + # - "traefik.http.services.next.loadbalancer.server.port=3000" diff --git a/lib/redis.ts b/lib/redis.ts deleted file mode 100644 index 42b262f..0000000 --- a/lib/redis.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Redis from "ioredis"; - -let redis: Redis | null = null; - -export const getRedisClient = () => { - if (!redis) { - redis = new Redis({ - host: process.env.REDIS_HOST || "localhost", - port: parseInt(process.env.REDIS_PORT || "6379"), - password: process.env.REDIS_PASSWORD, - }); - - redis.on("error", (err) => { - console.error("Redis error:", err); - }); - } - return redis; -}; \ No newline at end of file diff --git a/pages/api/incr.ts b/pages/api/incr.ts deleted file mode 100644 index 7ef4f64..0000000 --- a/pages/api/incr.ts +++ /dev/null @@ -1,53 +0,0 @@ -import Redis from "ioredis"; -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, -}); - -export const config = { - runtime: "nodejs", // Возможно потребуется изменить на "nodejs" если возникнут проблемы -}; - -export default async function incr(req: NextRequest): Promise { - if (req.method !== "POST") { - return new NextResponse("use POST", { status: 405 }); - } - if (req.headers.get("Content-Type") !== "application/json") { - return new NextResponse("must be json", { status: 400 }); - } - - const body = await req.json(); - const slug = body.slug; - - if (!slug) { - return new NextResponse("Slug not found", { status: 400 }); - } - - const ip = req.ip; - if (ip) { - // Хеширование IP-адреса - const buf = await crypto.subtle.digest( - "SHA-256", - 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"); - - if (!isNew) { - return new NextResponse(null, { status: 202 }); - } - } - - // Увеличиваем счетчик просмотров - await redis.incr(`projects:${slug}:views`); - return new NextResponse(null, { status: 202 }); -} \ No newline at end of file