update
This commit is contained in:
237
lib/prisma.ts
Normal file
237
lib/prisma.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user