import { Router } from 'express'; import { z } from 'zod'; import { socialAdminService } from './social-admin.service'; import { requireRole } from '../../middleware/rbac.middleware'; import { SOCIAL_ROLES } from '../../utils/roles'; import { logger } from '../../utils/logger'; const router = Router(); // Self-contained auth guard (defense-in-depth — parent also applies requireRole) router.use(requireRole(...SOCIAL_ROLES)); /** GET /api/social/admin/stats — Dashboard overview */ router.get('/stats', async (_req, res) => { try { const stats = await socialAdminService.getDashboardStats(); res.json(stats); } catch (err) { logger.error('Social admin stats error:', err); res.status(500).json({ error: 'Failed to fetch social stats' }); } }); /** GET /api/social/admin/graph — Full social graph */ router.get('/graph', async (req, res) => { try { const role = req.query.role as string | undefined; const search = req.query.search as string | undefined; const data = await socialAdminService.getGraphData({ role, search }); res.json(data); } catch (err) { logger.error('Social admin graph error:', err); res.status(500).json({ error: 'Failed to fetch social graph' }); } }); /** GET /api/social/admin/graph/ego/:userId — Ego-centric subgraph */ router.get('/graph/ego/:userId', async (req, res) => { try { const userId = req.params.userId as string; const maxHops = parseInt(req.query.maxHops as string) || 2; const data = await socialAdminService.getEgoGraphData(userId, Math.min(maxHops, 3)); res.json(data); } catch (err) { logger.error('Social admin ego graph error:', err); res.status(500).json({ error: 'Failed to fetch ego graph' }); } }); /** GET /api/social/admin/moderation — Moderation overview */ router.get('/moderation', async (req, res) => { try { const page = parseInt(req.query.page as string) || 1; const limit = Math.min(parseInt(req.query.limit as string) || 20, 100); const data = await socialAdminService.getModerationData(page, limit); res.json(data); } catch (err) { logger.error('Social admin moderation error:', err); res.status(500).json({ error: 'Failed to fetch moderation data' }); } }); /** POST /api/social/admin/blocks/:id/remove — Force-remove a block */ router.post('/blocks/:id/remove', async (req, res) => { try { const blockId = parseInt(req.params.id as string); if (isNaN(blockId)) return res.status(400).json({ error: 'Invalid block ID' }); await socialAdminService.removeBlock(blockId); res.json({ success: true }); } catch (err: unknown) { const statusCode = (err as { statusCode?: number }).statusCode ?? 500; const message = err instanceof Error ? err.message : 'Failed to remove block'; res.status(statusCode).json({ error: message }); } }); const achievementSchema = z.object({ userId: z.string().min(1).max(100), achievementId: z.string().min(1).max(100), }); /** POST /api/social/admin/achievements/grant — Grant achievement */ router.post('/achievements/grant', async (req, res) => { try { const parsed = achievementSchema.safeParse(req.body); if (!parsed.success) { return res.status(400).json({ error: 'Valid userId and achievementId are required' }); } const result = await socialAdminService.grantAchievement(parsed.data.userId, parsed.data.achievementId); res.json(result); } catch (err: unknown) { const statusCode = (err as { statusCode?: number }).statusCode ?? 500; const message = err instanceof Error ? err.message : 'Failed to grant achievement'; res.status(statusCode).json({ error: message }); } }); /** POST /api/social/admin/achievements/revoke — Revoke achievement */ router.post('/achievements/revoke', async (req, res) => { try { const parsed = achievementSchema.safeParse(req.body); if (!parsed.success) { return res.status(400).json({ error: 'Valid userId and achievementId are required' }); } await socialAdminService.revokeAchievement(parsed.data.userId, parsed.data.achievementId); res.json({ success: true }); } catch (err: unknown) { const statusCode = (err as { statusCode?: number }).statusCode ?? 500; const message = err instanceof Error ? err.message : 'Failed to revoke achievement'; res.status(statusCode).json({ error: message }); } }); export { router as socialAdminRouter };