Files
flyer-crawler.projectium.com/src/routes/gamification.ts
Torben Sorensen 80d2b1ffe6
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m28s
Add user database service and unit tests
- Implement user database service with functions for user management (create, find, update, delete).
- Add comprehensive unit tests for user database service using Vitest.
- Mock database interactions to ensure isolated testing.
- Create setup files for unit tests to handle database connections and global mocks.
- Introduce error handling for unique constraints and foreign key violations.
- Enhance logging for better traceability during database operations.
2025-12-04 15:30:27 -08:00

85 lines
2.5 KiB
TypeScript

// 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;