Files
portfolio/lib/prisma.ts
Dennis Konkol 203a332306 update
2025-09-02 23:46:36 +00:00

238 lines
6.1 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,
include: {
_count: {
select: {
pageViews: true,
userInteractions: true
}
}
}
}),
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 },
include: {
_count: {
select: {
pageViews: true,
userInteractions: true
}
}
}
});
},
// 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 },
_count: { type: true }
})
]);
const analytics: any = { views: pageViews, likes: 0, shares: 0 };
interactions.forEach(interaction => {
if (interaction.type === 'LIKE') analytics.likes = interaction._count.type;
if (interaction.type === 'SHARE') analytics.shares = interaction._count.type;
});
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;
}
};