// src/routes/gamification.ts import express, { NextFunction } from 'express'; import passport, { isAdmin } from './passport'; import { getAllAchievements, getUserAchievements, awardAchievement, getLeaderboard } from '../services/db/index.db'; import { logger } from '../services/logger.server'; import { UserProfile } from '../types'; const router = express.Router(); /** * GET /api/achievements - Get the master list of all available achievements. * This is a public endpoint. */ router.get('/', async (req, res, next: NextFunction) => { try { const achievements = await getAllAchievements(); res.json(achievements); } catch (error) { logger.error('Error fetching all achievements:', { error }); next(error); } }); /** * GET /api/achievements/leaderboard - Get the top users by points. * This is a public endpoint. */ router.get('/leaderboard', async (req, res, next: NextFunction) => { // Allow client to specify a limit, but default to 10 and cap it at 50. const limit = Math.min(parseInt(req.query.limit as string, 10) || 10, 50); try { const leaderboard = await getLeaderboard(limit); res.json(leaderboard); } catch (error) { logger.error('Error fetching leaderboard:', { error }); next(error); } }); /** * GET /api/achievements/me - Get all achievements for the authenticated user. * This is a protected endpoint. */ router.get( '/me', passport.authenticate('jwt', { session: false }), async (req, res, next: NextFunction) => { const user = req.user as UserProfile; try { const userAchievements = await getUserAchievements(user.user_id); res.json(userAchievements); } catch (error) { logger.error('Error fetching user achievements:', { error, userId: user.user_id }); next(error); } } ); /** * POST /api/achievements/award - Manually award an achievement to a user. * This is an admin-only endpoint. */ router.post( '/award', passport.authenticate('jwt', { session: false }), isAdmin, async (req, res, next: NextFunction) => { const { userId, achievementName } = req.body; if (!userId || !achievementName) { return res.status(400).json({ message: 'Both userId and achievementName are required.' }); } try { await awardAchievement(userId, achievementName); res.status(200).json({ message: `Successfully awarded '${achievementName}' to user ${userId}.` }); } catch (error) { logger.error('Error awarding achievement via admin endpoint:', { error, userId, achievementName }); next(error); } } ); export default router;