Added settings for user data deletion
This commit is contained in:
parent
59e15cfde8
commit
f49490042a
35 changed files with 2758 additions and 499 deletions
229
backend/src/routes/users.ts
Normal file
229
backend/src/routes/users.ts
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import { Router, Response } from 'express';
|
||||
import { prisma } from '../index';
|
||||
import { authenticate, AuthRequest } from '../middleware/auth';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/me', authenticate, async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: req.user!.id },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
screenName: true,
|
||||
avatarUrl: true,
|
||||
createdAt: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
console.error('Get user error:', error);
|
||||
res.status(500).json({ error: 'Failed to get user' });
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/me', authenticate, async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { name, screenName, avatarUrl } = req.body;
|
||||
|
||||
const updated = await prisma.user.update({
|
||||
where: { id: req.user!.id },
|
||||
data: {
|
||||
name: name || undefined,
|
||||
screenName: screenName !== undefined ? screenName || null : undefined,
|
||||
avatarUrl: avatarUrl !== undefined ? avatarUrl || null : undefined
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
screenName: true,
|
||||
avatarUrl: true,
|
||||
createdAt: true
|
||||
}
|
||||
});
|
||||
|
||||
res.json(updated);
|
||||
} catch (error) {
|
||||
console.error('Update user error:', error);
|
||||
res.status(500).json({ error: 'Failed to update user' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/me/location-history', authenticate, async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const locations = await prisma.locationHistory.findMany({
|
||||
where: { userId: req.user!.id },
|
||||
include: {
|
||||
game: {
|
||||
select: { id: true, name: true }
|
||||
}
|
||||
},
|
||||
orderBy: { recordedAt: 'desc' }
|
||||
});
|
||||
|
||||
const games = await prisma.game.findMany({
|
||||
where: {
|
||||
teams: {
|
||||
some: {
|
||||
members: {
|
||||
some: { userId: req.user!.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
select: { id: true, name: true }
|
||||
});
|
||||
|
||||
const locationByGame = games.map(game => {
|
||||
const gameLocations = locations.filter(l => l.gameId === game.id);
|
||||
return {
|
||||
game: game,
|
||||
locations: gameLocations,
|
||||
locationCount: gameLocations.length
|
||||
};
|
||||
}).filter(g => g.locationCount > 0);
|
||||
|
||||
res.json({
|
||||
totalLocations: locations.length,
|
||||
byGame: locationByGame
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Get location history error:', error);
|
||||
res.status(500).json({ error: 'Failed to get location history' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/me/games', authenticate, async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const memberships = await prisma.teamMember.findMany({
|
||||
where: { userId: req.user!.id },
|
||||
include: {
|
||||
team: {
|
||||
include: {
|
||||
game: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
status: true,
|
||||
startDate: true,
|
||||
locationLat: true,
|
||||
locationLng: true,
|
||||
gameMasterId: true,
|
||||
gameMaster: { select: { name: true } }
|
||||
}
|
||||
},
|
||||
teamRoutes: {
|
||||
include: {
|
||||
route: {
|
||||
include: {
|
||||
routeLegs: {
|
||||
orderBy: { sequenceNumber: 'asc' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
photoSubmissions: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const gamesWithDetails = memberships.map(m => {
|
||||
const team = m.team;
|
||||
const game = team.game;
|
||||
const teamRoute = team.teamRoutes[0];
|
||||
const route = teamRoute?.route;
|
||||
const photoSubmissions = team.photoSubmissions;
|
||||
|
||||
const routeLegs = route?.routeLegs || [];
|
||||
const proofLocations = routeLegs.filter(leg =>
|
||||
photoSubmissions.some(p => p.routeLegId === leg.id)
|
||||
);
|
||||
|
||||
let totalDistance = 0;
|
||||
if (game.locationLat && game.locationLng) {
|
||||
let prevLat = game.locationLat;
|
||||
let prevLng = game.locationLng;
|
||||
for (const leg of routeLegs) {
|
||||
if (leg.locationLat && leg.locationLng) {
|
||||
const R = 6371;
|
||||
const dLat = (leg.locationLat - prevLat) * Math.PI / 180;
|
||||
const dLng = (leg.locationLng - prevLng) * Math.PI / 180;
|
||||
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
||||
Math.cos(prevLat * Math.PI / 180) * Math.cos(leg.locationLat * Math.PI / 180) *
|
||||
Math.sin(dLng/2) * Math.sin(dLng/2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
totalDistance += R * c;
|
||||
prevLat = leg.locationLat;
|
||||
prevLng = leg.locationLng;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
gameId: game.id,
|
||||
gameName: game.name,
|
||||
gameStatus: game.status,
|
||||
gameMaster: game.gameMaster.name,
|
||||
startDate: game.startDate,
|
||||
teamId: team.id,
|
||||
teamName: team.name,
|
||||
teamStatus: team.status,
|
||||
routeId: route?.id || null,
|
||||
routeName: route?.name || null,
|
||||
routeColor: route?.color || null,
|
||||
totalLegs: routeLegs.length,
|
||||
totalDistance: Math.round(totalDistance * 100) / 100,
|
||||
proofLocations: proofLocations.map(leg => ({
|
||||
legNumber: leg.sequenceNumber,
|
||||
description: leg.description,
|
||||
locationLat: leg.locationLat,
|
||||
locationLng: leg.locationLng,
|
||||
hasPhotoProof: photoSubmissions.some(p => p.routeLegId === leg.id)
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
res.json(gamesWithDetails);
|
||||
} catch (error) {
|
||||
console.error('Get user games error:', error);
|
||||
res.status(500).json({ error: 'Failed to get user games' });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/me/location-data', authenticate, async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
await prisma.locationHistory.deleteMany({
|
||||
where: { userId: req.user!.id }
|
||||
});
|
||||
|
||||
res.json({ message: 'Location data deleted' });
|
||||
} catch (error) {
|
||||
console.error('Delete location data error:', error);
|
||||
res.status(500).json({ error: 'Failed to delete location data' });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/me/account', authenticate, async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
await prisma.user.delete({
|
||||
where: { id: req.user!.id }
|
||||
});
|
||||
|
||||
res.json({ message: 'Account deleted' });
|
||||
} catch (error) {
|
||||
console.error('Delete account error:', error);
|
||||
res.status(500).json({ error: 'Failed to delete account' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Loading…
Add table
Add a link
Reference in a new issue