Updating (#65)
* Fix ActivityFeed: Remove dynamic import that was causing it to disappear in production * Fix ActivityFeed hydration error: Move localStorage read to useEffect to prevent server/client mismatch * Update Node.js version to 25 in Gitea workflows - Fix EBADENGINE error for camera-controls@3.1.2 which requires Node.js >=22 - Update production-deploy.yml, dev-deploy.yml, and ci-cd-with-gitea-vars.yml.disabled - Node.js v25 matches local development environment * Update Dockerfile to use Node.js 25 - Update base image from node:20 to node:25 - Matches Gitea workflow configuration and camera-controls@3.1.2 requirements * Fix production deployment: Start database dependencies - Remove --no-deps flag which prevented postgres and redis from starting - Remove --build flag as image is already built in previous step - This fixes 'Can't reach database server at postgres:5432' error * Fix postgres health check in production - Remove init-db.sql volume mount (not available in CI/CD environment) - Init script not needed as Prisma handles schema migrations - Postgres will initialize empty database automatically * Fix cache permission error in Docker container - Create cache directories AFTER copying standalone files - Create both fetch-cache and images subdirectories - Set proper ownership for nextjs user - Fixes EACCES permission denied errors for prerender cache * Fix German jogging fallback text * Use Directus content in production * fix: Security vulnerability - block malicious file requests * fix: Switch projects to Directus, add security fixes and example projects
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { setRequestLocale } from "next-intl/server";
|
||||
import React from "react";
|
||||
import { notFound } from "next/navigation";
|
||||
import ConsentBanner from "../components/ConsentBanner";
|
||||
import { getLocalizedMessage } from "@/lib/i18n-loader";
|
||||
|
||||
async function loadEnhancedMessages(locale: string) {
|
||||
// Supported locales - must match middleware.ts
|
||||
const SUPPORTED_LOCALES = ["en", "de"] as const;
|
||||
type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
||||
|
||||
function isValidLocale(locale: string): locale is SupportedLocale {
|
||||
return SUPPORTED_LOCALES.includes(locale as SupportedLocale);
|
||||
}
|
||||
|
||||
async function loadEnhancedMessages(locale: SupportedLocale) {
|
||||
// Lade basis JSON Messages
|
||||
const baseMessages = (await import(`../../messages/${locale}.json`)).default;
|
||||
|
||||
@@ -13,6 +22,11 @@ async function loadEnhancedMessages(locale: string) {
|
||||
return baseMessages;
|
||||
}
|
||||
|
||||
// Define valid static params to prevent malicious path traversal
|
||||
export function generateStaticParams() {
|
||||
return SUPPORTED_LOCALES.map((locale) => ({ locale }));
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
params,
|
||||
@@ -21,6 +35,12 @@ export default async function LocaleLayout({
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Security: Validate locale to prevent malicious imports
|
||||
if (!isValidLocale(locale)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Ensure next-intl actually uses the route segment locale for this request.
|
||||
setRequestLocale(locale);
|
||||
// Load messages explicitly by route locale to avoid falling back to the wrong
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getProjects } from '@/lib/directus';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
@@ -7,56 +7,27 @@ export async function GET(request: NextRequest) {
|
||||
const slug = searchParams.get('slug');
|
||||
const search = searchParams.get('search');
|
||||
const category = searchParams.get('category');
|
||||
const locale = searchParams.get('locale') || 'en';
|
||||
|
||||
// Use Directus instead of Prisma
|
||||
const projects = await getProjects(locale, {
|
||||
featured: undefined,
|
||||
published: true,
|
||||
category: category && category !== 'All' ? category : undefined,
|
||||
search: search || undefined,
|
||||
});
|
||||
|
||||
if (!projects) {
|
||||
// Directus not available or no projects found
|
||||
return NextResponse.json({ projects: [] });
|
||||
}
|
||||
|
||||
// Filter by slug if provided (since Directus query doesn't support slug filter directly)
|
||||
if (slug) {
|
||||
const project = await prisma.project.findFirst({
|
||||
where: {
|
||||
published: true,
|
||||
slug,
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
|
||||
const project = projects.find(p => p.slug === slug);
|
||||
return NextResponse.json({ projects: project ? [project] : [] });
|
||||
}
|
||||
|
||||
if (search) {
|
||||
// General search
|
||||
const projects = await prisma.project.findMany({
|
||||
where: {
|
||||
published: true,
|
||||
OR: [
|
||||
{ title: { contains: search, mode: 'insensitive' } },
|
||||
{ description: { contains: search, mode: 'insensitive' } },
|
||||
{ tags: { hasSome: [search] } },
|
||||
{ content: { contains: search, mode: 'insensitive' } }
|
||||
]
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
|
||||
return NextResponse.json({ projects });
|
||||
}
|
||||
|
||||
if (category && category !== 'All') {
|
||||
// Filter by category
|
||||
const projects = await prisma.project.findMany({
|
||||
where: {
|
||||
published: true,
|
||||
category: category
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
|
||||
return NextResponse.json({ projects });
|
||||
}
|
||||
|
||||
// Return all published projects if no specific search
|
||||
const projects = await prisma.project.findMany({
|
||||
where: { published: true },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
|
||||
return NextResponse.json({ projects });
|
||||
} catch (error) {
|
||||
console.error('Error searching projects:', error);
|
||||
|
||||
Reference in New Issue
Block a user