100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { Router } from 'express';
|
|
import { authenticate } from '../../middleware/auth.middleware';
|
|
import { checkSocialEnabled } from './social.middleware';
|
|
import { achievementsService } from './achievements.service';
|
|
|
|
const router = Router();
|
|
|
|
router.use(authenticate, checkSocialEnabled);
|
|
|
|
/** GET /api/social/achievements — my achievements with progress */
|
|
router.get('/', async (req, res, next) => {
|
|
try {
|
|
const achievements = await achievementsService.listForUser(req.user!.id);
|
|
res.json({ achievements });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
/** GET /api/social/achievements/definitions — all achievement definitions */
|
|
router.get('/definitions', async (_req, res) => {
|
|
res.json({ achievements: achievementsService.getDefinitions() });
|
|
});
|
|
|
|
/** GET /api/social/achievements/stats — my volunteer stats */
|
|
router.get('/stats', async (req, res, next) => {
|
|
try {
|
|
const stats = await achievementsService.getVolunteerStats(req.user!.id);
|
|
res.json({ stats });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
/** GET /api/social/achievements/stats/:userId — another user's stats (privacy-filtered) */
|
|
router.get('/stats/:userId', async (req, res, next) => {
|
|
try {
|
|
const targetUserId = req.params.userId as string;
|
|
|
|
// Privacy check
|
|
const privacy = await (await import('../../config/database')).prisma.privacySettings.findUnique({
|
|
where: { userId: targetUserId },
|
|
});
|
|
|
|
if (privacy?.showInFriendActivity === false && targetUserId !== req.user!.id) {
|
|
return res.json({ stats: null });
|
|
}
|
|
|
|
const stats = await achievementsService.getVolunteerStats(targetUserId);
|
|
res.json({ stats });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
/** GET /api/social/achievements/user/:userId — another user's achievements */
|
|
router.get('/user/:userId', async (req, res, next) => {
|
|
try {
|
|
const targetUserId = req.params.userId as string;
|
|
|
|
// Privacy check
|
|
const privacy = await (await import('../../config/database')).prisma.privacySettings.findUnique({
|
|
where: { userId: targetUserId },
|
|
});
|
|
|
|
if (privacy?.showInFriendActivity === false && targetUserId !== req.user!.id) {
|
|
return res.json({ achievements: [] });
|
|
}
|
|
|
|
const achievements = await achievementsService.listForUser(targetUserId);
|
|
// Only return unlocked achievements for other users
|
|
const filtered = targetUserId === req.user!.id
|
|
? achievements
|
|
: achievements.filter((a) => a.unlocked);
|
|
res.json({ achievements: filtered });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
/** GET /api/social/achievements/leaderboard — leaderboard */
|
|
router.get('/leaderboard', async (req, res, next) => {
|
|
try {
|
|
const type = (req.query.type as string) || 'canvass';
|
|
if (!['canvass', 'shifts', 'campaigns'].includes(type)) {
|
|
return res.status(400).json({ error: { message: 'Invalid leaderboard type' } });
|
|
}
|
|
|
|
const limit = Math.min(parseInt(req.query.limit as string) || 10, 50);
|
|
const leaderboard = await achievementsService.getLeaderboard(type as 'canvass' | 'shifts' | 'campaigns', limit);
|
|
const myRank = await achievementsService.getUserRank(req.user!.id, type as 'canvass' | 'shifts' | 'campaigns');
|
|
|
|
res.json({ leaderboard, myRank, type });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
export const achievementsRouter = router;
|