Files
portfolio/lib/prisma.ts
Dennis Konkol b9b3e5308d 🚀 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
2025-09-05 19:47:53 +00:00

221 lines
5.7 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
// Database service functions
export const projectService = {
// Get all projects with pagination and filtering
async getAllProjects(options: {
page?: number;
limit?: number;
category?: string;
featured?: boolean;
published?: boolean;
difficulty?: string;
search?: string;
} = {}) {
const { page = 1, limit = 50, category, featured, published, difficulty, search } = options;
const skip = (page - 1) * limit;
const where: any = {};
if (category) where.category = category;
if (featured !== undefined) where.featured = featured;
if (published !== undefined) where.published = published;
if (difficulty) where.difficulty = difficulty;
if (search) {
where.OR = [
{ title: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } },
{ tags: { hasSome: [search] } },
{ content: { contains: search, mode: 'insensitive' } }
];
}
const [projects, total] = await Promise.all([
prisma.project.findMany({
where,
orderBy: { createdAt: 'desc' },
skip,
take: limit,
}),
prisma.project.count({ where })
]);
return {
projects,
total,
pages: Math.ceil(total / limit),
currentPage: page
};
},
// Get project by ID
async getProjectById(id: number) {
return prisma.project.findUnique({
where: { id },
});
},
// Create new project
async createProject(data: any) {
return prisma.project.create({
data: {
...data,
performance: data.performance || { lighthouse: 90, bundleSize: '50KB', loadTime: '1.5s' },
analytics: data.analytics || { views: 0, likes: 0, shares: 0 }
}
});
},
// Update project
async updateProject(id: number, data: any) {
return prisma.project.update({
where: { id },
data: { ...data, updatedAt: new Date() }
});
},
// Delete project
async deleteProject(id: number) {
return prisma.project.delete({
where: { id }
});
},
// Get featured projects
async getFeaturedProjects(limit: number = 6) {
return prisma.project.findMany({
where: { featured: true, published: true },
orderBy: { createdAt: 'desc' },
take: limit
});
},
// Get projects by category
async getProjectsByCategory(category: string, limit: number = 10) {
return prisma.project.findMany({
where: { category, published: true },
orderBy: { createdAt: 'desc' },
take: limit
});
},
// Search projects
async searchProjects(query: string, limit: number = 20) {
return prisma.project.findMany({
where: {
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ description: { contains: query, mode: 'insensitive' } },
{ tags: { hasSome: [query] } },
{ content: { contains: query, mode: 'insensitive' } }
],
published: true
},
orderBy: { createdAt: 'desc' },
take: limit
});
},
// Track page view
async trackPageView(projectId: number | null, page: string, ip?: string, userAgent?: string, referrer?: string) {
return prisma.pageView.create({
data: {
projectId,
page,
ip,
userAgent,
referrer
}
});
},
// Track user interaction
async trackUserInteraction(projectId: number, type: string, ip?: string, userAgent?: string) {
return prisma.userInteraction.create({
data: {
projectId,
type: type as any,
ip,
userAgent
}
});
},
// Get analytics
async getAnalytics(projectId: number) {
const [pageViews, interactions] = await Promise.all([
prisma.pageView.count({ where: { projectId } }),
prisma.userInteraction.groupBy({
by: ['type'],
where: { projectId },
})
]);
const analytics: any = { views: pageViews, likes: 0, shares: 0 };
interactions.forEach(interaction => {
if (interaction.type === 'LIKE') analytics.likes = 0;
if (interaction.type === 'SHARE') analytics.shares = 0;
});
return analytics;
},
// Get performance stats
async getPerformanceStats() {
const projects = await prisma.project.findMany({
select: {
performance: true,
analytics: true,
category: true,
difficulty: true
}
});
const stats = {
totalProjects: projects.length,
avgLighthouse: 0,
totalViews: 0,
totalLikes: 0,
totalShares: 0,
byCategory: {} as any,
byDifficulty: {} as any
};
projects.forEach(project => {
const perf = project.performance as any;
const analytics = project.analytics as any;
stats.avgLighthouse += perf?.lighthouse || 0;
stats.totalViews += analytics?.views || 0;
stats.totalLikes += analytics?.likes || 0;
stats.totalShares += analytics?.shares || 0;
// Category stats
if (!stats.byCategory[project.category]) stats.byCategory[project.category] = 0;
stats.byCategory[project.category]++;
// Difficulty stats
if (!stats.byDifficulty[project.difficulty]) stats.byDifficulty[project.difficulty] = 0;
stats.byDifficulty[project.difficulty]++;
});
if (stats.totalProjects > 0) {
stats.avgLighthouse = Math.round(stats.avgLighthouse / stats.totalProjects);
}
return stats;
}
};