1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
import express from 'express';
import { prisma } from '../config/db';
import { PAGE_SIZE } from '../config/constants';
export const handleScoreUpload = async (req: express.Request, res: express.Response) => {
try {
const { meta, scores } = req.body;
const userId = req.session.userId;
if (!userId) {
return res.status(401).json({ error: 'Unauthorized. Please log in to upload scores.' });
}
// Basic universal validation
if (!meta || !meta.game || !meta.service || !scores) {
return res.status(400).json({ error: 'Invalid request format. Expected meta with game/service and scores array' });
}
let game = await prisma.game.findUnique({
where: { internalName: meta.game }
});
if (!game) {
game = await prisma.game.findFirst({
where: { formattedName: meta.game }
});
}
if (!game) {
return res.status(400).json({ error: `Game '${meta.game}' is not supported. Ensure that you are using the case-sensitive version of either the internal name or formatted name` });
}
const internalGameName = game.internalName;
const scoresArray = Array.isArray(scores) ? scores : [scores];
const scoresToCreate = [];
let skippedCount = 0;
for (const scoreData of scoresArray) {
// Check if exact same score data already exists
const existingScore = await prisma.score.findFirst({
where: {
gameInternalName: internalGameName,
userId: userId,
data: {
equals: scoreData
}
}
});
if (existingScore) {
skippedCount++;
} else {
scoresToCreate.push({
gameInternalName: internalGameName,
userId: userId,
timestamp: scoreData.timestamp,
data: scoreData
});
}
}
const createdScores = scoresToCreate.length > 0
? await prisma.score.createMany({
data: scoresToCreate
})
: { count: 0 };
res.status(200).json({
message: 'Score upload processed successfully',
game: meta.game,
service: meta.service,
scoreCount: createdScores.count,
skippedCount: skippedCount,
totalProcessed: scoresArray.length
});
} catch (error) {
console.error('Score upload endpoint error:', error);
res.status(500).json({ error: 'Internal server error. Unable to process score upload' });
}
}
export const handleGetScores = async (req: express.Request, res: express.Response) => {
try {
const { userId, internalGameName, pageNum, sortKey, direction } = req.query;
if (!userId || !internalGameName || !pageNum) {
return res.status(400).json({ error: 'Missing required parameters' });
}
const pageNumber = parseInt(pageNum as string);
const gameInternalName = internalGameName as string;
const userIdNumber = parseInt(userId as string);
const sortKeyString = (sortKey as string) || 'timestamp';
const directionString = ((direction as string)?.toLowerCase() === 'asc') ? 'asc' : 'desc';
const num_pages = Math.ceil(await prisma.score.count({
where: {
gameInternalName,
userId: userIdNumber
}
}) / PAGE_SIZE);
let scores;
if (sortKeyString === 'timestamp') {
scores = await prisma.score.findMany({
where: {
gameInternalName,
userId: userIdNumber
},
orderBy: {
timestamp: directionString
},
skip: (pageNumber - 1) * PAGE_SIZE,
take: PAGE_SIZE
});
} else if (sortKeyString === 'score') {
// raw SQL for JSON field ordering
scores = await prisma.$queryRawUnsafe<any[]>(`
SELECT * FROM "Score"
WHERE "gameInternalName" = $1 AND "userId" = $2
ORDER BY (data->>'score')::numeric ${directionString.toUpperCase()}
OFFSET $3
LIMIT $4
`, gameInternalName, userIdNumber, (pageNumber - 1) * PAGE_SIZE, PAGE_SIZE);
} else {
return res.status(400).json({ error: 'Invalid sort key' });
}
const safeScores = scores.map(score => ({
...score,
timestamp: typeof score.timestamp === 'bigint'
? Number(score.timestamp)
: score.timestamp
}));
res.status(200).json({
scores: safeScores,
num_pages
});
} catch (error) {
console.error('Failed to fetch scores:', error);
res.status(500).json({ error: 'Internal server error. Unable to fetch scores' });
}
};
|