Initial commit: HotWives Platform completa
- Backend completo com Express, TypeScript e Prisma - Sistema de autenticação JWT - API REST com todas as funcionalidades - Sistema de mensagens e chat em tempo real (Socket.io) - Upload e gerenciamento de fotos - Sistema de perfis com verificação - Busca avançada com filtros - Sistema de eventos - Dashboard administrativo - Frontend Next.js 14 com TypeScript - Design moderno com Tailwind CSS - Componentes UI com Radix UI - Tema dark/light - Configuração Nginx pronta para produção - Scripts de instalação e deploy - Documentação completa
This commit is contained in:
357
backend/src/controllers/event.controller.ts
Normal file
357
backend/src/controllers/event.controller.ts
Normal file
@@ -0,0 +1,357 @@
|
||||
import { Response } from 'express';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { AuthRequest } from '../middleware/auth.middleware';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export const getEvents = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { page = 1, limit = 20, upcoming = 'true' } = req.query;
|
||||
const skip = (Number(page) - 1) * Number(limit);
|
||||
|
||||
const where: any = { status: 'PUBLISHED' };
|
||||
|
||||
if (upcoming === 'true') {
|
||||
where.date = { gte: new Date() };
|
||||
}
|
||||
|
||||
const [events, total] = await Promise.all([
|
||||
prisma.event.findMany({
|
||||
where,
|
||||
skip,
|
||||
take: Number(limit),
|
||||
include: {
|
||||
creator: {
|
||||
include: { profile: true }
|
||||
},
|
||||
participants: {
|
||||
where: { status: 'approved' },
|
||||
include: {
|
||||
user: {
|
||||
include: { profile: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: { date: 'asc' }
|
||||
}),
|
||||
prisma.event.count({ where })
|
||||
]);
|
||||
|
||||
res.json({
|
||||
events,
|
||||
pagination: {
|
||||
page: Number(page),
|
||||
limit: Number(limit),
|
||||
total,
|
||||
pages: Math.ceil(total / Number(limit))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar eventos:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar eventos' });
|
||||
}
|
||||
};
|
||||
|
||||
export const getEvent = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
creator: {
|
||||
include: { profile: true }
|
||||
},
|
||||
participants: {
|
||||
include: {
|
||||
user: {
|
||||
include: { profile: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Evento não encontrado' });
|
||||
}
|
||||
|
||||
// Verificar se é privado e se o usuário tem acesso
|
||||
if (event.isPrivate && event.creatorId !== req.userId) {
|
||||
const isParticipant = event.participants.some(
|
||||
p => p.userId === req.userId && p.status === 'approved'
|
||||
);
|
||||
|
||||
if (!isParticipant) {
|
||||
return res.status(403).json({ error: 'Acesso negado' });
|
||||
}
|
||||
}
|
||||
|
||||
res.json(event);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar evento:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar evento' });
|
||||
}
|
||||
};
|
||||
|
||||
export const createEvent = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
date,
|
||||
endDate,
|
||||
location,
|
||||
city,
|
||||
state,
|
||||
address,
|
||||
maxParticipants,
|
||||
isPrivate,
|
||||
requiresApproval,
|
||||
tags,
|
||||
price
|
||||
} = req.body;
|
||||
|
||||
let coverImage = null;
|
||||
if (req.file) {
|
||||
coverImage = `/uploads/photos/${req.file.filename}`;
|
||||
}
|
||||
|
||||
const event = await prisma.event.create({
|
||||
data: {
|
||||
creatorId: req.userId!,
|
||||
title,
|
||||
description,
|
||||
date: new Date(date),
|
||||
endDate: endDate ? new Date(endDate) : null,
|
||||
location,
|
||||
city,
|
||||
state,
|
||||
address,
|
||||
coverImage,
|
||||
maxParticipants: maxParticipants ? Number(maxParticipants) : null,
|
||||
isPrivate: isPrivate === 'true',
|
||||
requiresApproval: requiresApproval !== 'false',
|
||||
tags: tags ? JSON.parse(tags) : [],
|
||||
price: price ? parseFloat(price) : null,
|
||||
status: 'PUBLISHED'
|
||||
},
|
||||
include: {
|
||||
creator: {
|
||||
include: { profile: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(event);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar evento:', error);
|
||||
res.status(500).json({ error: 'Erro ao criar evento' });
|
||||
}
|
||||
};
|
||||
|
||||
export const updateEvent = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Evento não encontrado' });
|
||||
}
|
||||
|
||||
if (event.creatorId !== req.userId) {
|
||||
return res.status(403).json({ error: 'Acesso negado' });
|
||||
}
|
||||
|
||||
const updateData: any = { ...req.body };
|
||||
|
||||
if (req.body.date) {
|
||||
updateData.date = new Date(req.body.date);
|
||||
}
|
||||
|
||||
if (req.body.endDate) {
|
||||
updateData.endDate = new Date(req.body.endDate);
|
||||
}
|
||||
|
||||
if (req.file) {
|
||||
updateData.coverImage = `/uploads/photos/${req.file.filename}`;
|
||||
}
|
||||
|
||||
if (req.body.tags) {
|
||||
updateData.tags = JSON.parse(req.body.tags);
|
||||
}
|
||||
|
||||
const updatedEvent = await prisma.event.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
include: {
|
||||
creator: {
|
||||
include: { profile: true }
|
||||
},
|
||||
participants: true
|
||||
}
|
||||
});
|
||||
|
||||
res.json(updatedEvent);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar evento:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar evento' });
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteEvent = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Evento não encontrado' });
|
||||
}
|
||||
|
||||
if (event.creatorId !== req.userId) {
|
||||
return res.status(403).json({ error: 'Acesso negado' });
|
||||
}
|
||||
|
||||
await prisma.event.delete({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
res.json({ message: 'Evento deletado com sucesso' });
|
||||
} catch (error) {
|
||||
console.error('Erro ao deletar evento:', error);
|
||||
res.status(500).json({ error: 'Erro ao deletar evento' });
|
||||
}
|
||||
};
|
||||
|
||||
export const joinEvent = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { message } = req.body;
|
||||
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
participants: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Evento não encontrado' });
|
||||
}
|
||||
|
||||
// Verificar se já está participando
|
||||
const existing = await prisma.eventParticipant.findUnique({
|
||||
where: {
|
||||
eventId_userId: {
|
||||
eventId: id,
|
||||
userId: req.userId!
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
return res.status(400).json({ error: 'Você já está participando deste evento' });
|
||||
}
|
||||
|
||||
// Verificar limite de participantes
|
||||
if (event.maxParticipants) {
|
||||
const approvedCount = event.participants.filter(p => p.status === 'approved').length;
|
||||
if (approvedCount >= event.maxParticipants) {
|
||||
return res.status(400).json({ error: 'Evento lotado' });
|
||||
}
|
||||
}
|
||||
|
||||
const status = event.requiresApproval ? 'pending' : 'approved';
|
||||
|
||||
const participant = await prisma.eventParticipant.create({
|
||||
data: {
|
||||
eventId: id,
|
||||
userId: req.userId!,
|
||||
status,
|
||||
message
|
||||
}
|
||||
});
|
||||
|
||||
// Notificar criador
|
||||
await prisma.notification.create({
|
||||
data: {
|
||||
userId: event.creatorId,
|
||||
type: 'event',
|
||||
title: 'Nova Solicitação de Participação',
|
||||
message: `Alguém quer participar do seu evento: ${event.title}`,
|
||||
link: `/events/${id}`
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(participant);
|
||||
} catch (error) {
|
||||
console.error('Erro ao participar do evento:', error);
|
||||
res.status(500).json({ error: 'Erro ao participar do evento' });
|
||||
}
|
||||
};
|
||||
|
||||
export const leaveEvent = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
await prisma.eventParticipant.deleteMany({
|
||||
where: {
|
||||
eventId: id,
|
||||
userId: req.userId
|
||||
}
|
||||
});
|
||||
|
||||
res.json({ message: 'Você saiu do evento' });
|
||||
} catch (error) {
|
||||
console.error('Erro ao sair do evento:', error);
|
||||
res.status(500).json({ error: 'Erro ao sair do evento' });
|
||||
}
|
||||
};
|
||||
|
||||
export const updateParticipantStatus = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id, participantId } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Evento não encontrado' });
|
||||
}
|
||||
|
||||
if (event.creatorId !== req.userId) {
|
||||
return res.status(403).json({ error: 'Apenas o criador pode aprovar participantes' });
|
||||
}
|
||||
|
||||
const participant = await prisma.eventParticipant.update({
|
||||
where: { id: participantId },
|
||||
data: { status }
|
||||
});
|
||||
|
||||
// Notificar participante
|
||||
await prisma.notification.create({
|
||||
data: {
|
||||
userId: participant.userId,
|
||||
type: 'event',
|
||||
title: status === 'approved' ? 'Participação Aprovada' : 'Participação Rejeitada',
|
||||
message: `Sua participação no evento "${event.title}" foi ${status === 'approved' ? 'aprovada' : 'rejeitada'}`,
|
||||
link: `/events/${id}`
|
||||
}
|
||||
});
|
||||
|
||||
res.json(participant);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar status do participante:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar status' });
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user