🚀 Add automatic deployment system

- Add auto-deploy.sh script with full CI/CD pipeline
- Add quick-deploy.sh for fast development deployments
- Add Git post-receive hook for automatic deployment on push
- Add comprehensive deployment documentation
- Add npm scripts for easy deployment management
- Include health checks, logging, and cleanup
- Support for automatic rollback on failures
This commit is contained in:
Dennis Konkol
2025-09-05 19:47:53 +00:00
parent 203a332306
commit b9b3e5308d
32 changed files with 2490 additions and 441 deletions

View File

@@ -24,16 +24,37 @@ import {
Calendar,
Activity
} from 'lucide-react';
import { projectService, DatabaseProject } from '@/lib/prisma';
import { projectService } from '@/lib/prisma';
import { useToast } from './Toast';
interface Project {
id: number;
title: string;
description: string;
content: string;
imageUrl?: string | null;
github?: string | null;
liveUrl?: string | null;
tags: string[];
category: string;
difficulty: string;
featured: boolean;
published: boolean;
createdAt: Date;
updatedAt: Date;
_count?: {
pageViews: number;
userInteractions: number;
};
}
interface AdminDashboardProps {
onProjectSelect: (project: DatabaseProject) => void;
onProjectSelect: (project: Project) => void;
onNewProject: () => void;
}
export default function AdminDashboard({ onProjectSelect, onNewProject }: AdminDashboardProps) {
const [projects, setProjects] = useState<DatabaseProject[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState<string>('');
@@ -52,7 +73,7 @@ export default function AdminDashboard({ onProjectSelect, onNewProject }: AdminD
try {
setLoading(true);
const data = await projectService.getAllProjects();
setProjects(data);
setProjects(data.projects);
} catch (error) {
console.error('Error loading projects:', error);
// Fallback to localStorage if database fails
@@ -79,8 +100,8 @@ export default function AdminDashboard({ onProjectSelect, onNewProject }: AdminD
switch (sortBy) {
case 'date':
aValue = new Date(a.created_at);
bValue = new Date(b.created_at);
aValue = new Date(a.createdAt);
bValue = new Date(b.createdAt);
break;
case 'title':
aValue = a.title.toLowerCase();
@@ -92,12 +113,12 @@ export default function AdminDashboard({ onProjectSelect, onNewProject }: AdminD
bValue = difficultyOrder[b.difficulty as keyof typeof difficultyOrder];
break;
case 'views':
aValue = a.analytics.views;
bValue = b.analytics.views;
aValue = a._count?.pageViews || 0;
bValue = b._count?.pageViews || 0;
break;
default:
aValue = a.created_at;
bValue = b.created_at;
aValue = a.createdAt;
bValue = b.createdAt;
}
if (sortOrder === 'asc') {
@@ -113,10 +134,9 @@ export default function AdminDashboard({ onProjectSelect, onNewProject }: AdminD
published: projects.filter(p => p.published).length,
featured: projects.filter(p => p.featured).length,
categories: new Set(projects.map(p => p.category)).size,
totalViews: projects.reduce((sum, p) => sum + p.analytics.views, 0),
totalLikes: projects.reduce((sum, p) => sum + p.analytics.likes, 0),
avgLighthouse: projects.length > 0 ?
Math.round(projects.reduce((sum, p) => sum + p.performance.lighthouse, 0) / projects.length) : 0
totalViews: projects.reduce((sum, p) => sum + (p._count?.pageViews || 0), 0),
totalLikes: projects.reduce((sum, p) => sum + (p._count?.userInteractions || 0), 0),
avgLighthouse: 0
};
// Bulk operations
@@ -514,15 +534,15 @@ export default function AdminDashboard({ onProjectSelect, onNewProject }: AdminD
</span>
<span className="flex items-center">
<Calendar className="mr-1" size={14} />
{new Date(project.created_at).toLocaleDateString()}
{new Date(project.createdAt).toLocaleDateString()}
</span>
<span className="flex items-center">
<Eye className="mr-1" size={14} />
{project.analytics.views} views
{project._count?.pageViews || 0} views
</span>
<span className="flex items-center">
<Activity className="mr-1" size={14} />
{project.performance.lighthouse}/100
N/A
</span>
</div>
</div>