import { Router } from 'express'; import { prisma } from '../../config/database.js'; import { requireModerator, requireAdmin } from '../../middleware/requireRole.js'; import { validate } from '../../middleware/validate.js'; import { rejectListingSchema } from '../../validators/admin.js'; import { AppError } from '../../middleware/errorHandler.js'; const router = Router(); // GET /api/admin/listings - All listings router.get('/', requireModerator, async (req, res, next) => { try { const { page = '1', pageSize = '20', status, category, search } = req.query; const skip = (parseInt(page as string) - 1) * parseInt(pageSize as string); const take = parseInt(pageSize as string); const where: any = {}; if (status) where.status = status; if (category) where.category = category; if (search) { where.OR = [ { title: { contains: search as string, mode: 'insensitive' } }, { description: { contains: search as string, mode: 'insensitive' } }, ]; } const [listings, total] = await Promise.all([ prisma.listing.findMany({ where, select: { id: true, title: true, price: true, category: true, condition: true, status: true, isFeatured: true, createdAt: true, viewCount: true, seller: { select: { id: true, fullName: true, avatar: true } }, images: { take: 1, orderBy: { order: 'asc' } }, _count: { select: { offers: true, favorites: true } }, }, skip, take, orderBy: { createdAt: 'desc' }, }), prisma.listing.count({ where }), ]); res.json({ data: listings, total, page: parseInt(page as string), pageSize: take, totalPages: Math.ceil(total / take), }); } catch (error) { next(error); } }); // POST /api/admin/listings/:id/approve router.post('/:id/approve', requireModerator, async (req, res, next) => { try { const listing = await prisma.listing.findUnique({ where: { id: req.params.id } }); if (!listing) throw new AppError(404, 'Listing not found'); if (listing.status !== 'PENDING_REVIEW') throw new AppError(400, 'Listing is not pending review'); const updated = await prisma.listing.update({ where: { id: req.params.id }, data: { status: 'ACTIVE', reviewedBy: req.userId, reviewedAt: new Date() }, }); await prisma.moderationLog.create({ data: { moderatorId: req.userId!, targetListingId: req.params.id, action: 'APPROVED', reason: 'Listing approved', }, }); await prisma.notification.create({ data: { userId: listing.sellerId, type: 'LISTING_APPROVED', title: 'Listing Approved', body: `Your listing "${listing.title}" has been approved and is now live.`, data: { listingId: listing.id }, }, }); res.json(updated); } catch (error) { next(error); } }); // POST /api/admin/listings/:id/reject router.post('/:id/reject', requireModerator, validate(rejectListingSchema), async (req, res, next) => { try { const listing = await prisma.listing.findUnique({ where: { id: req.params.id } }); if (!listing) throw new AppError(404, 'Listing not found'); const updated = await prisma.listing.update({ where: { id: req.params.id }, data: { status: 'DELETED', rejectionReason: req.body.reason, reviewedBy: req.userId, reviewedAt: new Date(), }, }); await prisma.moderationLog.create({ data: { moderatorId: req.userId!, targetListingId: req.params.id, action: 'REJECTED', reason: req.body.reason, }, }); await prisma.notification.create({ data: { userId: listing.sellerId, type: 'LISTING_REJECTED', title: 'Listing Rejected', body: `Your listing "${listing.title}" was rejected. Reason: ${req.body.reason}`, data: { listingId: listing.id }, }, }); res.json(updated); } catch (error) { next(error); } }); // DELETE /api/admin/listings/:id - Force delete router.delete('/:id', requireAdmin, async (req, res, next) => { try { const listing = await prisma.listing.findUnique({ where: { id: req.params.id } }); if (!listing) throw new AppError(404, 'Listing not found'); await prisma.listing.update({ where: { id: req.params.id }, data: { status: 'DELETED' }, }); await prisma.moderationLog.create({ data: { moderatorId: req.userId!, targetListingId: req.params.id, targetUserId: listing.sellerId, action: 'LISTING_DELETED', reason: 'Force deleted by admin', }, }); res.json({ message: 'Listing deleted' }); } catch (error) { next(error); } }); // POST /api/admin/listings/:id/feature - Toggle featured router.post('/:id/feature', requireAdmin, async (req, res, next) => { try { const listing = await prisma.listing.findUnique({ where: { id: req.params.id } }); if (!listing) throw new AppError(404, 'Listing not found'); const updated = await prisma.listing.update({ where: { id: req.params.id }, data: { isFeatured: !listing.isFeatured }, }); await prisma.moderationLog.create({ data: { moderatorId: req.userId!, targetListingId: req.params.id, action: 'LISTING_FEATURED', reason: updated.isFeatured ? 'Listing featured' : 'Listing unfeatured', }, }); res.json(updated); } catch (error) { next(error); } }); export default router;