@opprs/db-prisma 2.2.1-canary.840995 → 2.2.1-canary.93558b4
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/dist/index.cjs +1182 -156
- package/dist/index.d.cts +998 -105
- package/dist/index.d.ts +998 -105
- package/dist/index.js +1069 -139
- package/package.json +2 -2
- 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_tournament_external_url/migration.sql +2 -0
- package/prisma/schema.prisma +291 -37
- package/prisma/seed.ts +107 -35
package/dist/index.js
CHANGED
|
@@ -93,48 +93,12 @@ async function findPlayers(options = {}) {
|
|
|
93
93
|
include: options.include
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
-
async function getRatedPlayers(options = {}) {
|
|
97
|
-
return findPlayers({
|
|
98
|
-
...options,
|
|
99
|
-
where: { isRated: true }
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
async function getTopPlayersByRating(limit = 50) {
|
|
103
|
-
return findPlayers({
|
|
104
|
-
take: limit,
|
|
105
|
-
orderBy: { rating: "desc" },
|
|
106
|
-
where: { isRated: true }
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
async function getTopPlayersByRanking(limit = 50) {
|
|
110
|
-
return findPlayers({
|
|
111
|
-
take: limit,
|
|
112
|
-
orderBy: { ranking: "asc" },
|
|
113
|
-
where: {
|
|
114
|
-
isRated: true,
|
|
115
|
-
ranking: { not: null }
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
96
|
async function updatePlayer(id, data) {
|
|
120
97
|
return prisma.player.update({
|
|
121
98
|
where: { id },
|
|
122
99
|
data
|
|
123
100
|
});
|
|
124
101
|
}
|
|
125
|
-
async function updatePlayerRating(id, rating, ratingDeviation, eventCount) {
|
|
126
|
-
const updateData = {
|
|
127
|
-
rating,
|
|
128
|
-
ratingDeviation,
|
|
129
|
-
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
130
|
-
lastEventDate: /* @__PURE__ */ new Date()
|
|
131
|
-
};
|
|
132
|
-
if (eventCount !== void 0) {
|
|
133
|
-
updateData.eventCount = eventCount;
|
|
134
|
-
updateData.isRated = eventCount >= 5;
|
|
135
|
-
}
|
|
136
|
-
return updatePlayer(id, updateData);
|
|
137
|
-
}
|
|
138
102
|
async function deletePlayer(id) {
|
|
139
103
|
return prisma.player.delete({
|
|
140
104
|
where: { id }
|
|
@@ -147,7 +111,7 @@ async function getPlayerWithResults(id) {
|
|
|
147
111
|
const player = await prisma.player.findUnique({
|
|
148
112
|
where: { id },
|
|
149
113
|
include: {
|
|
150
|
-
|
|
114
|
+
standings: {
|
|
151
115
|
include: {
|
|
152
116
|
tournament: true
|
|
153
117
|
},
|
|
@@ -164,7 +128,7 @@ async function getPlayerWithResults(id) {
|
|
|
164
128
|
}
|
|
165
129
|
return {
|
|
166
130
|
...player,
|
|
167
|
-
results: player.
|
|
131
|
+
results: player.standings
|
|
168
132
|
};
|
|
169
133
|
}
|
|
170
134
|
async function searchPlayers(query, limit = 20) {
|
|
@@ -176,6 +140,212 @@ async function searchPlayers(query, limit = 20) {
|
|
|
176
140
|
});
|
|
177
141
|
}
|
|
178
142
|
|
|
143
|
+
// src/oppr-rankings.ts
|
|
144
|
+
async function getOrCreateOpprPlayerRanking(playerId) {
|
|
145
|
+
const existing = await prisma.opprPlayerRanking.findUnique({
|
|
146
|
+
where: { playerId }
|
|
147
|
+
});
|
|
148
|
+
if (existing) return existing;
|
|
149
|
+
return prisma.opprPlayerRanking.create({
|
|
150
|
+
data: { playerId }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async function createOpprPlayerRanking(data) {
|
|
154
|
+
return prisma.opprPlayerRanking.create({
|
|
155
|
+
data
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async function findOpprPlayerRankingById(id, include) {
|
|
159
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
160
|
+
where: { id },
|
|
161
|
+
include
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async function findOpprPlayerRankingByPlayerId(playerId, include) {
|
|
165
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
166
|
+
where: { playerId },
|
|
167
|
+
include
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
async function findOpprPlayerRankings(options = {}) {
|
|
171
|
+
return prisma.opprPlayerRanking.findMany({
|
|
172
|
+
take: options.take,
|
|
173
|
+
skip: options.skip,
|
|
174
|
+
where: options.where,
|
|
175
|
+
orderBy: options.orderBy,
|
|
176
|
+
include: options.include
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async function getTopPlayersByOpprRating(limit = 50) {
|
|
180
|
+
return prisma.opprPlayerRanking.findMany({
|
|
181
|
+
take: limit,
|
|
182
|
+
where: { isRated: true },
|
|
183
|
+
orderBy: { rating: "desc" },
|
|
184
|
+
include: { player: true }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async function getTopPlayersByOpprRanking(limit = 50) {
|
|
188
|
+
return prisma.opprPlayerRanking.findMany({
|
|
189
|
+
take: limit,
|
|
190
|
+
where: {
|
|
191
|
+
isRated: true,
|
|
192
|
+
ranking: { not: null }
|
|
193
|
+
},
|
|
194
|
+
orderBy: { ranking: "asc" },
|
|
195
|
+
include: { player: true }
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
async function getRatedOpprPlayers(options = {}) {
|
|
199
|
+
return prisma.opprPlayerRanking.findMany({
|
|
200
|
+
...options,
|
|
201
|
+
where: { isRated: true },
|
|
202
|
+
include: { player: true, ...options.include }
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
async function updateOpprPlayerRanking(playerId, data) {
|
|
206
|
+
return prisma.opprPlayerRanking.update({
|
|
207
|
+
where: { playerId },
|
|
208
|
+
data: {
|
|
209
|
+
...data,
|
|
210
|
+
lastRatingUpdate: data.lastRatingUpdate ?? /* @__PURE__ */ new Date()
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
async function updateOpprRatingAfterTournament(playerId, newRating, newRD, tournamentId, eventCount) {
|
|
215
|
+
const ranking = await getOrCreateOpprPlayerRanking(playerId);
|
|
216
|
+
const isRated = eventCount !== void 0 ? eventCount >= 5 : ranking.isRated;
|
|
217
|
+
const updated = await prisma.opprPlayerRanking.update({
|
|
218
|
+
where: { playerId },
|
|
219
|
+
data: {
|
|
220
|
+
rating: newRating,
|
|
221
|
+
ratingDeviation: newRD,
|
|
222
|
+
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
223
|
+
isRated
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
await createOpprRankingHistory({
|
|
227
|
+
opprPlayerRankingId: ranking.id,
|
|
228
|
+
rating: newRating,
|
|
229
|
+
ratingDeviation: newRD,
|
|
230
|
+
ranking: updated.ranking ?? void 0,
|
|
231
|
+
isRated,
|
|
232
|
+
changeType: "TOURNAMENT_RESULT",
|
|
233
|
+
tournamentId
|
|
234
|
+
});
|
|
235
|
+
return updated;
|
|
236
|
+
}
|
|
237
|
+
async function updateWorldRankings(rankings) {
|
|
238
|
+
await prisma.$transaction(async (tx) => {
|
|
239
|
+
for (const { playerId, ranking } of rankings) {
|
|
240
|
+
const opprRanking = await tx.opprPlayerRanking.findUnique({
|
|
241
|
+
where: { playerId }
|
|
242
|
+
});
|
|
243
|
+
if (opprRanking) {
|
|
244
|
+
await tx.opprPlayerRanking.update({
|
|
245
|
+
where: { playerId },
|
|
246
|
+
data: { ranking }
|
|
247
|
+
});
|
|
248
|
+
await tx.opprRankingHistory.create({
|
|
249
|
+
data: {
|
|
250
|
+
opprPlayerRankingId: opprRanking.id,
|
|
251
|
+
rating: opprRanking.rating,
|
|
252
|
+
ratingDeviation: opprRanking.ratingDeviation,
|
|
253
|
+
ranking,
|
|
254
|
+
isRated: opprRanking.isRated,
|
|
255
|
+
changeType: "RANKING_REFRESH"
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
async function applyRDDecayForInactivePlayers(thresholdDays = 30, decayPerDay = 0.3, maxRD = 200) {
|
|
263
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
264
|
+
cutoffDate.setDate(cutoffDate.getDate() - thresholdDays);
|
|
265
|
+
const inactivePlayers = await prisma.opprPlayerRanking.findMany({
|
|
266
|
+
where: {
|
|
267
|
+
lastRatingUpdate: { lt: cutoffDate },
|
|
268
|
+
ratingDeviation: { lt: maxRD }
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
let updatedCount = 0;
|
|
272
|
+
await prisma.$transaction(async (tx) => {
|
|
273
|
+
for (const ranking of inactivePlayers) {
|
|
274
|
+
const daysSinceUpdate = Math.floor(
|
|
275
|
+
(Date.now() - ranking.lastRatingUpdate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
276
|
+
);
|
|
277
|
+
const newRD = Math.min(ranking.ratingDeviation + daysSinceUpdate * decayPerDay, maxRD);
|
|
278
|
+
await tx.opprPlayerRanking.update({
|
|
279
|
+
where: { id: ranking.id },
|
|
280
|
+
data: { ratingDeviation: newRD }
|
|
281
|
+
});
|
|
282
|
+
await tx.opprRankingHistory.create({
|
|
283
|
+
data: {
|
|
284
|
+
opprPlayerRankingId: ranking.id,
|
|
285
|
+
rating: ranking.rating,
|
|
286
|
+
ratingDeviation: newRD,
|
|
287
|
+
ranking: ranking.ranking,
|
|
288
|
+
isRated: ranking.isRated,
|
|
289
|
+
changeType: "RD_DECAY",
|
|
290
|
+
notes: `RD increased from ${ranking.ratingDeviation.toFixed(1)} to ${newRD.toFixed(1)} due to ${daysSinceUpdate} days of inactivity`
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
updatedCount++;
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
return updatedCount;
|
|
297
|
+
}
|
|
298
|
+
async function deleteOpprPlayerRanking(playerId) {
|
|
299
|
+
return prisma.opprPlayerRanking.delete({
|
|
300
|
+
where: { playerId }
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
async function countOpprPlayerRankings(where) {
|
|
304
|
+
return prisma.opprPlayerRanking.count({ where });
|
|
305
|
+
}
|
|
306
|
+
async function createOpprRankingHistory(data) {
|
|
307
|
+
return prisma.opprRankingHistory.create({
|
|
308
|
+
data
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async function getOpprRankingHistory(playerId, limit) {
|
|
312
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
313
|
+
if (!ranking) return [];
|
|
314
|
+
return prisma.opprRankingHistory.findMany({
|
|
315
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
316
|
+
orderBy: { createdAt: "desc" },
|
|
317
|
+
take: limit,
|
|
318
|
+
include: { tournament: true }
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async function getOpprRankingHistoryByDateRange(playerId, startDate, endDate) {
|
|
322
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
323
|
+
if (!ranking) return [];
|
|
324
|
+
return prisma.opprRankingHistory.findMany({
|
|
325
|
+
where: {
|
|
326
|
+
opprPlayerRankingId: ranking.id,
|
|
327
|
+
createdAt: {
|
|
328
|
+
gte: startDate,
|
|
329
|
+
lte: endDate
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
orderBy: { createdAt: "asc" },
|
|
333
|
+
include: { tournament: true }
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
async function getLatestOpprRankingHistory(playerId) {
|
|
337
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
338
|
+
if (!ranking) return null;
|
|
339
|
+
return prisma.opprRankingHistory.findFirst({
|
|
340
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
341
|
+
orderBy: { createdAt: "desc" },
|
|
342
|
+
include: { tournament: true }
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async function countOpprRankingHistory(where) {
|
|
346
|
+
return prisma.opprRankingHistory.count({ where });
|
|
347
|
+
}
|
|
348
|
+
|
|
179
349
|
// src/tournaments.ts
|
|
180
350
|
async function createTournament(data) {
|
|
181
351
|
return prisma.tournament.create({
|
|
@@ -255,13 +425,33 @@ async function getTournamentWithResults(id) {
|
|
|
255
425
|
return prisma.tournament.findUnique({
|
|
256
426
|
where: { id },
|
|
257
427
|
include: {
|
|
258
|
-
|
|
428
|
+
standings: {
|
|
259
429
|
include: {
|
|
260
430
|
player: true
|
|
261
431
|
},
|
|
262
|
-
orderBy: {
|
|
263
|
-
|
|
264
|
-
|
|
432
|
+
orderBy: [{ isFinals: "desc" }, { position: "asc" }]
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
async function getTournamentWithMatches(id) {
|
|
438
|
+
return prisma.tournament.findUnique({
|
|
439
|
+
where: { id },
|
|
440
|
+
include: {
|
|
441
|
+
rounds: {
|
|
442
|
+
include: {
|
|
443
|
+
matches: {
|
|
444
|
+
include: {
|
|
445
|
+
entries: {
|
|
446
|
+
include: {
|
|
447
|
+
player: true
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
orderBy: { number: "asc" }
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
orderBy: [{ isFinals: "asc" }, { number: "asc" }]
|
|
265
455
|
}
|
|
266
456
|
}
|
|
267
457
|
});
|
|
@@ -272,7 +462,7 @@ async function searchTournaments(query, limit = 20) {
|
|
|
272
462
|
where: {
|
|
273
463
|
OR: [
|
|
274
464
|
{ name: { contains: query, mode: "insensitive" } },
|
|
275
|
-
{ location: { contains: query, mode: "insensitive" } }
|
|
465
|
+
{ location: { name: { contains: query, mode: "insensitive" } } }
|
|
276
466
|
]
|
|
277
467
|
},
|
|
278
468
|
orderBy: { date: "desc" }
|
|
@@ -283,7 +473,7 @@ async function getTournamentStats(id) {
|
|
|
283
473
|
if (!tournament) {
|
|
284
474
|
return null;
|
|
285
475
|
}
|
|
286
|
-
const playerCount = tournament.
|
|
476
|
+
const playerCount = tournament.standings.length;
|
|
287
477
|
if (playerCount === 0) {
|
|
288
478
|
return {
|
|
289
479
|
tournament,
|
|
@@ -294,9 +484,9 @@ async function getTournamentStats(id) {
|
|
|
294
484
|
lowestPoints: 0
|
|
295
485
|
};
|
|
296
486
|
}
|
|
297
|
-
const totalPoints = tournament.
|
|
298
|
-
const totalEfficiency = tournament.
|
|
299
|
-
const allPoints = tournament.
|
|
487
|
+
const totalPoints = tournament.standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
488
|
+
const totalEfficiency = tournament.standings.reduce((sum, s) => sum + (s.efficiency || 0), 0);
|
|
489
|
+
const allPoints = tournament.standings.map((s) => s.totalPoints || 0);
|
|
300
490
|
return {
|
|
301
491
|
tournament,
|
|
302
492
|
playerCount,
|
|
@@ -307,44 +497,364 @@ async function getTournamentStats(id) {
|
|
|
307
497
|
};
|
|
308
498
|
}
|
|
309
499
|
|
|
310
|
-
// src/
|
|
311
|
-
async function
|
|
312
|
-
|
|
500
|
+
// src/rounds.ts
|
|
501
|
+
async function createRound(data) {
|
|
502
|
+
return prisma.round.create({
|
|
503
|
+
data: {
|
|
504
|
+
...data,
|
|
505
|
+
isFinals: data.isFinals ?? false
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
async function createManyRounds(data) {
|
|
510
|
+
const roundsData = data.map((item) => ({
|
|
511
|
+
...item,
|
|
512
|
+
isFinals: item.isFinals ?? false
|
|
513
|
+
}));
|
|
514
|
+
return prisma.round.createMany({
|
|
515
|
+
data: roundsData
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
async function findRoundById(id, include) {
|
|
519
|
+
return prisma.round.findUnique({
|
|
520
|
+
where: { id },
|
|
521
|
+
include
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
async function findRoundByTournamentAndNumber(tournamentId, number, isFinals, include) {
|
|
525
|
+
return prisma.round.findUnique({
|
|
526
|
+
where: {
|
|
527
|
+
tournamentId_number_isFinals: {
|
|
528
|
+
tournamentId,
|
|
529
|
+
number,
|
|
530
|
+
isFinals
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
include
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
async function findRounds(options = {}) {
|
|
537
|
+
return prisma.round.findMany({
|
|
538
|
+
take: options.take,
|
|
539
|
+
skip: options.skip,
|
|
540
|
+
where: options.where,
|
|
541
|
+
orderBy: options.orderBy,
|
|
542
|
+
include: options.include
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
async function getTournamentRounds(tournamentId, options = {}) {
|
|
546
|
+
return findRounds({
|
|
547
|
+
...options,
|
|
548
|
+
where: { tournamentId },
|
|
549
|
+
orderBy: options.orderBy ?? [{ isFinals: "asc" }, { number: "asc" }]
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
async function getQualifyingRounds(tournamentId, options = {}) {
|
|
553
|
+
return findRounds({
|
|
554
|
+
...options,
|
|
555
|
+
where: { tournamentId, isFinals: false },
|
|
556
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
async function getFinalsRounds(tournamentId, options = {}) {
|
|
560
|
+
return findRounds({
|
|
561
|
+
...options,
|
|
562
|
+
where: { tournamentId, isFinals: true },
|
|
563
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
async function updateRound(id, data) {
|
|
567
|
+
return prisma.round.update({
|
|
568
|
+
where: { id },
|
|
569
|
+
data
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
async function deleteRound(id) {
|
|
573
|
+
return prisma.round.delete({
|
|
574
|
+
where: { id }
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
async function deleteRoundsByTournament(tournamentId) {
|
|
578
|
+
return prisma.round.deleteMany({
|
|
579
|
+
where: { tournamentId }
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
async function countRounds(where) {
|
|
583
|
+
return prisma.round.count({ where });
|
|
584
|
+
}
|
|
585
|
+
async function getRoundWithMatches(id) {
|
|
586
|
+
return prisma.round.findUnique({
|
|
587
|
+
where: { id },
|
|
588
|
+
include: {
|
|
589
|
+
matches: {
|
|
590
|
+
include: {
|
|
591
|
+
entries: {
|
|
592
|
+
include: {
|
|
593
|
+
player: true
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
orderBy: {
|
|
598
|
+
number: "asc"
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/matches.ts
|
|
606
|
+
async function createMatch(data) {
|
|
607
|
+
return prisma.match.create({
|
|
608
|
+
data
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
async function createManyMatches(data) {
|
|
612
|
+
return prisma.match.createMany({
|
|
613
|
+
data
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
async function findMatchById(id, include) {
|
|
617
|
+
return prisma.match.findUnique({
|
|
618
|
+
where: { id },
|
|
619
|
+
include
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
async function findMatches(options = {}) {
|
|
623
|
+
return prisma.match.findMany({
|
|
624
|
+
take: options.take,
|
|
625
|
+
skip: options.skip,
|
|
626
|
+
where: options.where,
|
|
627
|
+
orderBy: options.orderBy,
|
|
628
|
+
include: options.include
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
async function getTournamentMatches(tournamentId, options = {}) {
|
|
632
|
+
return findMatches({
|
|
633
|
+
...options,
|
|
634
|
+
where: { tournamentId },
|
|
635
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
async function getRoundMatches(roundId, options = {}) {
|
|
639
|
+
return findMatches({
|
|
640
|
+
...options,
|
|
641
|
+
where: { roundId },
|
|
642
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
async function updateMatch(id, data) {
|
|
646
|
+
return prisma.match.update({
|
|
647
|
+
where: { id },
|
|
648
|
+
data
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
async function deleteMatch(id) {
|
|
652
|
+
return prisma.match.delete({
|
|
653
|
+
where: { id }
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
async function deleteMatchesByTournament(tournamentId) {
|
|
657
|
+
return prisma.match.deleteMany({
|
|
658
|
+
where: { tournamentId }
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async function deleteMatchesByRound(roundId) {
|
|
662
|
+
return prisma.match.deleteMany({
|
|
663
|
+
where: { roundId }
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
async function countMatches(where) {
|
|
667
|
+
return prisma.match.count({ where });
|
|
668
|
+
}
|
|
669
|
+
async function getMatchWithEntries(id) {
|
|
670
|
+
return prisma.match.findUnique({
|
|
671
|
+
where: { id },
|
|
672
|
+
include: {
|
|
673
|
+
entries: {
|
|
674
|
+
include: {
|
|
675
|
+
player: true
|
|
676
|
+
},
|
|
677
|
+
orderBy: {
|
|
678
|
+
position: "asc"
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
round: true
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
async function getPlayerTournamentMatches(playerId, tournamentId, include) {
|
|
686
|
+
return findMatches({
|
|
687
|
+
where: {
|
|
688
|
+
tournamentId,
|
|
689
|
+
entries: {
|
|
690
|
+
some: {
|
|
691
|
+
playerId
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
include: include ?? {
|
|
696
|
+
entries: {
|
|
697
|
+
include: {
|
|
698
|
+
player: true
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
round: true
|
|
702
|
+
},
|
|
703
|
+
orderBy: [{ round: { number: "asc" } }, { number: "asc" }]
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/entries.ts
|
|
708
|
+
async function createEntry(data) {
|
|
709
|
+
return prisma.entry.create({
|
|
710
|
+
data
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
async function createManyEntries(data) {
|
|
714
|
+
return prisma.entry.createMany({
|
|
715
|
+
data
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
async function findEntryById(id, include) {
|
|
719
|
+
return prisma.entry.findUnique({
|
|
720
|
+
where: { id },
|
|
721
|
+
include
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
async function findEntryByMatchAndPlayer(matchId, playerId, include) {
|
|
725
|
+
return prisma.entry.findUnique({
|
|
726
|
+
where: {
|
|
727
|
+
matchId_playerId: {
|
|
728
|
+
matchId,
|
|
729
|
+
playerId
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
include
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
async function findEntries(options = {}) {
|
|
736
|
+
return prisma.entry.findMany({
|
|
737
|
+
take: options.take,
|
|
738
|
+
skip: options.skip,
|
|
739
|
+
where: options.where,
|
|
740
|
+
orderBy: options.orderBy,
|
|
741
|
+
include: options.include
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
async function getMatchEntries(matchId, options = {}) {
|
|
745
|
+
return findEntries({
|
|
746
|
+
...options,
|
|
747
|
+
where: { matchId },
|
|
748
|
+
include: options.include ?? { player: true },
|
|
749
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
async function getPlayerEntries(playerId, options = {}) {
|
|
753
|
+
return findEntries({
|
|
754
|
+
...options,
|
|
755
|
+
where: { playerId },
|
|
756
|
+
include: options.include ?? { match: { include: { round: true, tournament: true } } }
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
async function getPlayerTournamentEntries(playerId, tournamentId, include) {
|
|
760
|
+
return findEntries({
|
|
761
|
+
where: {
|
|
762
|
+
playerId,
|
|
763
|
+
match: {
|
|
764
|
+
tournamentId
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
include: include ?? {
|
|
768
|
+
match: {
|
|
769
|
+
include: {
|
|
770
|
+
round: true,
|
|
771
|
+
entries: {
|
|
772
|
+
include: {
|
|
773
|
+
player: true
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
async function updateEntry(id, data) {
|
|
782
|
+
return prisma.entry.update({
|
|
783
|
+
where: { id },
|
|
784
|
+
data
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
async function deleteEntry(id) {
|
|
788
|
+
return prisma.entry.delete({
|
|
789
|
+
where: { id }
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
async function deleteEntriesByMatch(matchId) {
|
|
793
|
+
return prisma.entry.deleteMany({
|
|
794
|
+
where: { matchId }
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
async function countEntries(where) {
|
|
798
|
+
return prisma.entry.count({ where });
|
|
799
|
+
}
|
|
800
|
+
async function getPlayerEntryStats(playerId) {
|
|
801
|
+
const entries = await getPlayerEntries(playerId);
|
|
802
|
+
if (entries.length === 0) {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
const wins = entries.filter((e) => e.result === "WIN").length;
|
|
806
|
+
const losses = entries.filter((e) => e.result === "LOSS").length;
|
|
807
|
+
const ties = entries.filter((e) => e.result === "TIE").length;
|
|
808
|
+
return {
|
|
809
|
+
totalMatches: entries.length,
|
|
810
|
+
wins,
|
|
811
|
+
losses,
|
|
812
|
+
ties,
|
|
813
|
+
winRate: wins / entries.length
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/standings.ts
|
|
818
|
+
async function createStanding(data) {
|
|
819
|
+
const standingData = {
|
|
313
820
|
...data,
|
|
821
|
+
isFinals: data.isFinals ?? false,
|
|
314
822
|
decayedPoints: data.decayedPoints ?? data.totalPoints ?? 0
|
|
315
823
|
};
|
|
316
|
-
return prisma.
|
|
317
|
-
data:
|
|
824
|
+
return prisma.standing.create({
|
|
825
|
+
data: standingData
|
|
318
826
|
});
|
|
319
827
|
}
|
|
320
|
-
async function
|
|
321
|
-
const
|
|
828
|
+
async function createManyStandings(data) {
|
|
829
|
+
const standingsData = data.map((item) => ({
|
|
322
830
|
...item,
|
|
831
|
+
isFinals: item.isFinals ?? false,
|
|
323
832
|
decayedPoints: item.decayedPoints ?? item.totalPoints ?? 0
|
|
324
833
|
}));
|
|
325
|
-
return prisma.
|
|
326
|
-
data:
|
|
834
|
+
return prisma.standing.createMany({
|
|
835
|
+
data: standingsData
|
|
327
836
|
});
|
|
328
837
|
}
|
|
329
|
-
async function
|
|
330
|
-
return prisma.
|
|
838
|
+
async function findStandingById(id, include) {
|
|
839
|
+
return prisma.standing.findUnique({
|
|
331
840
|
where: { id },
|
|
332
841
|
include
|
|
333
842
|
});
|
|
334
843
|
}
|
|
335
|
-
async function
|
|
336
|
-
return prisma.
|
|
844
|
+
async function findStandingByPlayerAndTournament(playerId, tournamentId, isFinals, include) {
|
|
845
|
+
return prisma.standing.findUnique({
|
|
337
846
|
where: {
|
|
338
|
-
|
|
847
|
+
playerId_tournamentId_isFinals: {
|
|
339
848
|
playerId,
|
|
340
|
-
tournamentId
|
|
849
|
+
tournamentId,
|
|
850
|
+
isFinals
|
|
341
851
|
}
|
|
342
852
|
},
|
|
343
853
|
include
|
|
344
854
|
});
|
|
345
855
|
}
|
|
346
|
-
async function
|
|
347
|
-
return prisma.
|
|
856
|
+
async function findStandings(options = {}) {
|
|
857
|
+
return prisma.standing.findMany({
|
|
348
858
|
take: options.take,
|
|
349
859
|
skip: options.skip,
|
|
350
860
|
where: options.where,
|
|
@@ -352,45 +862,85 @@ async function findResults(options = {}) {
|
|
|
352
862
|
include: options.include
|
|
353
863
|
});
|
|
354
864
|
}
|
|
355
|
-
async function
|
|
356
|
-
return
|
|
865
|
+
async function getPlayerStandings(playerId, options = {}) {
|
|
866
|
+
return findStandings({
|
|
357
867
|
...options,
|
|
358
868
|
where: { playerId },
|
|
359
869
|
include: { tournament: true, ...options.include },
|
|
360
870
|
orderBy: { tournament: { date: "desc" } }
|
|
361
871
|
});
|
|
362
872
|
}
|
|
363
|
-
async function
|
|
364
|
-
return
|
|
873
|
+
async function getTournamentStandings(tournamentId, options = {}) {
|
|
874
|
+
return findStandings({
|
|
365
875
|
...options,
|
|
366
876
|
where: { tournamentId },
|
|
367
877
|
include: { player: true, ...options.include },
|
|
368
|
-
orderBy: { position: "asc" }
|
|
878
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
369
879
|
});
|
|
370
880
|
}
|
|
881
|
+
async function getQualifyingStandings(tournamentId, options = {}) {
|
|
882
|
+
return findStandings({
|
|
883
|
+
...options,
|
|
884
|
+
where: { tournamentId, isFinals: false },
|
|
885
|
+
include: { player: true, ...options.include },
|
|
886
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
async function getFinalsStandings(tournamentId, options = {}) {
|
|
890
|
+
return findStandings({
|
|
891
|
+
...options,
|
|
892
|
+
where: { tournamentId, isFinals: true },
|
|
893
|
+
include: { player: true, ...options.include },
|
|
894
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
async function getMergedStandings(tournamentId) {
|
|
898
|
+
const [finals, qualifying] = await Promise.all([
|
|
899
|
+
findStandings({
|
|
900
|
+
where: { tournamentId, isFinals: true },
|
|
901
|
+
orderBy: { position: "asc" },
|
|
902
|
+
include: { player: true }
|
|
903
|
+
}),
|
|
904
|
+
findStandings({
|
|
905
|
+
where: { tournamentId, isFinals: false },
|
|
906
|
+
orderBy: { position: "asc" },
|
|
907
|
+
include: { player: true }
|
|
908
|
+
})
|
|
909
|
+
]);
|
|
910
|
+
const finalistIds = new Set(finals.map((s) => s.playerId));
|
|
911
|
+
const nonFinalists = qualifying.filter((s) => !finalistIds.has(s.playerId));
|
|
912
|
+
return [
|
|
913
|
+
...finals.map((s) => ({ ...s, mergedPosition: s.position, isFinalist: true })),
|
|
914
|
+
...nonFinalists.map((s, i) => ({
|
|
915
|
+
...s,
|
|
916
|
+
mergedPosition: finals.length + i + 1,
|
|
917
|
+
isFinalist: false
|
|
918
|
+
}))
|
|
919
|
+
];
|
|
920
|
+
}
|
|
371
921
|
async function getPlayerTopFinishes(playerId, limit = 15) {
|
|
372
|
-
return
|
|
922
|
+
return findStandings({
|
|
373
923
|
where: { playerId },
|
|
374
924
|
take: limit,
|
|
375
925
|
include: { tournament: true },
|
|
376
926
|
orderBy: { decayedPoints: "desc" }
|
|
377
927
|
});
|
|
378
928
|
}
|
|
379
|
-
async function
|
|
380
|
-
return prisma.
|
|
929
|
+
async function updateStanding(id, data) {
|
|
930
|
+
return prisma.standing.update({
|
|
381
931
|
where: { id },
|
|
382
932
|
data
|
|
383
933
|
});
|
|
384
934
|
}
|
|
385
|
-
async function
|
|
386
|
-
const
|
|
935
|
+
async function updateStandingPoints(id, linearPoints, dynamicPoints, totalPoints) {
|
|
936
|
+
const standing = await findStandingById(id, {
|
|
387
937
|
tournament: true
|
|
388
938
|
});
|
|
389
|
-
if (!
|
|
390
|
-
throw new Error(`
|
|
939
|
+
if (!standing) {
|
|
940
|
+
throw new Error(`Standing with id ${id} not found`);
|
|
391
941
|
}
|
|
392
942
|
const now = /* @__PURE__ */ new Date();
|
|
393
|
-
const tournamentDate =
|
|
943
|
+
const tournamentDate = standing.tournament.date;
|
|
394
944
|
const ageInDays = Math.floor((now.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
395
945
|
const ageInYears = ageInDays / 365;
|
|
396
946
|
let decayMultiplier = 0;
|
|
@@ -404,7 +954,7 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
404
954
|
decayMultiplier = 0;
|
|
405
955
|
}
|
|
406
956
|
const decayedPoints = totalPoints * decayMultiplier;
|
|
407
|
-
return
|
|
957
|
+
return updateStanding(id, {
|
|
408
958
|
linearPoints,
|
|
409
959
|
dynamicPoints,
|
|
410
960
|
totalPoints,
|
|
@@ -413,50 +963,50 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
413
963
|
decayedPoints
|
|
414
964
|
});
|
|
415
965
|
}
|
|
416
|
-
async function
|
|
417
|
-
return prisma.
|
|
966
|
+
async function deleteStanding(id) {
|
|
967
|
+
return prisma.standing.delete({
|
|
418
968
|
where: { id }
|
|
419
969
|
});
|
|
420
970
|
}
|
|
421
|
-
async function
|
|
422
|
-
return prisma.
|
|
971
|
+
async function deleteStandingsByTournament(tournamentId) {
|
|
972
|
+
return prisma.standing.deleteMany({
|
|
423
973
|
where: { tournamentId }
|
|
424
974
|
});
|
|
425
975
|
}
|
|
426
|
-
async function
|
|
427
|
-
return prisma.
|
|
976
|
+
async function countStandings(where) {
|
|
977
|
+
return prisma.standing.count({ where });
|
|
428
978
|
}
|
|
429
979
|
async function getPlayerStats(playerId) {
|
|
430
|
-
const
|
|
431
|
-
if (
|
|
980
|
+
const standings = await getPlayerStandings(playerId);
|
|
981
|
+
if (standings.length === 0) {
|
|
432
982
|
return null;
|
|
433
983
|
}
|
|
434
|
-
const totalPoints =
|
|
435
|
-
const totalDecayedPoints =
|
|
436
|
-
const averagePosition =
|
|
437
|
-
const averageEfficiency =
|
|
438
|
-
const firstPlaceFinishes =
|
|
439
|
-
const topThreeFinishes =
|
|
984
|
+
const totalPoints = standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
985
|
+
const totalDecayedPoints = standings.reduce((sum, s) => sum + (s.decayedPoints || 0), 0);
|
|
986
|
+
const averagePosition = standings.reduce((sum, s) => sum + s.position, 0) / standings.length;
|
|
987
|
+
const averageEfficiency = standings.reduce((sum, s) => sum + (s.efficiency || 0), 0) / standings.length;
|
|
988
|
+
const firstPlaceFinishes = standings.filter((s) => s.position === 1).length;
|
|
989
|
+
const topThreeFinishes = standings.filter((s) => s.position <= 3).length;
|
|
440
990
|
return {
|
|
441
|
-
totalEvents:
|
|
991
|
+
totalEvents: standings.length,
|
|
442
992
|
totalPoints,
|
|
443
993
|
totalDecayedPoints,
|
|
444
|
-
averagePoints: totalPoints /
|
|
994
|
+
averagePoints: totalPoints / standings.length,
|
|
445
995
|
averagePosition,
|
|
446
996
|
averageFinish: averagePosition,
|
|
447
997
|
averageEfficiency,
|
|
448
998
|
firstPlaceFinishes,
|
|
449
999
|
topThreeFinishes,
|
|
450
|
-
bestFinish: Math.min(...
|
|
451
|
-
highestPoints: Math.max(...
|
|
1000
|
+
bestFinish: Math.min(...standings.map((s) => s.position)),
|
|
1001
|
+
highestPoints: Math.max(...standings.map((s) => s.totalPoints || 0))
|
|
452
1002
|
};
|
|
453
1003
|
}
|
|
454
1004
|
async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date()) {
|
|
455
|
-
const
|
|
1005
|
+
const standings = await findStandings({
|
|
456
1006
|
include: { tournament: true }
|
|
457
1007
|
});
|
|
458
|
-
const updates =
|
|
459
|
-
const tournamentDate =
|
|
1008
|
+
const updates = standings.map((standing) => {
|
|
1009
|
+
const tournamentDate = standing.tournament.date;
|
|
460
1010
|
const ageInDays = Math.floor(
|
|
461
1011
|
(referenceDate.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
462
1012
|
);
|
|
@@ -471,9 +1021,9 @@ async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date())
|
|
|
471
1021
|
} else {
|
|
472
1022
|
decayMultiplier = 0;
|
|
473
1023
|
}
|
|
474
|
-
const decayedPoints = (
|
|
475
|
-
return prisma.
|
|
476
|
-
where: { id:
|
|
1024
|
+
const decayedPoints = (standing.totalPoints || 0) * decayMultiplier;
|
|
1025
|
+
return prisma.standing.update({
|
|
1026
|
+
where: { id: standing.id },
|
|
477
1027
|
data: {
|
|
478
1028
|
ageInDays,
|
|
479
1029
|
decayMultiplier,
|
|
@@ -510,10 +1060,6 @@ async function createUserWithPlayer(userData, playerData) {
|
|
|
510
1060
|
id: true,
|
|
511
1061
|
playerNumber: true,
|
|
512
1062
|
name: true,
|
|
513
|
-
rating: true,
|
|
514
|
-
ratingDeviation: true,
|
|
515
|
-
ranking: true,
|
|
516
|
-
isRated: true,
|
|
517
1063
|
eventCount: true
|
|
518
1064
|
}
|
|
519
1065
|
}
|
|
@@ -543,10 +1089,6 @@ async function getUserWithPlayer(id) {
|
|
|
543
1089
|
id: true,
|
|
544
1090
|
playerNumber: true,
|
|
545
1091
|
name: true,
|
|
546
|
-
rating: true,
|
|
547
|
-
ratingDeviation: true,
|
|
548
|
-
ranking: true,
|
|
549
|
-
isRated: true,
|
|
550
1092
|
eventCount: true
|
|
551
1093
|
}
|
|
552
1094
|
}
|
|
@@ -566,10 +1108,6 @@ async function getUserByEmailWithPlayer(email) {
|
|
|
566
1108
|
id: true,
|
|
567
1109
|
playerNumber: true,
|
|
568
1110
|
name: true,
|
|
569
|
-
rating: true,
|
|
570
|
-
ratingDeviation: true,
|
|
571
|
-
ranking: true,
|
|
572
|
-
isRated: true,
|
|
573
1111
|
eventCount: true
|
|
574
1112
|
}
|
|
575
1113
|
}
|
|
@@ -612,10 +1150,6 @@ async function findUsers(params) {
|
|
|
612
1150
|
id: true,
|
|
613
1151
|
playerNumber: true,
|
|
614
1152
|
name: true,
|
|
615
|
-
rating: true,
|
|
616
|
-
ratingDeviation: true,
|
|
617
|
-
ranking: true,
|
|
618
|
-
isRated: true,
|
|
619
1153
|
eventCount: true
|
|
620
1154
|
}
|
|
621
1155
|
}
|
|
@@ -644,10 +1178,6 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
644
1178
|
id: true,
|
|
645
1179
|
playerNumber: true,
|
|
646
1180
|
name: true,
|
|
647
|
-
rating: true,
|
|
648
|
-
ratingDeviation: true,
|
|
649
|
-
ranking: true,
|
|
650
|
-
isRated: true,
|
|
651
1181
|
eventCount: true
|
|
652
1182
|
}
|
|
653
1183
|
}
|
|
@@ -656,32 +1186,396 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
656
1186
|
return user;
|
|
657
1187
|
});
|
|
658
1188
|
}
|
|
1189
|
+
|
|
1190
|
+
// src/api-keys.ts
|
|
1191
|
+
var MAX_API_KEYS_PER_USER = 5;
|
|
1192
|
+
async function createApiKey(data) {
|
|
1193
|
+
return prisma.apiKey.create({ data });
|
|
1194
|
+
}
|
|
1195
|
+
async function findApiKeyById(id) {
|
|
1196
|
+
return prisma.apiKey.findUnique({ where: { id } });
|
|
1197
|
+
}
|
|
1198
|
+
async function findApiKeysByPrefix(keyPrefix) {
|
|
1199
|
+
const keys = await prisma.apiKey.findMany({
|
|
1200
|
+
where: { keyPrefix },
|
|
1201
|
+
include: {
|
|
1202
|
+
user: {
|
|
1203
|
+
select: {
|
|
1204
|
+
id: true,
|
|
1205
|
+
email: true,
|
|
1206
|
+
role: true
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
return keys;
|
|
1212
|
+
}
|
|
1213
|
+
async function getUserApiKeys(userId) {
|
|
1214
|
+
return prisma.apiKey.findMany({
|
|
1215
|
+
where: { userId },
|
|
1216
|
+
select: {
|
|
1217
|
+
id: true,
|
|
1218
|
+
name: true,
|
|
1219
|
+
keyPrefix: true,
|
|
1220
|
+
expiresAt: true,
|
|
1221
|
+
lastUsedAt: true,
|
|
1222
|
+
createdAt: true
|
|
1223
|
+
},
|
|
1224
|
+
orderBy: { createdAt: "desc" }
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
async function countUserApiKeys(userId) {
|
|
1228
|
+
return prisma.apiKey.count({ where: { userId } });
|
|
1229
|
+
}
|
|
1230
|
+
async function updateApiKeyLastUsed(id) {
|
|
1231
|
+
await prisma.apiKey.update({
|
|
1232
|
+
where: { id },
|
|
1233
|
+
data: { lastUsedAt: /* @__PURE__ */ new Date() }
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
async function deleteApiKey(id) {
|
|
1237
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1238
|
+
}
|
|
1239
|
+
async function deleteUserApiKey(id, userId) {
|
|
1240
|
+
const key = await prisma.apiKey.findFirst({
|
|
1241
|
+
where: { id, userId }
|
|
1242
|
+
});
|
|
1243
|
+
if (!key) {
|
|
1244
|
+
return null;
|
|
1245
|
+
}
|
|
1246
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// src/locations.ts
|
|
1250
|
+
async function createLocation(data) {
|
|
1251
|
+
return prisma.location.create({
|
|
1252
|
+
data
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
async function findLocationById(id, include) {
|
|
1256
|
+
return prisma.location.findUnique({
|
|
1257
|
+
where: { id },
|
|
1258
|
+
include
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
async function findLocationByExternalId(externalId, include) {
|
|
1262
|
+
return prisma.location.findUnique({
|
|
1263
|
+
where: { externalId },
|
|
1264
|
+
include
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
async function findLocations(options = {}) {
|
|
1268
|
+
return prisma.location.findMany({
|
|
1269
|
+
take: options.take,
|
|
1270
|
+
skip: options.skip,
|
|
1271
|
+
where: options.where,
|
|
1272
|
+
orderBy: options.orderBy,
|
|
1273
|
+
include: options.include
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
async function searchLocations(query, limit = 20) {
|
|
1277
|
+
return findLocations({
|
|
1278
|
+
take: limit,
|
|
1279
|
+
where: {
|
|
1280
|
+
OR: [
|
|
1281
|
+
{ name: { contains: query, mode: "insensitive" } },
|
|
1282
|
+
{ city: { contains: query, mode: "insensitive" } }
|
|
1283
|
+
]
|
|
1284
|
+
},
|
|
1285
|
+
orderBy: { name: "asc" }
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
async function updateLocation(id, data) {
|
|
1289
|
+
return prisma.location.update({
|
|
1290
|
+
where: { id },
|
|
1291
|
+
data
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
async function deleteLocation(id) {
|
|
1295
|
+
return prisma.location.delete({
|
|
1296
|
+
where: { id }
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
async function countLocations(where) {
|
|
1300
|
+
return prisma.location.count({ where });
|
|
1301
|
+
}
|
|
1302
|
+
async function getLocationWithTournaments(id) {
|
|
1303
|
+
return prisma.location.findUnique({
|
|
1304
|
+
where: { id },
|
|
1305
|
+
include: {
|
|
1306
|
+
tournaments: {
|
|
1307
|
+
orderBy: {
|
|
1308
|
+
date: "desc"
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/blog-posts.ts
|
|
1316
|
+
var defaultInclude = {
|
|
1317
|
+
author: {
|
|
1318
|
+
select: { id: true, email: true }
|
|
1319
|
+
},
|
|
1320
|
+
tags: {
|
|
1321
|
+
select: { id: true, name: true, slug: true }
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
async function createBlogPost(data) {
|
|
1325
|
+
const { tagIds, ...postData } = data;
|
|
1326
|
+
return prisma.blogPost.create({
|
|
1327
|
+
data: {
|
|
1328
|
+
...postData,
|
|
1329
|
+
tags: tagIds?.length ? { connect: tagIds.map((id) => ({ id })) } : void 0
|
|
1330
|
+
},
|
|
1331
|
+
include: defaultInclude
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
async function findBlogPostById(id) {
|
|
1335
|
+
return prisma.blogPost.findUnique({
|
|
1336
|
+
where: { id },
|
|
1337
|
+
include: defaultInclude
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
async function findBlogPostBySlug(slug) {
|
|
1341
|
+
return prisma.blogPost.findUnique({
|
|
1342
|
+
where: { slug },
|
|
1343
|
+
include: defaultInclude
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
async function findBlogPosts(options = {}) {
|
|
1347
|
+
return prisma.blogPost.findMany({
|
|
1348
|
+
take: options.take,
|
|
1349
|
+
skip: options.skip,
|
|
1350
|
+
where: options.where,
|
|
1351
|
+
orderBy: options.orderBy ?? { createdAt: "desc" },
|
|
1352
|
+
include: options.include ?? defaultInclude
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
async function findPublishedBlogPosts(options = {}) {
|
|
1356
|
+
const { tagSlug, ...restOptions } = options;
|
|
1357
|
+
const where = {
|
|
1358
|
+
status: "PUBLISHED",
|
|
1359
|
+
publishedAt: { not: null },
|
|
1360
|
+
...tagSlug && {
|
|
1361
|
+
tags: {
|
|
1362
|
+
some: { slug: tagSlug }
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
return findBlogPosts({
|
|
1367
|
+
...restOptions,
|
|
1368
|
+
where,
|
|
1369
|
+
orderBy: options.orderBy ?? { publishedAt: "desc" }
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
async function searchBlogPosts(query, limit = 20, publishedOnly = true) {
|
|
1373
|
+
const where = {
|
|
1374
|
+
OR: [
|
|
1375
|
+
{ title: { contains: query, mode: "insensitive" } },
|
|
1376
|
+
{ excerpt: { contains: query, mode: "insensitive" } }
|
|
1377
|
+
],
|
|
1378
|
+
...publishedOnly && {
|
|
1379
|
+
status: "PUBLISHED",
|
|
1380
|
+
publishedAt: { not: null }
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
return findBlogPosts({
|
|
1384
|
+
take: limit,
|
|
1385
|
+
where,
|
|
1386
|
+
orderBy: { publishedAt: "desc" }
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
async function updateBlogPost(id, data) {
|
|
1390
|
+
const { tagIds, ...postData } = data;
|
|
1391
|
+
return prisma.blogPost.update({
|
|
1392
|
+
where: { id },
|
|
1393
|
+
data: {
|
|
1394
|
+
...postData,
|
|
1395
|
+
// If tagIds is provided, replace all tags
|
|
1396
|
+
...tagIds !== void 0 && {
|
|
1397
|
+
tags: {
|
|
1398
|
+
set: tagIds.map((tagId) => ({ id: tagId }))
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
include: defaultInclude
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
async function deleteBlogPost(id) {
|
|
1406
|
+
return prisma.blogPost.delete({
|
|
1407
|
+
where: { id }
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
async function countBlogPosts(where) {
|
|
1411
|
+
return prisma.blogPost.count({ where });
|
|
1412
|
+
}
|
|
1413
|
+
async function countPublishedBlogPosts(tagSlug) {
|
|
1414
|
+
return countBlogPosts({
|
|
1415
|
+
status: "PUBLISHED",
|
|
1416
|
+
publishedAt: { not: null },
|
|
1417
|
+
...tagSlug && {
|
|
1418
|
+
tags: {
|
|
1419
|
+
some: { slug: tagSlug }
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// src/blog-tags.ts
|
|
1426
|
+
async function createBlogTag(data) {
|
|
1427
|
+
return prisma.blogTag.create({
|
|
1428
|
+
data
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
async function findBlogTagById(id) {
|
|
1432
|
+
return prisma.blogTag.findUnique({
|
|
1433
|
+
where: { id }
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
async function findBlogTagBySlug(slug) {
|
|
1437
|
+
return prisma.blogTag.findUnique({
|
|
1438
|
+
where: { slug }
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
async function findBlogTags(options = {}) {
|
|
1442
|
+
return prisma.blogTag.findMany({
|
|
1443
|
+
take: options.take,
|
|
1444
|
+
skip: options.skip,
|
|
1445
|
+
where: options.where,
|
|
1446
|
+
orderBy: options.orderBy ?? { name: "asc" },
|
|
1447
|
+
include: options.include
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
async function searchBlogTags(query, limit = 20) {
|
|
1451
|
+
return findBlogTags({
|
|
1452
|
+
take: limit,
|
|
1453
|
+
where: {
|
|
1454
|
+
name: { contains: query, mode: "insensitive" }
|
|
1455
|
+
},
|
|
1456
|
+
orderBy: { name: "asc" }
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
async function updateBlogTag(id, data) {
|
|
1460
|
+
return prisma.blogTag.update({
|
|
1461
|
+
where: { id },
|
|
1462
|
+
data
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
async function deleteBlogTag(id) {
|
|
1466
|
+
return prisma.blogTag.delete({
|
|
1467
|
+
where: { id }
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
async function countBlogTags(where) {
|
|
1471
|
+
return prisma.blogTag.count({ where });
|
|
1472
|
+
}
|
|
1473
|
+
async function getBlogTagWithPostCount(id) {
|
|
1474
|
+
return prisma.blogTag.findUnique({
|
|
1475
|
+
where: { id },
|
|
1476
|
+
include: {
|
|
1477
|
+
_count: {
|
|
1478
|
+
select: { posts: true }
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
async function getBlogTagsWithPostCounts() {
|
|
1484
|
+
return prisma.blogTag.findMany({
|
|
1485
|
+
include: {
|
|
1486
|
+
_count: {
|
|
1487
|
+
select: { posts: true }
|
|
1488
|
+
}
|
|
1489
|
+
},
|
|
1490
|
+
orderBy: { name: "asc" }
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
659
1493
|
export {
|
|
1494
|
+
MAX_API_KEYS_PER_USER,
|
|
1495
|
+
applyRDDecayForInactivePlayers,
|
|
660
1496
|
connect,
|
|
1497
|
+
countBlogPosts,
|
|
1498
|
+
countBlogTags,
|
|
1499
|
+
countEntries,
|
|
1500
|
+
countLocations,
|
|
1501
|
+
countMatches,
|
|
1502
|
+
countOpprPlayerRankings,
|
|
1503
|
+
countOpprRankingHistory,
|
|
661
1504
|
countPlayers,
|
|
662
|
-
|
|
1505
|
+
countPublishedBlogPosts,
|
|
1506
|
+
countRounds,
|
|
1507
|
+
countStandings,
|
|
663
1508
|
countTournaments,
|
|
1509
|
+
countUserApiKeys,
|
|
664
1510
|
countUsers,
|
|
665
|
-
|
|
1511
|
+
createApiKey,
|
|
1512
|
+
createBlogPost,
|
|
1513
|
+
createBlogTag,
|
|
1514
|
+
createEntry,
|
|
1515
|
+
createLocation,
|
|
1516
|
+
createManyEntries,
|
|
1517
|
+
createManyMatches,
|
|
1518
|
+
createManyRounds,
|
|
1519
|
+
createManyStandings,
|
|
1520
|
+
createMatch,
|
|
1521
|
+
createOpprPlayerRanking,
|
|
1522
|
+
createOpprRankingHistory,
|
|
666
1523
|
createPlayer,
|
|
667
|
-
|
|
1524
|
+
createRound,
|
|
1525
|
+
createStanding,
|
|
668
1526
|
createTournament,
|
|
669
1527
|
createUser,
|
|
670
1528
|
createUserWithPlayer,
|
|
1529
|
+
deleteApiKey,
|
|
1530
|
+
deleteBlogPost,
|
|
1531
|
+
deleteBlogTag,
|
|
1532
|
+
deleteEntriesByMatch,
|
|
1533
|
+
deleteEntry,
|
|
1534
|
+
deleteLocation,
|
|
1535
|
+
deleteMatch,
|
|
1536
|
+
deleteMatchesByRound,
|
|
1537
|
+
deleteMatchesByTournament,
|
|
1538
|
+
deleteOpprPlayerRanking,
|
|
671
1539
|
deletePlayer,
|
|
672
|
-
|
|
673
|
-
|
|
1540
|
+
deleteRound,
|
|
1541
|
+
deleteRoundsByTournament,
|
|
1542
|
+
deleteStanding,
|
|
1543
|
+
deleteStandingsByTournament,
|
|
674
1544
|
deleteTournament,
|
|
675
1545
|
deleteUser,
|
|
1546
|
+
deleteUserApiKey,
|
|
676
1547
|
disconnect,
|
|
1548
|
+
findApiKeyById,
|
|
1549
|
+
findApiKeysByPrefix,
|
|
1550
|
+
findBlogPostById,
|
|
1551
|
+
findBlogPostBySlug,
|
|
1552
|
+
findBlogPosts,
|
|
1553
|
+
findBlogTagById,
|
|
1554
|
+
findBlogTagBySlug,
|
|
1555
|
+
findBlogTags,
|
|
1556
|
+
findEntries,
|
|
1557
|
+
findEntryById,
|
|
1558
|
+
findEntryByMatchAndPlayer,
|
|
1559
|
+
findLocationByExternalId,
|
|
1560
|
+
findLocationById,
|
|
1561
|
+
findLocations,
|
|
1562
|
+
findMatchById,
|
|
1563
|
+
findMatches,
|
|
1564
|
+
findOpprPlayerRankingById,
|
|
1565
|
+
findOpprPlayerRankingByPlayerId,
|
|
1566
|
+
findOpprPlayerRankings,
|
|
677
1567
|
findPlayerByExternalId,
|
|
678
1568
|
findPlayerById,
|
|
679
1569
|
findPlayerByPlayerNumber,
|
|
680
1570
|
findPlayerByUserEmail,
|
|
681
1571
|
findPlayers,
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1572
|
+
findPublishedBlogPosts,
|
|
1573
|
+
findRoundById,
|
|
1574
|
+
findRoundByTournamentAndNumber,
|
|
1575
|
+
findRounds,
|
|
1576
|
+
findStandingById,
|
|
1577
|
+
findStandingByPlayerAndTournament,
|
|
1578
|
+
findStandings,
|
|
685
1579
|
findTournamentByExternalId,
|
|
686
1580
|
findTournamentById,
|
|
687
1581
|
findTournaments,
|
|
@@ -689,34 +1583,70 @@ export {
|
|
|
689
1583
|
findUserById,
|
|
690
1584
|
findUsers,
|
|
691
1585
|
generateUniquePlayerNumber,
|
|
1586
|
+
getBlogTagWithPostCount,
|
|
1587
|
+
getBlogTagsWithPostCounts,
|
|
1588
|
+
getFinalsRounds,
|
|
1589
|
+
getFinalsStandings,
|
|
1590
|
+
getLatestOpprRankingHistory,
|
|
1591
|
+
getLocationWithTournaments,
|
|
692
1592
|
getMajorTournaments,
|
|
693
|
-
|
|
1593
|
+
getMatchEntries,
|
|
1594
|
+
getMatchWithEntries,
|
|
1595
|
+
getMergedStandings,
|
|
1596
|
+
getOpprRankingHistory,
|
|
1597
|
+
getOpprRankingHistoryByDateRange,
|
|
1598
|
+
getOrCreateOpprPlayerRanking,
|
|
1599
|
+
getPlayerEntries,
|
|
1600
|
+
getPlayerEntryStats,
|
|
1601
|
+
getPlayerStandings,
|
|
694
1602
|
getPlayerStats,
|
|
695
1603
|
getPlayerTopFinishes,
|
|
1604
|
+
getPlayerTournamentEntries,
|
|
1605
|
+
getPlayerTournamentMatches,
|
|
696
1606
|
getPlayerWithResults,
|
|
697
|
-
|
|
1607
|
+
getQualifyingRounds,
|
|
1608
|
+
getQualifyingStandings,
|
|
1609
|
+
getRatedOpprPlayers,
|
|
698
1610
|
getRecentTournaments,
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
1611
|
+
getRoundMatches,
|
|
1612
|
+
getRoundWithMatches,
|
|
1613
|
+
getTopPlayersByOpprRanking,
|
|
1614
|
+
getTopPlayersByOpprRating,
|
|
1615
|
+
getTournamentMatches,
|
|
1616
|
+
getTournamentRounds,
|
|
1617
|
+
getTournamentStandings,
|
|
702
1618
|
getTournamentStats,
|
|
1619
|
+
getTournamentWithMatches,
|
|
703
1620
|
getTournamentWithResults,
|
|
704
1621
|
getTournamentsByBoosterType,
|
|
705
1622
|
getTournamentsByDateRange,
|
|
1623
|
+
getUserApiKeys,
|
|
706
1624
|
getUserByEmailWithPlayer,
|
|
707
1625
|
getUserWithPlayer,
|
|
708
1626
|
isValidPlayerNumber,
|
|
709
1627
|
linkPlayerToUser,
|
|
710
1628
|
prisma,
|
|
711
1629
|
recalculateTimeDecay,
|
|
1630
|
+
searchBlogPosts,
|
|
1631
|
+
searchBlogTags,
|
|
1632
|
+
searchLocations,
|
|
712
1633
|
searchPlayers,
|
|
713
1634
|
searchTournaments,
|
|
714
1635
|
testConnection,
|
|
1636
|
+
updateApiKeyLastUsed,
|
|
1637
|
+
updateBlogPost,
|
|
1638
|
+
updateBlogTag,
|
|
1639
|
+
updateEntry,
|
|
1640
|
+
updateLocation,
|
|
1641
|
+
updateMatch,
|
|
1642
|
+
updateOpprPlayerRanking,
|
|
1643
|
+
updateOpprRatingAfterTournament,
|
|
715
1644
|
updatePlayer,
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1645
|
+
updateRound,
|
|
1646
|
+
updateStanding,
|
|
1647
|
+
updateStandingPoints,
|
|
719
1648
|
updateTournament,
|
|
720
1649
|
updateUser,
|
|
721
|
-
updateUserRefreshToken
|
|
1650
|
+
updateUserRefreshToken,
|
|
1651
|
+
updateWorldRankings
|
|
722
1652
|
};
|