All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m28s
- 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.
85 lines
2.5 KiB
TypeScript
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; |