Files
ckeproekt/lib/graphql/resolvers.ts

455 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
const prisma = new PrismaClient();
export const resolvers = {
Query: {
// News queries
news: async (_: any, { id }: { id: string }) => {
return await prisma.news.findUnique({
where: { id },
include: { author: true }
});
},
newsList: async (_: any, args: {
page?: number;
limit?: number;
category?: string;
search?: string;
featured?: boolean;
published?: boolean;
sortBy?: string;
sortOrder?: string;
}) => {
const {
page = 1,
limit = 10,
category,
search,
featured,
published = true,
sortBy = 'publishedAt',
sortOrder = 'desc'
} = args;
const skip = (page - 1) * limit;
const where: any = {};
if (published !== undefined) {
where.published = published;
}
if (category) {
where.category = category;
}
if (featured !== undefined) {
where.featured = featured;
}
if (search) {
where.OR = [
{ title: { contains: search, mode: 'insensitive' } },
{ summary: { contains: search, mode: 'insensitive' } },
{ content: { contains: search, mode: 'insensitive' } }
];
}
const orderBy: any = {};
orderBy[sortBy] = sortOrder;
const [news, total] = await Promise.all([
prisma.news.findMany({
where,
skip,
take: limit,
orderBy,
include: { author: true }
}),
prisma.news.count({ where })
]);
const totalPages = Math.ceil(total / limit);
return {
news,
total,
page,
limit,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1
};
},
newsCount: async (_: any, args: {
category?: string;
search?: string;
published?: boolean;
}) => {
const { category, search, published = true } = args;
const where: any = {};
if (published !== undefined) {
where.published = published;
}
if (category) {
where.category = category;
}
if (search) {
where.OR = [
{ title: { contains: search, mode: 'insensitive' } },
{ summary: { contains: search, mode: 'insensitive' } },
{ content: { contains: search, mode: 'insensitive' } }
];
}
return await prisma.news.count({ where });
},
// Category queries
categories: async () => {
return await prisma.category.findMany({
orderBy: { name: 'asc' }
});
},
category: async (_: any, { id }: { id: string }) => {
return await prisma.category.findUnique({
where: { id }
});
},
// User queries
user: async (_: any, { id }: { id: string }) => {
return await prisma.user.findUnique({
where: { id },
include: { news: true }
});
},
users: async () => {
return await prisma.user.findMany({
include: { news: true }
});
},
me: async (_: any, __: any, context: any) => {
if (!context.user) {
throw new Error('Not authenticated');
}
return await prisma.user.findUnique({
where: { id: context.user.id },
include: { news: true }
});
}
},
Mutation: {
// News mutations
createNews: async (_: any, { input }: { input: any }, context: any) => {
if (!context.user) {
throw new Error('Not authenticated');
}
const news = await prisma.news.create({
data: {
...input,
authorId: context.user.id,
publishedAt: input.publishedAt ? new Date(input.publishedAt) : new Date()
},
include: { author: true }
});
return news;
},
updateNews: async (_: any, { id, input }: { id: string; input: any }, context: any) => {
if (!context.user) {
throw new Error('Not authenticated');
}
const existingNews = await prisma.news.findUnique({
where: { id }
});
if (!existingNews) {
throw new Error('News not found');
}
if (existingNews.authorId !== context.user.id && context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
const updateData: any = { ...input };
if (input.publishedAt) {
updateData.publishedAt = new Date(input.publishedAt);
}
const news = await prisma.news.update({
where: { id },
data: updateData,
include: { author: true }
});
return news;
},
deleteNews: async (_: any, { id }: { id: string }, context: any) => {
if (!context.user) {
throw new Error('Not authenticated');
}
const existingNews = await prisma.news.findUnique({
where: { id }
});
if (!existingNews) {
throw new Error('News not found');
}
if (existingNews.authorId !== context.user.id && context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
await prisma.news.delete({
where: { id }
});
return true;
},
toggleNewsPublished: async (_: any, { id }: { id: string }, context: any) => {
if (!context.user) {
throw new Error('Not authenticated');
}
const existingNews = await prisma.news.findUnique({
where: { id }
});
if (!existingNews) {
throw new Error('News not found');
}
if (existingNews.authorId !== context.user.id && context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
const news = await prisma.news.update({
where: { id },
data: { published: !existingNews.published },
include: { author: true }
});
return news;
},
toggleNewsFeatured: async (_: any, { id }: { id: string }, context: any) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
const existingNews = await prisma.news.findUnique({
where: { id }
});
if (!existingNews) {
throw new Error('News not found');
}
const news = await prisma.news.update({
where: { id },
data: { featured: !existingNews.featured },
include: { author: true }
});
return news;
},
incrementNewsViews: async (_: any, { id }: { id: string }) => {
const news = await prisma.news.update({
where: { id },
data: { views: { increment: 1 } },
include: { author: true }
});
return news;
},
likeNews: async (_: any, { id }: { id: string }) => {
const news = await prisma.news.update({
where: { id },
data: { likes: { increment: 1 } },
include: { author: true }
});
return news;
},
// Category mutations
createCategory: async (_: any, { input }: { input: any }, context: any) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
const category = await prisma.category.create({
data: input
});
return category;
},
updateCategory: async (_: any, { id, input }: { id: string; input: any }, context: any) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
const category = await prisma.category.update({
where: { id },
data: input
});
return category;
},
deleteCategory: async (_: any, { id }: { id: string }, context: any) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
await prisma.category.delete({
where: { id }
});
return true;
},
// User mutations
createUser: async (_: any, { input }: { input: any }, context: any) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
const hashedPassword = await bcrypt.hash(input.password, 12);
const user = await prisma.user.create({
data: {
...input,
password: hashedPassword
},
include: { news: true }
});
return user;
},
updateUser: async (_: any, { id, input }: { id: string; input: any }, context: any) => {
if (!context.user || (context.user.id !== id && context.user.role !== 'ADMIN')) {
throw new Error('Not authorized');
}
const updateData: any = { ...input };
if (input.password) {
updateData.password = await bcrypt.hash(input.password, 12);
}
const user = await prisma.user.update({
where: { id },
data: updateData,
include: { news: true }
});
return user;
},
deleteUser: async (_: any, { id }: { id: string }, context: any) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
await prisma.user.delete({
where: { id }
});
return true;
},
// Auth mutations
login: async (_: any, { email, password }: { email: string; password: string }) => {
const user = await prisma.user.findUnique({
where: { email }
});
if (!user) {
throw new Error('Invalid credentials');
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
throw new Error('Invalid credentials');
}
const token = jwt.sign(
{ userId: user.id },
process.env.NEXTAUTH_SECRET || 'secret',
{ expiresIn: '7d' }
);
return {
token,
user
};
},
register: async (_: any, { input }: { input: any }) => {
const existingUser = await prisma.user.findFirst({
where: {
OR: [
{ email: input.email },
{ username: input.username }
]
}
});
if (existingUser) {
throw new Error('User already exists');
}
const hashedPassword = await bcrypt.hash(input.password, 12);
const user = await prisma.user.create({
data: {
...input,
password: hashedPassword
}
});
const token = jwt.sign(
{ userId: user.id },
process.env.NEXTAUTH_SECRET || 'secret',
{ expiresIn: '7d' }
);
return {
token,
user
};
},
logout: async () => {
// В GraphQL logout обычно обрабатывается на клиенте
// путем удаления токена из localStorage/cookies
return true;
}
}
};