fix: Switch projects to Directus, add security fixes and example projects
All checks were successful
Production Deployment (Zero Downtime) / deploy-production (push) Successful in 14m27s
All checks were successful
Production Deployment (Zero Downtime) / deploy-production (push) Successful in 14m27s
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { prisma } from '@/lib/prisma';
|
import { getProjects } from '@/lib/directus';
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
@@ -7,56 +7,27 @@ export async function GET(request: NextRequest) {
|
|||||||
const slug = searchParams.get('slug');
|
const slug = searchParams.get('slug');
|
||||||
const search = searchParams.get('search');
|
const search = searchParams.get('search');
|
||||||
const category = searchParams.get('category');
|
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) {
|
if (slug) {
|
||||||
const project = await prisma.project.findFirst({
|
const project = projects.find(p => p.slug === slug);
|
||||||
where: {
|
|
||||||
published: true,
|
|
||||||
slug,
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
});
|
|
||||||
|
|
||||||
return NextResponse.json({ projects: project ? [project] : [] });
|
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 });
|
return NextResponse.json({ projects });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error searching projects:', error);
|
console.error('Error searching projects:', error);
|
||||||
|
|||||||
178
scripts/add-example-projects.js
Normal file
178
scripts/add-example-projects.js
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Add Example Projects to Directus
|
||||||
|
*
|
||||||
|
* Creates 3 example projects in Directus CMS
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node scripts/add-example-projects.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
if (!DIRECTUS_TOKEN) {
|
||||||
|
console.error('❌ Error: DIRECTUS_STATIC_TOKEN not found in .env');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function directusRequest(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${DIRECTUS_TOKEN}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
options.body = JSON.stringify(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
console.error(`HTTP ${response.status}: ${text}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error calling ${method} ${endpoint}:`, error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exampleProjects = [
|
||||||
|
{
|
||||||
|
slug: 'portfolio-website',
|
||||||
|
status: 'published',
|
||||||
|
featured: true,
|
||||||
|
category: 'Web Application',
|
||||||
|
difficulty: 'Advanced',
|
||||||
|
date: '2024',
|
||||||
|
github: 'https://github.com/denshooter/portfolio',
|
||||||
|
live: 'https://dk0.dev',
|
||||||
|
image_url: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800',
|
||||||
|
tags: ['Next.js', 'React', 'TypeScript', 'Tailwind CSS'],
|
||||||
|
technologies: ['Next.js 14', 'TypeScript', 'Tailwind CSS', 'Directus', 'Docker'],
|
||||||
|
challenges: 'Building a performant, SEO-optimized portfolio with multilingual support',
|
||||||
|
lessons_learned: 'Learned about Next.js App Router, Server Components, and modern deployment strategies',
|
||||||
|
future_improvements: 'Add blog section, improve animations, add dark mode',
|
||||||
|
translations: [
|
||||||
|
{
|
||||||
|
languages_code: 'en-US',
|
||||||
|
title: 'Portfolio Website',
|
||||||
|
description: 'A modern, performant portfolio website built with Next.js and Directus CMS',
|
||||||
|
content: '# Portfolio Website\n\nThis is my personal portfolio built with modern web technologies.\n\n## Features\n\n- 🚀 Fast and performant\n- 🌍 Multilingual (EN/DE)\n- 📱 Fully responsive\n- ♿ Accessible\n- 🎨 Beautiful design\n\n## Tech Stack\n\n- Next.js 14 with App Router\n- TypeScript\n- Tailwind CSS\n- Directus CMS\n- Docker deployment',
|
||||||
|
meta_description: 'Modern portfolio website showcasing my projects and skills',
|
||||||
|
keywords: 'portfolio, nextjs, react, typescript, web development'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
languages_code: 'de-DE',
|
||||||
|
title: 'Portfolio Website',
|
||||||
|
description: 'Eine moderne, performante Portfolio-Website mit Next.js und Directus CMS',
|
||||||
|
content: '# Portfolio Website\n\nDies ist mein persönliches Portfolio mit modernen Web-Technologien.\n\n## Features\n\n- 🚀 Schnell und performant\n- 🌍 Mehrsprachig (EN/DE)\n- 📱 Voll responsiv\n- ♿ Barrierefrei\n- 🎨 Schönes Design\n\n## Tech Stack\n\n- Next.js 14 mit App Router\n- TypeScript\n- Tailwind CSS\n- Directus CMS\n- Docker Deployment',
|
||||||
|
meta_description: 'Moderne Portfolio-Website mit meinen Projekten und Fähigkeiten',
|
||||||
|
keywords: 'portfolio, nextjs, react, typescript, webentwicklung'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'task-manager-app',
|
||||||
|
status: 'published',
|
||||||
|
featured: true,
|
||||||
|
category: 'Web Application',
|
||||||
|
difficulty: 'Intermediate',
|
||||||
|
date: '2023',
|
||||||
|
github: 'https://github.com/example/task-manager',
|
||||||
|
image_url: 'https://images.unsplash.com/photo-1484480974693-6ca0a78fb36b?w=800',
|
||||||
|
tags: ['React', 'Node.js', 'MongoDB', 'REST API'],
|
||||||
|
technologies: ['React', 'Node.js', 'Express', 'MongoDB', 'JWT'],
|
||||||
|
challenges: 'Implementing real-time updates and secure authentication',
|
||||||
|
lessons_learned: 'Learned about WebSockets, JWT authentication, and database optimization',
|
||||||
|
future_improvements: 'Add team collaboration features, mobile app, calendar integration',
|
||||||
|
translations: [
|
||||||
|
{
|
||||||
|
languages_code: 'en-US',
|
||||||
|
title: 'Task Manager App',
|
||||||
|
description: 'A full-stack task management application with real-time updates',
|
||||||
|
content: '# Task Manager App\n\nA comprehensive task management solution for individuals and teams.\n\n## Features\n\n- ✅ Create and manage tasks\n- 🔔 Real-time notifications\n- 🔐 Secure authentication\n- 📊 Progress tracking\n- 🎨 Customizable categories\n\n## Implementation\n\nBuilt with React on the frontend and Node.js/Express on the backend. Uses MongoDB for data storage and JWT for authentication.',
|
||||||
|
meta_description: 'Full-stack task management application',
|
||||||
|
keywords: 'task manager, productivity, react, nodejs, mongodb'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
languages_code: 'de-DE',
|
||||||
|
title: 'Task Manager App',
|
||||||
|
description: 'Eine Full-Stack Aufgabenverwaltungs-App mit Echtzeit-Updates',
|
||||||
|
content: '# Task Manager App\n\nEine umfassende Aufgabenverwaltungslösung für Einzelpersonen und Teams.\n\n## Features\n\n- ✅ Aufgaben erstellen und verwalten\n- 🔔 Echtzeit-Benachrichtigungen\n- 🔐 Sichere Authentifizierung\n- 📊 Fortschrittsverfolgung\n- 🎨 Anpassbare Kategorien\n\n## Implementierung\n\nErstellt mit React im Frontend und Node.js/Express im Backend. Nutzt MongoDB für Datenspeicherung und JWT für Authentifizierung.',
|
||||||
|
meta_description: 'Full-Stack Aufgabenverwaltungs-Anwendung',
|
||||||
|
keywords: 'task manager, produktivität, react, nodejs, mongodb'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'weather-dashboard',
|
||||||
|
status: 'published',
|
||||||
|
featured: false,
|
||||||
|
category: 'Web Application',
|
||||||
|
difficulty: 'Beginner',
|
||||||
|
date: '2023',
|
||||||
|
live: 'https://weather-demo.example.com',
|
||||||
|
image_url: 'https://images.unsplash.com/photo-1504608524841-42fe6f032b4b?w=800',
|
||||||
|
tags: ['JavaScript', 'API', 'CSS', 'HTML'],
|
||||||
|
technologies: ['Vanilla JavaScript', 'OpenWeather API', 'CSS Grid', 'LocalStorage'],
|
||||||
|
challenges: 'Working with external APIs and handling async data',
|
||||||
|
lessons_learned: 'Learned about API integration, error handling, and responsive design',
|
||||||
|
future_improvements: 'Add weather forecasts, save favorite locations, add charts',
|
||||||
|
translations: [
|
||||||
|
{
|
||||||
|
languages_code: 'en-US',
|
||||||
|
title: 'Weather Dashboard',
|
||||||
|
description: 'A simple weather dashboard showing current weather conditions',
|
||||||
|
content: '# Weather Dashboard\n\nA clean and simple weather dashboard for checking current conditions.\n\n## Features\n\n- 🌤️ Current weather data\n- 📍 Location search\n- 💾 Save favorite locations\n- 📱 Responsive design\n- 🎨 Clean UI\n\n## Technical Details\n\nBuilt with vanilla JavaScript and the OpenWeather API. Uses CSS Grid for layout and LocalStorage for saving user preferences.',
|
||||||
|
meta_description: 'Simple weather dashboard application',
|
||||||
|
keywords: 'weather, dashboard, javascript, api'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
languages_code: 'de-DE',
|
||||||
|
title: 'Wetter Dashboard',
|
||||||
|
description: 'Ein einfaches Wetter-Dashboard mit aktuellen Wetterbedingungen',
|
||||||
|
content: '# Wetter Dashboard\n\nEin übersichtliches Wetter-Dashboard zur Anzeige aktueller Bedingungen.\n\n## Features\n\n- 🌤️ Aktuelle Wetterdaten\n- 📍 Standortsuche\n- 💾 Favoriten speichern\n- 📱 Responsives Design\n- 🎨 Sauberes UI\n\n## Technische Details\n\nErstellt mit Vanilla JavaScript und der OpenWeather API. Nutzt CSS Grid für das Layout und LocalStorage zum Speichern von Benutzereinstellungen.',
|
||||||
|
meta_description: 'Einfache Wetter-Dashboard Anwendung',
|
||||||
|
keywords: 'wetter, dashboard, javascript, api'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
async function addProjects() {
|
||||||
|
console.log('\n📦 Adding Example Projects to Directus...\n');
|
||||||
|
|
||||||
|
for (const projectData of exampleProjects) {
|
||||||
|
console.log(`\n📁 Creating: ${projectData.translations[0].title}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await directusRequest(
|
||||||
|
'items/projects',
|
||||||
|
'POST',
|
||||||
|
projectData
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
console.log(` ✅ Successfully created project: ${projectData.slug}`);
|
||||||
|
} else {
|
||||||
|
console.log(` ❌ Failed to create project: ${projectData.slug}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(` ❌ Error creating project ${projectData.slug}:`, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n✅ Done!\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
addProjects().catch(console.error);
|
||||||
Reference in New Issue
Block a user