@opprs/db-prisma 2.2.1-canary.2db0cbb → 2.2.1-canary.3a435d6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +12 -18
- package/dist/index.cjs +814 -158
- package/dist/index.d.cts +640 -107
- package/dist/index.d.ts +640 -107
- package/dist/index.js +738 -141
- package/package.json +4 -3
- package/prisma/migrations/20260104000000_add_player_number/migration.sql +23 -0
- package/prisma/migrations/20260104092800_add_location_model_and_tournament_fields/migration.sql +45 -0
- package/prisma/migrations/20260104210034_add_policy_acceptance_fields/migration.sql +19 -0
- package/prisma/migrations/20260104231435_split_entries_standings/migration.sql +137 -0
- package/prisma/migrations/20260105000000_add_oppr_ranking_models/migration.sql +108 -0
- package/prisma/migrations/20260105010000_add_qualifying_format/migration.sql +5 -0
- package/prisma/migrations/20260105010000_add_tournament_external_url/migration.sql +2 -0
- package/prisma/migrations/20260105020000_add_blog_post_model/migration.sql +81 -0
- package/prisma/migrations/20260109000000_remove_entry_match_round_models/migration.sql +26 -0
- package/prisma/schema.prisma +239 -38
- package/prisma/seed.ts +145 -56
package/dist/index.cjs
CHANGED
|
@@ -20,65 +20,124 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
MAX_API_KEYS_PER_USER: () => MAX_API_KEYS_PER_USER,
|
|
24
|
+
applyRDDecayForInactivePlayers: () => applyRDDecayForInactivePlayers,
|
|
23
25
|
connect: () => connect,
|
|
26
|
+
countBlogPosts: () => countBlogPosts,
|
|
27
|
+
countBlogTags: () => countBlogTags,
|
|
28
|
+
countLocations: () => countLocations,
|
|
29
|
+
countOpprPlayerRankings: () => countOpprPlayerRankings,
|
|
30
|
+
countOpprRankingHistory: () => countOpprRankingHistory,
|
|
24
31
|
countPlayers: () => countPlayers,
|
|
25
|
-
|
|
32
|
+
countPublishedBlogPosts: () => countPublishedBlogPosts,
|
|
33
|
+
countStandings: () => countStandings,
|
|
26
34
|
countTournaments: () => countTournaments,
|
|
35
|
+
countUserApiKeys: () => countUserApiKeys,
|
|
27
36
|
countUsers: () => countUsers,
|
|
28
|
-
|
|
37
|
+
createApiKey: () => createApiKey,
|
|
38
|
+
createBlogPost: () => createBlogPost,
|
|
39
|
+
createBlogTag: () => createBlogTag,
|
|
40
|
+
createLocation: () => createLocation,
|
|
41
|
+
createManyStandings: () => createManyStandings,
|
|
42
|
+
createOpprPlayerRanking: () => createOpprPlayerRanking,
|
|
43
|
+
createOpprRankingHistory: () => createOpprRankingHistory,
|
|
29
44
|
createPlayer: () => createPlayer,
|
|
30
|
-
|
|
45
|
+
createStanding: () => createStanding,
|
|
31
46
|
createTournament: () => createTournament,
|
|
32
47
|
createUser: () => createUser,
|
|
33
48
|
createUserWithPlayer: () => createUserWithPlayer,
|
|
49
|
+
deleteApiKey: () => deleteApiKey,
|
|
50
|
+
deleteBlogPost: () => deleteBlogPost,
|
|
51
|
+
deleteBlogTag: () => deleteBlogTag,
|
|
52
|
+
deleteLocation: () => deleteLocation,
|
|
53
|
+
deleteOpprPlayerRanking: () => deleteOpprPlayerRanking,
|
|
34
54
|
deletePlayer: () => deletePlayer,
|
|
35
|
-
|
|
36
|
-
|
|
55
|
+
deleteStanding: () => deleteStanding,
|
|
56
|
+
deleteStandingsByTournament: () => deleteStandingsByTournament,
|
|
37
57
|
deleteTournament: () => deleteTournament,
|
|
38
58
|
deleteUser: () => deleteUser,
|
|
59
|
+
deleteUserApiKey: () => deleteUserApiKey,
|
|
39
60
|
disconnect: () => disconnect,
|
|
61
|
+
findApiKeyById: () => findApiKeyById,
|
|
62
|
+
findApiKeysByPrefix: () => findApiKeysByPrefix,
|
|
63
|
+
findBlogPostById: () => findBlogPostById,
|
|
64
|
+
findBlogPostBySlug: () => findBlogPostBySlug,
|
|
65
|
+
findBlogPosts: () => findBlogPosts,
|
|
66
|
+
findBlogTagById: () => findBlogTagById,
|
|
67
|
+
findBlogTagBySlug: () => findBlogTagBySlug,
|
|
68
|
+
findBlogTags: () => findBlogTags,
|
|
69
|
+
findLocationByExternalId: () => findLocationByExternalId,
|
|
70
|
+
findLocationById: () => findLocationById,
|
|
71
|
+
findLocations: () => findLocations,
|
|
72
|
+
findOpprPlayerRankingById: () => findOpprPlayerRankingById,
|
|
73
|
+
findOpprPlayerRankingByPlayerId: () => findOpprPlayerRankingByPlayerId,
|
|
74
|
+
findOpprPlayerRankings: () => findOpprPlayerRankings,
|
|
40
75
|
findPlayerByExternalId: () => findPlayerByExternalId,
|
|
41
76
|
findPlayerById: () => findPlayerById,
|
|
77
|
+
findPlayerByPlayerNumber: () => findPlayerByPlayerNumber,
|
|
42
78
|
findPlayerByUserEmail: () => findPlayerByUserEmail,
|
|
43
79
|
findPlayers: () => findPlayers,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
80
|
+
findPublishedBlogPosts: () => findPublishedBlogPosts,
|
|
81
|
+
findStandingById: () => findStandingById,
|
|
82
|
+
findStandingByPlayerAndTournament: () => findStandingByPlayerAndTournament,
|
|
83
|
+
findStandings: () => findStandings,
|
|
47
84
|
findTournamentByExternalId: () => findTournamentByExternalId,
|
|
48
85
|
findTournamentById: () => findTournamentById,
|
|
49
86
|
findTournaments: () => findTournaments,
|
|
50
87
|
findUserByEmail: () => findUserByEmail,
|
|
51
88
|
findUserById: () => findUserById,
|
|
52
89
|
findUsers: () => findUsers,
|
|
90
|
+
generateUniquePlayerNumber: () => generateUniquePlayerNumber,
|
|
91
|
+
getBlogTagWithPostCount: () => getBlogTagWithPostCount,
|
|
92
|
+
getBlogTagsWithPostCounts: () => getBlogTagsWithPostCounts,
|
|
93
|
+
getFinalsStandings: () => getFinalsStandings,
|
|
94
|
+
getLatestOpprRankingHistory: () => getLatestOpprRankingHistory,
|
|
95
|
+
getLocationWithTournaments: () => getLocationWithTournaments,
|
|
53
96
|
getMajorTournaments: () => getMajorTournaments,
|
|
54
|
-
|
|
97
|
+
getMergedStandings: () => getMergedStandings,
|
|
98
|
+
getOpprRankingHistory: () => getOpprRankingHistory,
|
|
99
|
+
getOpprRankingHistoryByDateRange: () => getOpprRankingHistoryByDateRange,
|
|
100
|
+
getOrCreateOpprPlayerRanking: () => getOrCreateOpprPlayerRanking,
|
|
101
|
+
getPlayerStandings: () => getPlayerStandings,
|
|
55
102
|
getPlayerStats: () => getPlayerStats,
|
|
56
103
|
getPlayerTopFinishes: () => getPlayerTopFinishes,
|
|
57
104
|
getPlayerWithResults: () => getPlayerWithResults,
|
|
58
|
-
|
|
105
|
+
getQualifyingStandings: () => getQualifyingStandings,
|
|
106
|
+
getRatedOpprPlayers: () => getRatedOpprPlayers,
|
|
59
107
|
getRecentTournaments: () => getRecentTournaments,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
108
|
+
getTopPlayersByOpprRanking: () => getTopPlayersByOpprRanking,
|
|
109
|
+
getTopPlayersByOpprRating: () => getTopPlayersByOpprRating,
|
|
110
|
+
getTournamentStandings: () => getTournamentStandings,
|
|
63
111
|
getTournamentStats: () => getTournamentStats,
|
|
64
112
|
getTournamentWithResults: () => getTournamentWithResults,
|
|
65
113
|
getTournamentsByBoosterType: () => getTournamentsByBoosterType,
|
|
66
114
|
getTournamentsByDateRange: () => getTournamentsByDateRange,
|
|
115
|
+
getUserApiKeys: () => getUserApiKeys,
|
|
67
116
|
getUserByEmailWithPlayer: () => getUserByEmailWithPlayer,
|
|
68
117
|
getUserWithPlayer: () => getUserWithPlayer,
|
|
118
|
+
isValidPlayerNumber: () => isValidPlayerNumber,
|
|
69
119
|
linkPlayerToUser: () => linkPlayerToUser,
|
|
70
120
|
prisma: () => prisma,
|
|
71
121
|
recalculateTimeDecay: () => recalculateTimeDecay,
|
|
122
|
+
searchBlogPosts: () => searchBlogPosts,
|
|
123
|
+
searchBlogTags: () => searchBlogTags,
|
|
124
|
+
searchLocations: () => searchLocations,
|
|
72
125
|
searchPlayers: () => searchPlayers,
|
|
73
126
|
searchTournaments: () => searchTournaments,
|
|
74
127
|
testConnection: () => testConnection,
|
|
128
|
+
updateApiKeyLastUsed: () => updateApiKeyLastUsed,
|
|
129
|
+
updateBlogPost: () => updateBlogPost,
|
|
130
|
+
updateBlogTag: () => updateBlogTag,
|
|
131
|
+
updateLocation: () => updateLocation,
|
|
132
|
+
updateOpprPlayerRanking: () => updateOpprPlayerRanking,
|
|
133
|
+
updateOpprRatingAfterTournament: () => updateOpprRatingAfterTournament,
|
|
75
134
|
updatePlayer: () => updatePlayer,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
updateResultPoints: () => updateResultPoints,
|
|
135
|
+
updateStanding: () => updateStanding,
|
|
136
|
+
updateStandingPoints: () => updateStandingPoints,
|
|
79
137
|
updateTournament: () => updateTournament,
|
|
80
138
|
updateUser: () => updateUser,
|
|
81
|
-
updateUserRefreshToken: () => updateUserRefreshToken
|
|
139
|
+
updateUserRefreshToken: () => updateUserRefreshToken,
|
|
140
|
+
updateWorldRankings: () => updateWorldRankings
|
|
82
141
|
});
|
|
83
142
|
module.exports = __toCommonJS(index_exports);
|
|
84
143
|
|
|
@@ -107,10 +166,40 @@ async function testConnection() {
|
|
|
107
166
|
}
|
|
108
167
|
}
|
|
109
168
|
|
|
169
|
+
// src/player-number.ts
|
|
170
|
+
var MIN_PLAYER_NUMBER = 1e4;
|
|
171
|
+
var MAX_PLAYER_NUMBER = 99999;
|
|
172
|
+
var MAX_RETRIES = 10;
|
|
173
|
+
function generateRandomPlayerNumber() {
|
|
174
|
+
return Math.floor(Math.random() * (MAX_PLAYER_NUMBER - MIN_PLAYER_NUMBER + 1)) + MIN_PLAYER_NUMBER;
|
|
175
|
+
}
|
|
176
|
+
async function generateUniquePlayerNumber() {
|
|
177
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
178
|
+
const candidate = generateRandomPlayerNumber();
|
|
179
|
+
const existing = await prisma.player.findUnique({
|
|
180
|
+
where: { playerNumber: candidate },
|
|
181
|
+
select: { id: true }
|
|
182
|
+
});
|
|
183
|
+
if (!existing) {
|
|
184
|
+
return candidate;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Failed to generate unique player number after ${MAX_RETRIES} attempts. Consider increasing the number range or implementing a different allocation strategy.`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
function isValidPlayerNumber(playerNumber) {
|
|
192
|
+
return Number.isInteger(playerNumber) && playerNumber >= MIN_PLAYER_NUMBER && playerNumber <= MAX_PLAYER_NUMBER;
|
|
193
|
+
}
|
|
194
|
+
|
|
110
195
|
// src/players.ts
|
|
111
196
|
async function createPlayer(data) {
|
|
197
|
+
const playerNumber = data.playerNumber ?? await generateUniquePlayerNumber();
|
|
112
198
|
return prisma.player.create({
|
|
113
|
-
data
|
|
199
|
+
data: {
|
|
200
|
+
...data,
|
|
201
|
+
playerNumber
|
|
202
|
+
}
|
|
114
203
|
});
|
|
115
204
|
}
|
|
116
205
|
async function findPlayerById(id, include) {
|
|
@@ -125,6 +214,12 @@ async function findPlayerByExternalId(externalId, include) {
|
|
|
125
214
|
include
|
|
126
215
|
});
|
|
127
216
|
}
|
|
217
|
+
async function findPlayerByPlayerNumber(playerNumber, include) {
|
|
218
|
+
return prisma.player.findUnique({
|
|
219
|
+
where: { playerNumber },
|
|
220
|
+
include
|
|
221
|
+
});
|
|
222
|
+
}
|
|
128
223
|
async function findPlayerByUserEmail(email, include) {
|
|
129
224
|
const user = await prisma.user.findUnique({
|
|
130
225
|
where: { email },
|
|
@@ -141,48 +236,12 @@ async function findPlayers(options = {}) {
|
|
|
141
236
|
include: options.include
|
|
142
237
|
});
|
|
143
238
|
}
|
|
144
|
-
async function getRatedPlayers(options = {}) {
|
|
145
|
-
return findPlayers({
|
|
146
|
-
...options,
|
|
147
|
-
where: { isRated: true }
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
async function getTopPlayersByRating(limit = 50) {
|
|
151
|
-
return findPlayers({
|
|
152
|
-
take: limit,
|
|
153
|
-
orderBy: { rating: "desc" },
|
|
154
|
-
where: { isRated: true }
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
async function getTopPlayersByRanking(limit = 50) {
|
|
158
|
-
return findPlayers({
|
|
159
|
-
take: limit,
|
|
160
|
-
orderBy: { ranking: "asc" },
|
|
161
|
-
where: {
|
|
162
|
-
isRated: true,
|
|
163
|
-
ranking: { not: null }
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
239
|
async function updatePlayer(id, data) {
|
|
168
240
|
return prisma.player.update({
|
|
169
241
|
where: { id },
|
|
170
242
|
data
|
|
171
243
|
});
|
|
172
244
|
}
|
|
173
|
-
async function updatePlayerRating(id, rating, ratingDeviation, eventCount) {
|
|
174
|
-
const updateData = {
|
|
175
|
-
rating,
|
|
176
|
-
ratingDeviation,
|
|
177
|
-
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
178
|
-
lastEventDate: /* @__PURE__ */ new Date()
|
|
179
|
-
};
|
|
180
|
-
if (eventCount !== void 0) {
|
|
181
|
-
updateData.eventCount = eventCount;
|
|
182
|
-
updateData.isRated = eventCount >= 5;
|
|
183
|
-
}
|
|
184
|
-
return updatePlayer(id, updateData);
|
|
185
|
-
}
|
|
186
245
|
async function deletePlayer(id) {
|
|
187
246
|
return prisma.player.delete({
|
|
188
247
|
where: { id }
|
|
@@ -195,7 +254,7 @@ async function getPlayerWithResults(id) {
|
|
|
195
254
|
const player = await prisma.player.findUnique({
|
|
196
255
|
where: { id },
|
|
197
256
|
include: {
|
|
198
|
-
|
|
257
|
+
standings: {
|
|
199
258
|
include: {
|
|
200
259
|
tournament: true
|
|
201
260
|
},
|
|
@@ -212,7 +271,7 @@ async function getPlayerWithResults(id) {
|
|
|
212
271
|
}
|
|
213
272
|
return {
|
|
214
273
|
...player,
|
|
215
|
-
results: player.
|
|
274
|
+
results: player.standings
|
|
216
275
|
};
|
|
217
276
|
}
|
|
218
277
|
async function searchPlayers(query, limit = 20) {
|
|
@@ -224,6 +283,212 @@ async function searchPlayers(query, limit = 20) {
|
|
|
224
283
|
});
|
|
225
284
|
}
|
|
226
285
|
|
|
286
|
+
// src/oppr-rankings.ts
|
|
287
|
+
async function getOrCreateOpprPlayerRanking(playerId) {
|
|
288
|
+
const existing = await prisma.opprPlayerRanking.findUnique({
|
|
289
|
+
where: { playerId }
|
|
290
|
+
});
|
|
291
|
+
if (existing) return existing;
|
|
292
|
+
return prisma.opprPlayerRanking.create({
|
|
293
|
+
data: { playerId }
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
async function createOpprPlayerRanking(data) {
|
|
297
|
+
return prisma.opprPlayerRanking.create({
|
|
298
|
+
data
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async function findOpprPlayerRankingById(id, include) {
|
|
302
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
303
|
+
where: { id },
|
|
304
|
+
include
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
async function findOpprPlayerRankingByPlayerId(playerId, include) {
|
|
308
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
309
|
+
where: { playerId },
|
|
310
|
+
include
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async function findOpprPlayerRankings(options = {}) {
|
|
314
|
+
return prisma.opprPlayerRanking.findMany({
|
|
315
|
+
take: options.take,
|
|
316
|
+
skip: options.skip,
|
|
317
|
+
where: options.where,
|
|
318
|
+
orderBy: options.orderBy,
|
|
319
|
+
include: options.include
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async function getTopPlayersByOpprRating(limit = 50) {
|
|
323
|
+
return prisma.opprPlayerRanking.findMany({
|
|
324
|
+
take: limit,
|
|
325
|
+
where: { isRated: true },
|
|
326
|
+
orderBy: { rating: "desc" },
|
|
327
|
+
include: { player: true }
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
async function getTopPlayersByOpprRanking(limit = 50) {
|
|
331
|
+
return prisma.opprPlayerRanking.findMany({
|
|
332
|
+
take: limit,
|
|
333
|
+
where: {
|
|
334
|
+
isRated: true,
|
|
335
|
+
ranking: { not: null }
|
|
336
|
+
},
|
|
337
|
+
orderBy: { ranking: "asc" },
|
|
338
|
+
include: { player: true }
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
async function getRatedOpprPlayers(options = {}) {
|
|
342
|
+
return prisma.opprPlayerRanking.findMany({
|
|
343
|
+
...options,
|
|
344
|
+
where: { isRated: true },
|
|
345
|
+
include: { player: true, ...options.include }
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async function updateOpprPlayerRanking(playerId, data) {
|
|
349
|
+
return prisma.opprPlayerRanking.update({
|
|
350
|
+
where: { playerId },
|
|
351
|
+
data: {
|
|
352
|
+
...data,
|
|
353
|
+
lastRatingUpdate: data.lastRatingUpdate ?? /* @__PURE__ */ new Date()
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async function updateOpprRatingAfterTournament(playerId, newRating, newRD, tournamentId, eventCount) {
|
|
358
|
+
const ranking = await getOrCreateOpprPlayerRanking(playerId);
|
|
359
|
+
const isRated = eventCount !== void 0 ? eventCount >= 5 : ranking.isRated;
|
|
360
|
+
const updated = await prisma.opprPlayerRanking.update({
|
|
361
|
+
where: { playerId },
|
|
362
|
+
data: {
|
|
363
|
+
rating: newRating,
|
|
364
|
+
ratingDeviation: newRD,
|
|
365
|
+
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
366
|
+
isRated
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
await createOpprRankingHistory({
|
|
370
|
+
opprPlayerRankingId: ranking.id,
|
|
371
|
+
rating: newRating,
|
|
372
|
+
ratingDeviation: newRD,
|
|
373
|
+
ranking: updated.ranking ?? void 0,
|
|
374
|
+
isRated,
|
|
375
|
+
changeType: "TOURNAMENT_RESULT",
|
|
376
|
+
tournamentId
|
|
377
|
+
});
|
|
378
|
+
return updated;
|
|
379
|
+
}
|
|
380
|
+
async function updateWorldRankings(rankings) {
|
|
381
|
+
await prisma.$transaction(async (tx) => {
|
|
382
|
+
for (const { playerId, ranking } of rankings) {
|
|
383
|
+
const opprRanking = await tx.opprPlayerRanking.findUnique({
|
|
384
|
+
where: { playerId }
|
|
385
|
+
});
|
|
386
|
+
if (opprRanking) {
|
|
387
|
+
await tx.opprPlayerRanking.update({
|
|
388
|
+
where: { playerId },
|
|
389
|
+
data: { ranking }
|
|
390
|
+
});
|
|
391
|
+
await tx.opprRankingHistory.create({
|
|
392
|
+
data: {
|
|
393
|
+
opprPlayerRankingId: opprRanking.id,
|
|
394
|
+
rating: opprRanking.rating,
|
|
395
|
+
ratingDeviation: opprRanking.ratingDeviation,
|
|
396
|
+
ranking,
|
|
397
|
+
isRated: opprRanking.isRated,
|
|
398
|
+
changeType: "RANKING_REFRESH"
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
async function applyRDDecayForInactivePlayers(thresholdDays = 30, decayPerDay = 0.3, maxRD = 200) {
|
|
406
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
407
|
+
cutoffDate.setDate(cutoffDate.getDate() - thresholdDays);
|
|
408
|
+
const inactivePlayers = await prisma.opprPlayerRanking.findMany({
|
|
409
|
+
where: {
|
|
410
|
+
lastRatingUpdate: { lt: cutoffDate },
|
|
411
|
+
ratingDeviation: { lt: maxRD }
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
let updatedCount = 0;
|
|
415
|
+
await prisma.$transaction(async (tx) => {
|
|
416
|
+
for (const ranking of inactivePlayers) {
|
|
417
|
+
const daysSinceUpdate = Math.floor(
|
|
418
|
+
(Date.now() - ranking.lastRatingUpdate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
419
|
+
);
|
|
420
|
+
const newRD = Math.min(ranking.ratingDeviation + daysSinceUpdate * decayPerDay, maxRD);
|
|
421
|
+
await tx.opprPlayerRanking.update({
|
|
422
|
+
where: { id: ranking.id },
|
|
423
|
+
data: { ratingDeviation: newRD }
|
|
424
|
+
});
|
|
425
|
+
await tx.opprRankingHistory.create({
|
|
426
|
+
data: {
|
|
427
|
+
opprPlayerRankingId: ranking.id,
|
|
428
|
+
rating: ranking.rating,
|
|
429
|
+
ratingDeviation: newRD,
|
|
430
|
+
ranking: ranking.ranking,
|
|
431
|
+
isRated: ranking.isRated,
|
|
432
|
+
changeType: "RD_DECAY",
|
|
433
|
+
notes: `RD increased from ${ranking.ratingDeviation.toFixed(1)} to ${newRD.toFixed(1)} due to ${daysSinceUpdate} days of inactivity`
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
updatedCount++;
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
return updatedCount;
|
|
440
|
+
}
|
|
441
|
+
async function deleteOpprPlayerRanking(playerId) {
|
|
442
|
+
return prisma.opprPlayerRanking.delete({
|
|
443
|
+
where: { playerId }
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
async function countOpprPlayerRankings(where) {
|
|
447
|
+
return prisma.opprPlayerRanking.count({ where });
|
|
448
|
+
}
|
|
449
|
+
async function createOpprRankingHistory(data) {
|
|
450
|
+
return prisma.opprRankingHistory.create({
|
|
451
|
+
data
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
async function getOpprRankingHistory(playerId, limit) {
|
|
455
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
456
|
+
if (!ranking) return [];
|
|
457
|
+
return prisma.opprRankingHistory.findMany({
|
|
458
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
459
|
+
orderBy: { createdAt: "desc" },
|
|
460
|
+
take: limit,
|
|
461
|
+
include: { tournament: true }
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
async function getOpprRankingHistoryByDateRange(playerId, startDate, endDate) {
|
|
465
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
466
|
+
if (!ranking) return [];
|
|
467
|
+
return prisma.opprRankingHistory.findMany({
|
|
468
|
+
where: {
|
|
469
|
+
opprPlayerRankingId: ranking.id,
|
|
470
|
+
createdAt: {
|
|
471
|
+
gte: startDate,
|
|
472
|
+
lte: endDate
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
orderBy: { createdAt: "asc" },
|
|
476
|
+
include: { tournament: true }
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
async function getLatestOpprRankingHistory(playerId) {
|
|
480
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
481
|
+
if (!ranking) return null;
|
|
482
|
+
return prisma.opprRankingHistory.findFirst({
|
|
483
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
484
|
+
orderBy: { createdAt: "desc" },
|
|
485
|
+
include: { tournament: true }
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
async function countOpprRankingHistory(where) {
|
|
489
|
+
return prisma.opprRankingHistory.count({ where });
|
|
490
|
+
}
|
|
491
|
+
|
|
227
492
|
// src/tournaments.ts
|
|
228
493
|
async function createTournament(data) {
|
|
229
494
|
return prisma.tournament.create({
|
|
@@ -303,13 +568,11 @@ async function getTournamentWithResults(id) {
|
|
|
303
568
|
return prisma.tournament.findUnique({
|
|
304
569
|
where: { id },
|
|
305
570
|
include: {
|
|
306
|
-
|
|
571
|
+
standings: {
|
|
307
572
|
include: {
|
|
308
573
|
player: true
|
|
309
574
|
},
|
|
310
|
-
orderBy: {
|
|
311
|
-
position: "asc"
|
|
312
|
-
}
|
|
575
|
+
orderBy: [{ isFinals: "desc" }, { position: "asc" }]
|
|
313
576
|
}
|
|
314
577
|
}
|
|
315
578
|
});
|
|
@@ -320,7 +583,7 @@ async function searchTournaments(query, limit = 20) {
|
|
|
320
583
|
where: {
|
|
321
584
|
OR: [
|
|
322
585
|
{ name: { contains: query, mode: "insensitive" } },
|
|
323
|
-
{ location: { contains: query, mode: "insensitive" } }
|
|
586
|
+
{ location: { name: { contains: query, mode: "insensitive" } } }
|
|
324
587
|
]
|
|
325
588
|
},
|
|
326
589
|
orderBy: { date: "desc" }
|
|
@@ -331,7 +594,7 @@ async function getTournamentStats(id) {
|
|
|
331
594
|
if (!tournament) {
|
|
332
595
|
return null;
|
|
333
596
|
}
|
|
334
|
-
const playerCount = tournament.
|
|
597
|
+
const playerCount = tournament.standings.length;
|
|
335
598
|
if (playerCount === 0) {
|
|
336
599
|
return {
|
|
337
600
|
tournament,
|
|
@@ -342,9 +605,9 @@ async function getTournamentStats(id) {
|
|
|
342
605
|
lowestPoints: 0
|
|
343
606
|
};
|
|
344
607
|
}
|
|
345
|
-
const totalPoints = tournament.
|
|
346
|
-
const totalEfficiency = tournament.
|
|
347
|
-
const allPoints = tournament.
|
|
608
|
+
const totalPoints = tournament.standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
609
|
+
const totalEfficiency = tournament.standings.reduce((sum, s) => sum + (s.efficiency || 0), 0);
|
|
610
|
+
const allPoints = tournament.standings.map((s) => s.totalPoints || 0);
|
|
348
611
|
return {
|
|
349
612
|
tournament,
|
|
350
613
|
playerCount,
|
|
@@ -355,44 +618,47 @@ async function getTournamentStats(id) {
|
|
|
355
618
|
};
|
|
356
619
|
}
|
|
357
620
|
|
|
358
|
-
// src/
|
|
359
|
-
async function
|
|
360
|
-
const
|
|
621
|
+
// src/standings.ts
|
|
622
|
+
async function createStanding(data) {
|
|
623
|
+
const standingData = {
|
|
361
624
|
...data,
|
|
625
|
+
isFinals: data.isFinals ?? false,
|
|
362
626
|
decayedPoints: data.decayedPoints ?? data.totalPoints ?? 0
|
|
363
627
|
};
|
|
364
|
-
return prisma.
|
|
365
|
-
data:
|
|
628
|
+
return prisma.standing.create({
|
|
629
|
+
data: standingData
|
|
366
630
|
});
|
|
367
631
|
}
|
|
368
|
-
async function
|
|
369
|
-
const
|
|
632
|
+
async function createManyStandings(data) {
|
|
633
|
+
const standingsData = data.map((item) => ({
|
|
370
634
|
...item,
|
|
635
|
+
isFinals: item.isFinals ?? false,
|
|
371
636
|
decayedPoints: item.decayedPoints ?? item.totalPoints ?? 0
|
|
372
637
|
}));
|
|
373
|
-
return prisma.
|
|
374
|
-
data:
|
|
638
|
+
return prisma.standing.createMany({
|
|
639
|
+
data: standingsData
|
|
375
640
|
});
|
|
376
641
|
}
|
|
377
|
-
async function
|
|
378
|
-
return prisma.
|
|
642
|
+
async function findStandingById(id, include) {
|
|
643
|
+
return prisma.standing.findUnique({
|
|
379
644
|
where: { id },
|
|
380
645
|
include
|
|
381
646
|
});
|
|
382
647
|
}
|
|
383
|
-
async function
|
|
384
|
-
return prisma.
|
|
648
|
+
async function findStandingByPlayerAndTournament(playerId, tournamentId, isFinals, include) {
|
|
649
|
+
return prisma.standing.findUnique({
|
|
385
650
|
where: {
|
|
386
|
-
|
|
651
|
+
playerId_tournamentId_isFinals: {
|
|
387
652
|
playerId,
|
|
388
|
-
tournamentId
|
|
653
|
+
tournamentId,
|
|
654
|
+
isFinals
|
|
389
655
|
}
|
|
390
656
|
},
|
|
391
657
|
include
|
|
392
658
|
});
|
|
393
659
|
}
|
|
394
|
-
async function
|
|
395
|
-
return prisma.
|
|
660
|
+
async function findStandings(options = {}) {
|
|
661
|
+
return prisma.standing.findMany({
|
|
396
662
|
take: options.take,
|
|
397
663
|
skip: options.skip,
|
|
398
664
|
where: options.where,
|
|
@@ -400,45 +666,85 @@ async function findResults(options = {}) {
|
|
|
400
666
|
include: options.include
|
|
401
667
|
});
|
|
402
668
|
}
|
|
403
|
-
async function
|
|
404
|
-
return
|
|
669
|
+
async function getPlayerStandings(playerId, options = {}) {
|
|
670
|
+
return findStandings({
|
|
405
671
|
...options,
|
|
406
672
|
where: { playerId },
|
|
407
673
|
include: { tournament: true, ...options.include },
|
|
408
674
|
orderBy: { tournament: { date: "desc" } }
|
|
409
675
|
});
|
|
410
676
|
}
|
|
411
|
-
async function
|
|
412
|
-
return
|
|
677
|
+
async function getTournamentStandings(tournamentId, options = {}) {
|
|
678
|
+
return findStandings({
|
|
413
679
|
...options,
|
|
414
680
|
where: { tournamentId },
|
|
415
681
|
include: { player: true, ...options.include },
|
|
416
|
-
orderBy: { position: "asc" }
|
|
682
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
417
683
|
});
|
|
418
684
|
}
|
|
685
|
+
async function getQualifyingStandings(tournamentId, options = {}) {
|
|
686
|
+
return findStandings({
|
|
687
|
+
...options,
|
|
688
|
+
where: { tournamentId, isFinals: false },
|
|
689
|
+
include: { player: true, ...options.include },
|
|
690
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
async function getFinalsStandings(tournamentId, options = {}) {
|
|
694
|
+
return findStandings({
|
|
695
|
+
...options,
|
|
696
|
+
where: { tournamentId, isFinals: true },
|
|
697
|
+
include: { player: true, ...options.include },
|
|
698
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
async function getMergedStandings(tournamentId) {
|
|
702
|
+
const [finals, qualifying] = await Promise.all([
|
|
703
|
+
findStandings({
|
|
704
|
+
where: { tournamentId, isFinals: true },
|
|
705
|
+
orderBy: { position: "asc" },
|
|
706
|
+
include: { player: true }
|
|
707
|
+
}),
|
|
708
|
+
findStandings({
|
|
709
|
+
where: { tournamentId, isFinals: false },
|
|
710
|
+
orderBy: { position: "asc" },
|
|
711
|
+
include: { player: true }
|
|
712
|
+
})
|
|
713
|
+
]);
|
|
714
|
+
const finalistIds = new Set(finals.map((s) => s.playerId));
|
|
715
|
+
const nonFinalists = qualifying.filter((s) => !finalistIds.has(s.playerId));
|
|
716
|
+
return [
|
|
717
|
+
...finals.map((s) => ({ ...s, mergedPosition: s.position, isFinalist: true })),
|
|
718
|
+
...nonFinalists.map((s, i) => ({
|
|
719
|
+
...s,
|
|
720
|
+
mergedPosition: finals.length + i + 1,
|
|
721
|
+
isFinalist: false
|
|
722
|
+
}))
|
|
723
|
+
];
|
|
724
|
+
}
|
|
419
725
|
async function getPlayerTopFinishes(playerId, limit = 15) {
|
|
420
|
-
return
|
|
726
|
+
return findStandings({
|
|
421
727
|
where: { playerId },
|
|
422
728
|
take: limit,
|
|
423
729
|
include: { tournament: true },
|
|
424
730
|
orderBy: { decayedPoints: "desc" }
|
|
425
731
|
});
|
|
426
732
|
}
|
|
427
|
-
async function
|
|
428
|
-
return prisma.
|
|
733
|
+
async function updateStanding(id, data) {
|
|
734
|
+
return prisma.standing.update({
|
|
429
735
|
where: { id },
|
|
430
736
|
data
|
|
431
737
|
});
|
|
432
738
|
}
|
|
433
|
-
async function
|
|
434
|
-
const
|
|
739
|
+
async function updateStandingPoints(id, linearPoints, dynamicPoints, totalPoints) {
|
|
740
|
+
const standing = await findStandingById(id, {
|
|
435
741
|
tournament: true
|
|
436
742
|
});
|
|
437
|
-
if (!
|
|
438
|
-
throw new Error(`
|
|
743
|
+
if (!standing) {
|
|
744
|
+
throw new Error(`Standing with id ${id} not found`);
|
|
439
745
|
}
|
|
440
746
|
const now = /* @__PURE__ */ new Date();
|
|
441
|
-
const tournamentDate =
|
|
747
|
+
const tournamentDate = standing.tournament.date;
|
|
442
748
|
const ageInDays = Math.floor((now.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
443
749
|
const ageInYears = ageInDays / 365;
|
|
444
750
|
let decayMultiplier = 0;
|
|
@@ -452,7 +758,7 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
452
758
|
decayMultiplier = 0;
|
|
453
759
|
}
|
|
454
760
|
const decayedPoints = totalPoints * decayMultiplier;
|
|
455
|
-
return
|
|
761
|
+
return updateStanding(id, {
|
|
456
762
|
linearPoints,
|
|
457
763
|
dynamicPoints,
|
|
458
764
|
totalPoints,
|
|
@@ -461,50 +767,50 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
461
767
|
decayedPoints
|
|
462
768
|
});
|
|
463
769
|
}
|
|
464
|
-
async function
|
|
465
|
-
return prisma.
|
|
770
|
+
async function deleteStanding(id) {
|
|
771
|
+
return prisma.standing.delete({
|
|
466
772
|
where: { id }
|
|
467
773
|
});
|
|
468
774
|
}
|
|
469
|
-
async function
|
|
470
|
-
return prisma.
|
|
775
|
+
async function deleteStandingsByTournament(tournamentId) {
|
|
776
|
+
return prisma.standing.deleteMany({
|
|
471
777
|
where: { tournamentId }
|
|
472
778
|
});
|
|
473
779
|
}
|
|
474
|
-
async function
|
|
475
|
-
return prisma.
|
|
780
|
+
async function countStandings(where) {
|
|
781
|
+
return prisma.standing.count({ where });
|
|
476
782
|
}
|
|
477
783
|
async function getPlayerStats(playerId) {
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
784
|
+
const standings = await getPlayerStandings(playerId);
|
|
785
|
+
if (standings.length === 0) {
|
|
480
786
|
return null;
|
|
481
787
|
}
|
|
482
|
-
const totalPoints =
|
|
483
|
-
const totalDecayedPoints =
|
|
484
|
-
const averagePosition =
|
|
485
|
-
const averageEfficiency =
|
|
486
|
-
const firstPlaceFinishes =
|
|
487
|
-
const topThreeFinishes =
|
|
788
|
+
const totalPoints = standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
789
|
+
const totalDecayedPoints = standings.reduce((sum, s) => sum + (s.decayedPoints || 0), 0);
|
|
790
|
+
const averagePosition = standings.reduce((sum, s) => sum + s.position, 0) / standings.length;
|
|
791
|
+
const averageEfficiency = standings.reduce((sum, s) => sum + (s.efficiency || 0), 0) / standings.length;
|
|
792
|
+
const firstPlaceFinishes = standings.filter((s) => s.position === 1).length;
|
|
793
|
+
const topThreeFinishes = standings.filter((s) => s.position <= 3).length;
|
|
488
794
|
return {
|
|
489
|
-
totalEvents:
|
|
795
|
+
totalEvents: standings.length,
|
|
490
796
|
totalPoints,
|
|
491
797
|
totalDecayedPoints,
|
|
492
|
-
averagePoints: totalPoints /
|
|
798
|
+
averagePoints: totalPoints / standings.length,
|
|
493
799
|
averagePosition,
|
|
494
800
|
averageFinish: averagePosition,
|
|
495
801
|
averageEfficiency,
|
|
496
802
|
firstPlaceFinishes,
|
|
497
803
|
topThreeFinishes,
|
|
498
|
-
bestFinish: Math.min(...
|
|
499
|
-
highestPoints: Math.max(...
|
|
804
|
+
bestFinish: Math.min(...standings.map((s) => s.position)),
|
|
805
|
+
highestPoints: Math.max(...standings.map((s) => s.totalPoints || 0))
|
|
500
806
|
};
|
|
501
807
|
}
|
|
502
808
|
async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date()) {
|
|
503
|
-
const
|
|
809
|
+
const standings = await findStandings({
|
|
504
810
|
include: { tournament: true }
|
|
505
811
|
});
|
|
506
|
-
const updates =
|
|
507
|
-
const tournamentDate =
|
|
812
|
+
const updates = standings.map((standing) => {
|
|
813
|
+
const tournamentDate = standing.tournament.date;
|
|
508
814
|
const ageInDays = Math.floor(
|
|
509
815
|
(referenceDate.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
510
816
|
);
|
|
@@ -519,9 +825,9 @@ async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date())
|
|
|
519
825
|
} else {
|
|
520
826
|
decayMultiplier = 0;
|
|
521
827
|
}
|
|
522
|
-
const decayedPoints = (
|
|
523
|
-
return prisma.
|
|
524
|
-
where: { id:
|
|
828
|
+
const decayedPoints = (standing.totalPoints || 0) * decayMultiplier;
|
|
829
|
+
return prisma.standing.update({
|
|
830
|
+
where: { id: standing.id },
|
|
525
831
|
data: {
|
|
526
832
|
ageInDays,
|
|
527
833
|
decayMultiplier,
|
|
@@ -540,9 +846,11 @@ async function createUser(data) {
|
|
|
540
846
|
}
|
|
541
847
|
async function createUserWithPlayer(userData, playerData) {
|
|
542
848
|
return prisma.$transaction(async (tx) => {
|
|
849
|
+
const playerNumber = await generateUniquePlayerNumber();
|
|
543
850
|
const player = await tx.player.create({
|
|
544
851
|
data: {
|
|
545
|
-
name: playerData.name
|
|
852
|
+
name: playerData.name,
|
|
853
|
+
playerNumber
|
|
546
854
|
}
|
|
547
855
|
});
|
|
548
856
|
const user = await tx.user.create({
|
|
@@ -554,11 +862,8 @@ async function createUserWithPlayer(userData, playerData) {
|
|
|
554
862
|
player: {
|
|
555
863
|
select: {
|
|
556
864
|
id: true,
|
|
865
|
+
playerNumber: true,
|
|
557
866
|
name: true,
|
|
558
|
-
rating: true,
|
|
559
|
-
ratingDeviation: true,
|
|
560
|
-
ranking: true,
|
|
561
|
-
isRated: true,
|
|
562
867
|
eventCount: true
|
|
563
868
|
}
|
|
564
869
|
}
|
|
@@ -586,11 +891,8 @@ async function getUserWithPlayer(id) {
|
|
|
586
891
|
player: {
|
|
587
892
|
select: {
|
|
588
893
|
id: true,
|
|
894
|
+
playerNumber: true,
|
|
589
895
|
name: true,
|
|
590
|
-
rating: true,
|
|
591
|
-
ratingDeviation: true,
|
|
592
|
-
ranking: true,
|
|
593
|
-
isRated: true,
|
|
594
896
|
eventCount: true
|
|
595
897
|
}
|
|
596
898
|
}
|
|
@@ -608,11 +910,8 @@ async function getUserByEmailWithPlayer(email) {
|
|
|
608
910
|
player: {
|
|
609
911
|
select: {
|
|
610
912
|
id: true,
|
|
913
|
+
playerNumber: true,
|
|
611
914
|
name: true,
|
|
612
|
-
rating: true,
|
|
613
|
-
ratingDeviation: true,
|
|
614
|
-
ranking: true,
|
|
615
|
-
isRated: true,
|
|
616
915
|
eventCount: true
|
|
617
916
|
}
|
|
618
917
|
}
|
|
@@ -653,11 +952,8 @@ async function findUsers(params) {
|
|
|
653
952
|
player: {
|
|
654
953
|
select: {
|
|
655
954
|
id: true,
|
|
955
|
+
playerNumber: true,
|
|
656
956
|
name: true,
|
|
657
|
-
rating: true,
|
|
658
|
-
ratingDeviation: true,
|
|
659
|
-
ranking: true,
|
|
660
|
-
isRated: true,
|
|
661
957
|
eventCount: true
|
|
662
958
|
}
|
|
663
959
|
}
|
|
@@ -684,11 +980,8 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
684
980
|
player: {
|
|
685
981
|
select: {
|
|
686
982
|
id: true,
|
|
983
|
+
playerNumber: true,
|
|
687
984
|
name: true,
|
|
688
|
-
rating: true,
|
|
689
|
-
ratingDeviation: true,
|
|
690
|
-
ranking: true,
|
|
691
|
-
isRated: true,
|
|
692
985
|
eventCount: true
|
|
693
986
|
}
|
|
694
987
|
}
|
|
@@ -697,65 +990,428 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
697
990
|
return user;
|
|
698
991
|
});
|
|
699
992
|
}
|
|
993
|
+
|
|
994
|
+
// src/api-keys.ts
|
|
995
|
+
var MAX_API_KEYS_PER_USER = 5;
|
|
996
|
+
async function createApiKey(data) {
|
|
997
|
+
return prisma.apiKey.create({ data });
|
|
998
|
+
}
|
|
999
|
+
async function findApiKeyById(id) {
|
|
1000
|
+
return prisma.apiKey.findUnique({ where: { id } });
|
|
1001
|
+
}
|
|
1002
|
+
async function findApiKeysByPrefix(keyPrefix) {
|
|
1003
|
+
const keys = await prisma.apiKey.findMany({
|
|
1004
|
+
where: { keyPrefix },
|
|
1005
|
+
include: {
|
|
1006
|
+
user: {
|
|
1007
|
+
select: {
|
|
1008
|
+
id: true,
|
|
1009
|
+
email: true,
|
|
1010
|
+
role: true
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
return keys;
|
|
1016
|
+
}
|
|
1017
|
+
async function getUserApiKeys(userId) {
|
|
1018
|
+
return prisma.apiKey.findMany({
|
|
1019
|
+
where: { userId },
|
|
1020
|
+
select: {
|
|
1021
|
+
id: true,
|
|
1022
|
+
name: true,
|
|
1023
|
+
keyPrefix: true,
|
|
1024
|
+
expiresAt: true,
|
|
1025
|
+
lastUsedAt: true,
|
|
1026
|
+
createdAt: true
|
|
1027
|
+
},
|
|
1028
|
+
orderBy: { createdAt: "desc" }
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
async function countUserApiKeys(userId) {
|
|
1032
|
+
return prisma.apiKey.count({ where: { userId } });
|
|
1033
|
+
}
|
|
1034
|
+
async function updateApiKeyLastUsed(id) {
|
|
1035
|
+
await prisma.apiKey.update({
|
|
1036
|
+
where: { id },
|
|
1037
|
+
data: { lastUsedAt: /* @__PURE__ */ new Date() }
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
async function deleteApiKey(id) {
|
|
1041
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1042
|
+
}
|
|
1043
|
+
async function deleteUserApiKey(id, userId) {
|
|
1044
|
+
const key = await prisma.apiKey.findFirst({
|
|
1045
|
+
where: { id, userId }
|
|
1046
|
+
});
|
|
1047
|
+
if (!key) {
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// src/locations.ts
|
|
1054
|
+
async function createLocation(data) {
|
|
1055
|
+
return prisma.location.create({
|
|
1056
|
+
data
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
async function findLocationById(id, include) {
|
|
1060
|
+
return prisma.location.findUnique({
|
|
1061
|
+
where: { id },
|
|
1062
|
+
include
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
async function findLocationByExternalId(externalId, include) {
|
|
1066
|
+
return prisma.location.findUnique({
|
|
1067
|
+
where: { externalId },
|
|
1068
|
+
include
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
async function findLocations(options = {}) {
|
|
1072
|
+
return prisma.location.findMany({
|
|
1073
|
+
take: options.take,
|
|
1074
|
+
skip: options.skip,
|
|
1075
|
+
where: options.where,
|
|
1076
|
+
orderBy: options.orderBy,
|
|
1077
|
+
include: options.include
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
async function searchLocations(query, limit = 20) {
|
|
1081
|
+
return findLocations({
|
|
1082
|
+
take: limit,
|
|
1083
|
+
where: {
|
|
1084
|
+
OR: [
|
|
1085
|
+
{ name: { contains: query, mode: "insensitive" } },
|
|
1086
|
+
{ city: { contains: query, mode: "insensitive" } }
|
|
1087
|
+
]
|
|
1088
|
+
},
|
|
1089
|
+
orderBy: { name: "asc" }
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
async function updateLocation(id, data) {
|
|
1093
|
+
return prisma.location.update({
|
|
1094
|
+
where: { id },
|
|
1095
|
+
data
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
async function deleteLocation(id) {
|
|
1099
|
+
return prisma.location.delete({
|
|
1100
|
+
where: { id }
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
async function countLocations(where) {
|
|
1104
|
+
return prisma.location.count({ where });
|
|
1105
|
+
}
|
|
1106
|
+
async function getLocationWithTournaments(id) {
|
|
1107
|
+
return prisma.location.findUnique({
|
|
1108
|
+
where: { id },
|
|
1109
|
+
include: {
|
|
1110
|
+
tournaments: {
|
|
1111
|
+
orderBy: {
|
|
1112
|
+
date: "desc"
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// src/blog-posts.ts
|
|
1120
|
+
var defaultInclude = {
|
|
1121
|
+
author: {
|
|
1122
|
+
select: { id: true, email: true }
|
|
1123
|
+
},
|
|
1124
|
+
tags: {
|
|
1125
|
+
select: { id: true, name: true, slug: true }
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
async function createBlogPost(data) {
|
|
1129
|
+
const { tagIds, ...postData } = data;
|
|
1130
|
+
return prisma.blogPost.create({
|
|
1131
|
+
data: {
|
|
1132
|
+
...postData,
|
|
1133
|
+
tags: tagIds?.length ? { connect: tagIds.map((id) => ({ id })) } : void 0
|
|
1134
|
+
},
|
|
1135
|
+
include: defaultInclude
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
async function findBlogPostById(id) {
|
|
1139
|
+
return prisma.blogPost.findUnique({
|
|
1140
|
+
where: { id },
|
|
1141
|
+
include: defaultInclude
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
async function findBlogPostBySlug(slug) {
|
|
1145
|
+
return prisma.blogPost.findUnique({
|
|
1146
|
+
where: { slug },
|
|
1147
|
+
include: defaultInclude
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
async function findBlogPosts(options = {}) {
|
|
1151
|
+
return prisma.blogPost.findMany({
|
|
1152
|
+
take: options.take,
|
|
1153
|
+
skip: options.skip,
|
|
1154
|
+
where: options.where,
|
|
1155
|
+
orderBy: options.orderBy ?? { createdAt: "desc" },
|
|
1156
|
+
include: options.include ?? defaultInclude
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
async function findPublishedBlogPosts(options = {}) {
|
|
1160
|
+
const { tagSlug, ...restOptions } = options;
|
|
1161
|
+
const where = {
|
|
1162
|
+
status: "PUBLISHED",
|
|
1163
|
+
publishedAt: { not: null },
|
|
1164
|
+
...tagSlug && {
|
|
1165
|
+
tags: {
|
|
1166
|
+
some: { slug: tagSlug }
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
return findBlogPosts({
|
|
1171
|
+
...restOptions,
|
|
1172
|
+
where,
|
|
1173
|
+
orderBy: options.orderBy ?? { publishedAt: "desc" }
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
async function searchBlogPosts(query, limit = 20, publishedOnly = true) {
|
|
1177
|
+
const where = {
|
|
1178
|
+
OR: [
|
|
1179
|
+
{ title: { contains: query, mode: "insensitive" } },
|
|
1180
|
+
{ excerpt: { contains: query, mode: "insensitive" } }
|
|
1181
|
+
],
|
|
1182
|
+
...publishedOnly && {
|
|
1183
|
+
status: "PUBLISHED",
|
|
1184
|
+
publishedAt: { not: null }
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
return findBlogPosts({
|
|
1188
|
+
take: limit,
|
|
1189
|
+
where,
|
|
1190
|
+
orderBy: { publishedAt: "desc" }
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
async function updateBlogPost(id, data) {
|
|
1194
|
+
const { tagIds, ...postData } = data;
|
|
1195
|
+
return prisma.blogPost.update({
|
|
1196
|
+
where: { id },
|
|
1197
|
+
data: {
|
|
1198
|
+
...postData,
|
|
1199
|
+
// If tagIds is provided, replace all tags
|
|
1200
|
+
...tagIds !== void 0 && {
|
|
1201
|
+
tags: {
|
|
1202
|
+
set: tagIds.map((tagId) => ({ id: tagId }))
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
include: defaultInclude
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
async function deleteBlogPost(id) {
|
|
1210
|
+
return prisma.blogPost.delete({
|
|
1211
|
+
where: { id }
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
async function countBlogPosts(where) {
|
|
1215
|
+
return prisma.blogPost.count({ where });
|
|
1216
|
+
}
|
|
1217
|
+
async function countPublishedBlogPosts(tagSlug) {
|
|
1218
|
+
return countBlogPosts({
|
|
1219
|
+
status: "PUBLISHED",
|
|
1220
|
+
publishedAt: { not: null },
|
|
1221
|
+
...tagSlug && {
|
|
1222
|
+
tags: {
|
|
1223
|
+
some: { slug: tagSlug }
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// src/blog-tags.ts
|
|
1230
|
+
async function createBlogTag(data) {
|
|
1231
|
+
return prisma.blogTag.create({
|
|
1232
|
+
data
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
async function findBlogTagById(id) {
|
|
1236
|
+
return prisma.blogTag.findUnique({
|
|
1237
|
+
where: { id }
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
async function findBlogTagBySlug(slug) {
|
|
1241
|
+
return prisma.blogTag.findUnique({
|
|
1242
|
+
where: { slug }
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
async function findBlogTags(options = {}) {
|
|
1246
|
+
return prisma.blogTag.findMany({
|
|
1247
|
+
take: options.take,
|
|
1248
|
+
skip: options.skip,
|
|
1249
|
+
where: options.where,
|
|
1250
|
+
orderBy: options.orderBy ?? { name: "asc" },
|
|
1251
|
+
include: options.include
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
async function searchBlogTags(query, limit = 20) {
|
|
1255
|
+
return findBlogTags({
|
|
1256
|
+
take: limit,
|
|
1257
|
+
where: {
|
|
1258
|
+
name: { contains: query, mode: "insensitive" }
|
|
1259
|
+
},
|
|
1260
|
+
orderBy: { name: "asc" }
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
async function updateBlogTag(id, data) {
|
|
1264
|
+
return prisma.blogTag.update({
|
|
1265
|
+
where: { id },
|
|
1266
|
+
data
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
async function deleteBlogTag(id) {
|
|
1270
|
+
return prisma.blogTag.delete({
|
|
1271
|
+
where: { id }
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
async function countBlogTags(where) {
|
|
1275
|
+
return prisma.blogTag.count({ where });
|
|
1276
|
+
}
|
|
1277
|
+
async function getBlogTagWithPostCount(id) {
|
|
1278
|
+
return prisma.blogTag.findUnique({
|
|
1279
|
+
where: { id },
|
|
1280
|
+
include: {
|
|
1281
|
+
_count: {
|
|
1282
|
+
select: { posts: true }
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
async function getBlogTagsWithPostCounts() {
|
|
1288
|
+
return prisma.blogTag.findMany({
|
|
1289
|
+
include: {
|
|
1290
|
+
_count: {
|
|
1291
|
+
select: { posts: true }
|
|
1292
|
+
}
|
|
1293
|
+
},
|
|
1294
|
+
orderBy: { name: "asc" }
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
700
1297
|
// Annotate the CommonJS export names for ESM import in node:
|
|
701
1298
|
0 && (module.exports = {
|
|
1299
|
+
MAX_API_KEYS_PER_USER,
|
|
1300
|
+
applyRDDecayForInactivePlayers,
|
|
702
1301
|
connect,
|
|
1302
|
+
countBlogPosts,
|
|
1303
|
+
countBlogTags,
|
|
1304
|
+
countLocations,
|
|
1305
|
+
countOpprPlayerRankings,
|
|
1306
|
+
countOpprRankingHistory,
|
|
703
1307
|
countPlayers,
|
|
704
|
-
|
|
1308
|
+
countPublishedBlogPosts,
|
|
1309
|
+
countStandings,
|
|
705
1310
|
countTournaments,
|
|
1311
|
+
countUserApiKeys,
|
|
706
1312
|
countUsers,
|
|
707
|
-
|
|
1313
|
+
createApiKey,
|
|
1314
|
+
createBlogPost,
|
|
1315
|
+
createBlogTag,
|
|
1316
|
+
createLocation,
|
|
1317
|
+
createManyStandings,
|
|
1318
|
+
createOpprPlayerRanking,
|
|
1319
|
+
createOpprRankingHistory,
|
|
708
1320
|
createPlayer,
|
|
709
|
-
|
|
1321
|
+
createStanding,
|
|
710
1322
|
createTournament,
|
|
711
1323
|
createUser,
|
|
712
1324
|
createUserWithPlayer,
|
|
1325
|
+
deleteApiKey,
|
|
1326
|
+
deleteBlogPost,
|
|
1327
|
+
deleteBlogTag,
|
|
1328
|
+
deleteLocation,
|
|
1329
|
+
deleteOpprPlayerRanking,
|
|
713
1330
|
deletePlayer,
|
|
714
|
-
|
|
715
|
-
|
|
1331
|
+
deleteStanding,
|
|
1332
|
+
deleteStandingsByTournament,
|
|
716
1333
|
deleteTournament,
|
|
717
1334
|
deleteUser,
|
|
1335
|
+
deleteUserApiKey,
|
|
718
1336
|
disconnect,
|
|
1337
|
+
findApiKeyById,
|
|
1338
|
+
findApiKeysByPrefix,
|
|
1339
|
+
findBlogPostById,
|
|
1340
|
+
findBlogPostBySlug,
|
|
1341
|
+
findBlogPosts,
|
|
1342
|
+
findBlogTagById,
|
|
1343
|
+
findBlogTagBySlug,
|
|
1344
|
+
findBlogTags,
|
|
1345
|
+
findLocationByExternalId,
|
|
1346
|
+
findLocationById,
|
|
1347
|
+
findLocations,
|
|
1348
|
+
findOpprPlayerRankingById,
|
|
1349
|
+
findOpprPlayerRankingByPlayerId,
|
|
1350
|
+
findOpprPlayerRankings,
|
|
719
1351
|
findPlayerByExternalId,
|
|
720
1352
|
findPlayerById,
|
|
1353
|
+
findPlayerByPlayerNumber,
|
|
721
1354
|
findPlayerByUserEmail,
|
|
722
1355
|
findPlayers,
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
1356
|
+
findPublishedBlogPosts,
|
|
1357
|
+
findStandingById,
|
|
1358
|
+
findStandingByPlayerAndTournament,
|
|
1359
|
+
findStandings,
|
|
726
1360
|
findTournamentByExternalId,
|
|
727
1361
|
findTournamentById,
|
|
728
1362
|
findTournaments,
|
|
729
1363
|
findUserByEmail,
|
|
730
1364
|
findUserById,
|
|
731
1365
|
findUsers,
|
|
1366
|
+
generateUniquePlayerNumber,
|
|
1367
|
+
getBlogTagWithPostCount,
|
|
1368
|
+
getBlogTagsWithPostCounts,
|
|
1369
|
+
getFinalsStandings,
|
|
1370
|
+
getLatestOpprRankingHistory,
|
|
1371
|
+
getLocationWithTournaments,
|
|
732
1372
|
getMajorTournaments,
|
|
733
|
-
|
|
1373
|
+
getMergedStandings,
|
|
1374
|
+
getOpprRankingHistory,
|
|
1375
|
+
getOpprRankingHistoryByDateRange,
|
|
1376
|
+
getOrCreateOpprPlayerRanking,
|
|
1377
|
+
getPlayerStandings,
|
|
734
1378
|
getPlayerStats,
|
|
735
1379
|
getPlayerTopFinishes,
|
|
736
1380
|
getPlayerWithResults,
|
|
737
|
-
|
|
1381
|
+
getQualifyingStandings,
|
|
1382
|
+
getRatedOpprPlayers,
|
|
738
1383
|
getRecentTournaments,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1384
|
+
getTopPlayersByOpprRanking,
|
|
1385
|
+
getTopPlayersByOpprRating,
|
|
1386
|
+
getTournamentStandings,
|
|
742
1387
|
getTournamentStats,
|
|
743
1388
|
getTournamentWithResults,
|
|
744
1389
|
getTournamentsByBoosterType,
|
|
745
1390
|
getTournamentsByDateRange,
|
|
1391
|
+
getUserApiKeys,
|
|
746
1392
|
getUserByEmailWithPlayer,
|
|
747
1393
|
getUserWithPlayer,
|
|
1394
|
+
isValidPlayerNumber,
|
|
748
1395
|
linkPlayerToUser,
|
|
749
1396
|
prisma,
|
|
750
1397
|
recalculateTimeDecay,
|
|
1398
|
+
searchBlogPosts,
|
|
1399
|
+
searchBlogTags,
|
|
1400
|
+
searchLocations,
|
|
751
1401
|
searchPlayers,
|
|
752
1402
|
searchTournaments,
|
|
753
1403
|
testConnection,
|
|
1404
|
+
updateApiKeyLastUsed,
|
|
1405
|
+
updateBlogPost,
|
|
1406
|
+
updateBlogTag,
|
|
1407
|
+
updateLocation,
|
|
1408
|
+
updateOpprPlayerRanking,
|
|
1409
|
+
updateOpprRatingAfterTournament,
|
|
754
1410
|
updatePlayer,
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
updateResultPoints,
|
|
1411
|
+
updateStanding,
|
|
1412
|
+
updateStandingPoints,
|
|
758
1413
|
updateTournament,
|
|
759
1414
|
updateUser,
|
|
760
|
-
updateUserRefreshToken
|
|
1415
|
+
updateUserRefreshToken,
|
|
1416
|
+
updateWorldRankings
|
|
761
1417
|
});
|