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: Record = {}; 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: Record) { 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: Record) { 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 'like' | 'share' | 'view' | 'comment', ip, userAgent } }); }, // Get analytics async getAnalytics(projectId: number): Promise> { const [pageViews, interactions] = await Promise.all([ prisma.pageView.count({ where: { projectId } }), prisma.userInteraction.groupBy({ by: ['type'], where: { projectId }, }) ]); const analytics: Record = { 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 Record, byDifficulty: {} as Record }; projects.forEach(project => { const perf = project.performance as Record; const analytics = project.analytics as Record; 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; } };