Update .dockerignore, route.ts, layout.tsx, and 6 more files

This commit is contained in:
AderKonstantin 2025-05-18 16:22:03 +03:00
parent 4283932d9e
commit 9bd8773b3b
9 changed files with 103 additions and 47 deletions

7
.dockerignore Normal file
View File

@ -0,0 +1,7 @@
node_modules
.git
.next
.vscode
Dockerfile
.dockerignore

26
app/api/views/route.ts Normal file
View File

@ -0,0 +1,26 @@
import { NextResponse } from "next/server";
import { getRedisClient } from "../../../lib/redis";
export const dynamic = 'force-dynamic'; // Отключаем кеширование
export async function GET() {
try {
const redis = getRedisClient();
const projects = await redis.keys("projects:*:views");
const viewsEntries = await redis.mget(...projects);
const viewsData = projects.reduce((acc, key, index) => {
const slug = key.split(":")[1];
acc[slug] = parseInt(viewsEntries[index] as string) || 0;
return acc;
}, {} as Record<string, number>);
return NextResponse.json(viewsData);
} catch (error) {
console.error("Redis error:", error);
return NextResponse.json(
{ error: "Failed to fetch views" },
{ status: 500 }
);
}
}

View File

@ -16,13 +16,6 @@ export const metadata: Metadata = {
"Junior Dev & Student", "Junior Dev & Student",
url: "https://aderk.tech", url: "https://aderk.tech",
siteName: "aderk.tech", siteName: "aderk.tech",
images: [
{
url: "https://aderk.tech/og.png",
width: 1920,
height: 1080,
},
],
locale: "ru-RU", locale: "ru-RU",
type: "website", type: "website",
}, },

View File

@ -10,7 +10,6 @@ type Props = {
description: string; description: string;
repository?: string; repository?: string;
}; };
views: number; views: number;
}; };
export const Header: React.FC<Props> = ({ project, views }) => { export const Header: React.FC<Props> = ({ project, views }) => {

View File

@ -1,33 +1,44 @@
'use client'; // Превращаем компонент в клиентский
import Link from "next/link"; import Link from "next/link";
import React from "react"; import React, { useEffect, useState } from "react";
import { allProjects } from "contentlayer/generated"; 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 "ioredis"; // Заменяем @upstash/redis на ioredis
import { Eye } from "lucide-react"; 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, // если есть пароль
});
export const revalidate = 60; 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) => { type ViewsData = Record<string, number>;
acc[project.slug] = parseInt(viewsEntries[index] as string) || 0;
return acc; export default function ProjectsPage() {
}, {} as Record<string, number>); const [views, setViews] = useState<ViewsData>({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Загружаем данные через клиентский запрос
useEffect(() => {
const fetchViews = async () => {
try {
const response = await fetch('/api/views');
if (!response.ok) throw new Error('Failed to fetch views');
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 featured = allProjects.find((project) => project.slug === "cbg")!;
const top2 = allProjects.find((project) => project.slug === "blog")!; const top2 = allProjects.find((project) => project.slug === "blog")!;
const top3 = allProjects.find((project) => project.slug === "bimkaspace")!; const top3 = allProjects.find((project) => project.slug === "bimkaspace")!;
const sorted = allProjects const sorted = allProjects
.filter((p) => p.published) .filter((p) => p.published)
.filter( .filter(

View File

@ -21,3 +21,4 @@ published: true
### DataBase ### DataBase
- PostgreSQL - PostgreSQL

View File

@ -1,26 +1,7 @@
services: services:
next:
build:
context: .
dockerfile: Dockerfile
container_name: example-frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.next.rule=Host(`beta.example`)"
- "traefik.http.routers.next.entrypoints=https" # Thats correct
- "traefik.http.routers.next.tls=true" # I dont need certresolver here
- "traefik.http.services.next.loadbalancer.server.port=3000"
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
depends_on:
- redis
networks:
- proxy
- backend
redis: redis:
image: redis:alpine image: redis:alpine
container_name: main-aderk-redis
volumes: volumes:
- redis_data:/data - redis_data:/data
environment: environment:
@ -34,6 +15,26 @@ services:
interval: 5s interval: 5s
timeout: 3s timeout: 3s
retries: 3 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
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
depends_on:
- redis
networks:
- proxy
- backend
volumes: volumes:
redis_data: redis_data:

18
lib/redis.ts Normal file
View File

@ -0,0 +1,18 @@
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;
};

View File

@ -9,7 +9,7 @@ const redis = new Redis({
}); });
export const config = { export const config = {
runtime: "edge", // Возможно потребуется изменить на "nodejs" если возникнут проблемы runtime: "nodejs", // Возможно потребуется изменить на "nodejs" если возникнут проблемы
}; };
export default async function incr(req: NextRequest): Promise<NextResponse> { export default async function incr(req: NextRequest): Promise<NextResponse> {