@opprs/db-prisma 2.2.1 → 2.3.0
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 +51 -2
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +48 -2
- package/package.json +1 -1
- package/prisma/migrations/20260104000000_add_player_number/migration.sql +23 -0
- package/prisma/schema.prisma +2 -0
- package/prisma/seed.ts +50 -13
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
disconnect: () => disconnect,
|
|
40
40
|
findPlayerByExternalId: () => findPlayerByExternalId,
|
|
41
41
|
findPlayerById: () => findPlayerById,
|
|
42
|
+
findPlayerByPlayerNumber: () => findPlayerByPlayerNumber,
|
|
42
43
|
findPlayerByUserEmail: () => findPlayerByUserEmail,
|
|
43
44
|
findPlayers: () => findPlayers,
|
|
44
45
|
findResultById: () => findResultById,
|
|
@@ -50,6 +51,7 @@ __export(index_exports, {
|
|
|
50
51
|
findUserByEmail: () => findUserByEmail,
|
|
51
52
|
findUserById: () => findUserById,
|
|
52
53
|
findUsers: () => findUsers,
|
|
54
|
+
generateUniquePlayerNumber: () => generateUniquePlayerNumber,
|
|
53
55
|
getMajorTournaments: () => getMajorTournaments,
|
|
54
56
|
getPlayerResults: () => getPlayerResults,
|
|
55
57
|
getPlayerStats: () => getPlayerStats,
|
|
@@ -66,6 +68,7 @@ __export(index_exports, {
|
|
|
66
68
|
getTournamentsByDateRange: () => getTournamentsByDateRange,
|
|
67
69
|
getUserByEmailWithPlayer: () => getUserByEmailWithPlayer,
|
|
68
70
|
getUserWithPlayer: () => getUserWithPlayer,
|
|
71
|
+
isValidPlayerNumber: () => isValidPlayerNumber,
|
|
69
72
|
linkPlayerToUser: () => linkPlayerToUser,
|
|
70
73
|
prisma: () => prisma,
|
|
71
74
|
recalculateTimeDecay: () => recalculateTimeDecay,
|
|
@@ -107,10 +110,40 @@ async function testConnection() {
|
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
// src/player-number.ts
|
|
114
|
+
var MIN_PLAYER_NUMBER = 1e4;
|
|
115
|
+
var MAX_PLAYER_NUMBER = 99999;
|
|
116
|
+
var MAX_RETRIES = 10;
|
|
117
|
+
function generateRandomPlayerNumber() {
|
|
118
|
+
return Math.floor(Math.random() * (MAX_PLAYER_NUMBER - MIN_PLAYER_NUMBER + 1)) + MIN_PLAYER_NUMBER;
|
|
119
|
+
}
|
|
120
|
+
async function generateUniquePlayerNumber() {
|
|
121
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
122
|
+
const candidate = generateRandomPlayerNumber();
|
|
123
|
+
const existing = await prisma.player.findUnique({
|
|
124
|
+
where: { playerNumber: candidate },
|
|
125
|
+
select: { id: true }
|
|
126
|
+
});
|
|
127
|
+
if (!existing) {
|
|
128
|
+
return candidate;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Failed to generate unique player number after ${MAX_RETRIES} attempts. Consider increasing the number range or implementing a different allocation strategy.`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
function isValidPlayerNumber(playerNumber) {
|
|
136
|
+
return Number.isInteger(playerNumber) && playerNumber >= MIN_PLAYER_NUMBER && playerNumber <= MAX_PLAYER_NUMBER;
|
|
137
|
+
}
|
|
138
|
+
|
|
110
139
|
// src/players.ts
|
|
111
140
|
async function createPlayer(data) {
|
|
141
|
+
const playerNumber = data.playerNumber ?? await generateUniquePlayerNumber();
|
|
112
142
|
return prisma.player.create({
|
|
113
|
-
data
|
|
143
|
+
data: {
|
|
144
|
+
...data,
|
|
145
|
+
playerNumber
|
|
146
|
+
}
|
|
114
147
|
});
|
|
115
148
|
}
|
|
116
149
|
async function findPlayerById(id, include) {
|
|
@@ -125,6 +158,12 @@ async function findPlayerByExternalId(externalId, include) {
|
|
|
125
158
|
include
|
|
126
159
|
});
|
|
127
160
|
}
|
|
161
|
+
async function findPlayerByPlayerNumber(playerNumber, include) {
|
|
162
|
+
return prisma.player.findUnique({
|
|
163
|
+
where: { playerNumber },
|
|
164
|
+
include
|
|
165
|
+
});
|
|
166
|
+
}
|
|
128
167
|
async function findPlayerByUserEmail(email, include) {
|
|
129
168
|
const user = await prisma.user.findUnique({
|
|
130
169
|
where: { email },
|
|
@@ -540,9 +579,11 @@ async function createUser(data) {
|
|
|
540
579
|
}
|
|
541
580
|
async function createUserWithPlayer(userData, playerData) {
|
|
542
581
|
return prisma.$transaction(async (tx) => {
|
|
582
|
+
const playerNumber = await generateUniquePlayerNumber();
|
|
543
583
|
const player = await tx.player.create({
|
|
544
584
|
data: {
|
|
545
|
-
name: playerData.name
|
|
585
|
+
name: playerData.name,
|
|
586
|
+
playerNumber
|
|
546
587
|
}
|
|
547
588
|
});
|
|
548
589
|
const user = await tx.user.create({
|
|
@@ -554,6 +595,7 @@ async function createUserWithPlayer(userData, playerData) {
|
|
|
554
595
|
player: {
|
|
555
596
|
select: {
|
|
556
597
|
id: true,
|
|
598
|
+
playerNumber: true,
|
|
557
599
|
name: true,
|
|
558
600
|
rating: true,
|
|
559
601
|
ratingDeviation: true,
|
|
@@ -586,6 +628,7 @@ async function getUserWithPlayer(id) {
|
|
|
586
628
|
player: {
|
|
587
629
|
select: {
|
|
588
630
|
id: true,
|
|
631
|
+
playerNumber: true,
|
|
589
632
|
name: true,
|
|
590
633
|
rating: true,
|
|
591
634
|
ratingDeviation: true,
|
|
@@ -608,6 +651,7 @@ async function getUserByEmailWithPlayer(email) {
|
|
|
608
651
|
player: {
|
|
609
652
|
select: {
|
|
610
653
|
id: true,
|
|
654
|
+
playerNumber: true,
|
|
611
655
|
name: true,
|
|
612
656
|
rating: true,
|
|
613
657
|
ratingDeviation: true,
|
|
@@ -653,6 +697,7 @@ async function findUsers(params) {
|
|
|
653
697
|
player: {
|
|
654
698
|
select: {
|
|
655
699
|
id: true,
|
|
700
|
+
playerNumber: true,
|
|
656
701
|
name: true,
|
|
657
702
|
rating: true,
|
|
658
703
|
ratingDeviation: true,
|
|
@@ -684,6 +729,7 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
684
729
|
player: {
|
|
685
730
|
select: {
|
|
686
731
|
id: true,
|
|
732
|
+
playerNumber: true,
|
|
687
733
|
name: true,
|
|
688
734
|
rating: true,
|
|
689
735
|
ratingDeviation: true,
|
|
@@ -718,6 +764,7 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
718
764
|
disconnect,
|
|
719
765
|
findPlayerByExternalId,
|
|
720
766
|
findPlayerById,
|
|
767
|
+
findPlayerByPlayerNumber,
|
|
721
768
|
findPlayerByUserEmail,
|
|
722
769
|
findPlayers,
|
|
723
770
|
findResultById,
|
|
@@ -729,6 +776,7 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
729
776
|
findUserByEmail,
|
|
730
777
|
findUserById,
|
|
731
778
|
findUsers,
|
|
779
|
+
generateUniquePlayerNumber,
|
|
732
780
|
getMajorTournaments,
|
|
733
781
|
getPlayerResults,
|
|
734
782
|
getPlayerStats,
|
|
@@ -745,6 +793,7 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
745
793
|
getTournamentsByDateRange,
|
|
746
794
|
getUserByEmailWithPlayer,
|
|
747
795
|
getUserWithPlayer,
|
|
796
|
+
isValidPlayerNumber,
|
|
748
797
|
linkPlayerToUser,
|
|
749
798
|
prisma,
|
|
750
799
|
recalculateTimeDecay,
|
package/dist/index.d.cts
CHANGED
|
@@ -25,6 +25,7 @@ declare function testConnection(): Promise<boolean>;
|
|
|
25
25
|
*/
|
|
26
26
|
interface CreatePlayerInput {
|
|
27
27
|
externalId?: string;
|
|
28
|
+
playerNumber?: number;
|
|
28
29
|
name?: string;
|
|
29
30
|
rating?: number;
|
|
30
31
|
ratingDeviation?: number;
|
|
@@ -56,7 +57,7 @@ interface FindPlayersOptions {
|
|
|
56
57
|
include?: Prisma.PlayerInclude;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
|
-
* Creates a new player
|
|
60
|
+
* Creates a new player with auto-generated playerNumber
|
|
60
61
|
*/
|
|
61
62
|
declare function createPlayer(data: CreatePlayerInput): Promise<Player>;
|
|
62
63
|
/**
|
|
@@ -67,6 +68,10 @@ declare function findPlayerById(id: string, include?: Prisma.PlayerInclude): Pro
|
|
|
67
68
|
* Finds a player by external ID
|
|
68
69
|
*/
|
|
69
70
|
declare function findPlayerByExternalId(externalId: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Finds a player by player number
|
|
73
|
+
*/
|
|
74
|
+
declare function findPlayerByPlayerNumber(playerNumber: number, include?: Prisma.PlayerInclude): Promise<Player | null>;
|
|
70
75
|
/**
|
|
71
76
|
* Finds a player through their linked User's email
|
|
72
77
|
*/
|
|
@@ -184,6 +189,7 @@ declare function getPlayerWithResults(id: string): Promise<{
|
|
|
184
189
|
createdAt: Date;
|
|
185
190
|
updatedAt: Date;
|
|
186
191
|
externalId: string | null;
|
|
192
|
+
playerNumber: number;
|
|
187
193
|
rating: number;
|
|
188
194
|
ratingDeviation: number;
|
|
189
195
|
ranking: number | null;
|
|
@@ -197,6 +203,18 @@ declare function getPlayerWithResults(id: string): Promise<{
|
|
|
197
203
|
*/
|
|
198
204
|
declare function searchPlayers(query: string, limit?: number): Promise<Player[]>;
|
|
199
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Generates a unique player number with collision handling.
|
|
208
|
+
* Retries up to MAX_RETRIES times if collision occurs.
|
|
209
|
+
*
|
|
210
|
+
* @throws Error if unable to generate unique number after max retries
|
|
211
|
+
*/
|
|
212
|
+
declare function generateUniquePlayerNumber(): Promise<number>;
|
|
213
|
+
/**
|
|
214
|
+
* Validates that a player number is in the valid range
|
|
215
|
+
*/
|
|
216
|
+
declare function isValidPlayerNumber(playerNumber: number): boolean;
|
|
217
|
+
|
|
200
218
|
/**
|
|
201
219
|
* Input for creating a new tournament
|
|
202
220
|
*/
|
|
@@ -299,6 +317,7 @@ declare function getTournamentWithResults(id: string): Promise<({
|
|
|
299
317
|
createdAt: Date;
|
|
300
318
|
updatedAt: Date;
|
|
301
319
|
externalId: string | null;
|
|
320
|
+
playerNumber: number;
|
|
302
321
|
rating: number;
|
|
303
322
|
ratingDeviation: number;
|
|
304
323
|
ranking: number | null;
|
|
@@ -358,6 +377,7 @@ declare function getTournamentStats(id: string): Promise<{
|
|
|
358
377
|
createdAt: Date;
|
|
359
378
|
updatedAt: Date;
|
|
360
379
|
externalId: string | null;
|
|
380
|
+
playerNumber: number;
|
|
361
381
|
rating: number;
|
|
362
382
|
ratingDeviation: number;
|
|
363
383
|
ranking: number | null;
|
|
@@ -571,6 +591,7 @@ interface UserWithPlayer {
|
|
|
571
591
|
updatedAt: Date;
|
|
572
592
|
player: {
|
|
573
593
|
id: string;
|
|
594
|
+
playerNumber: number;
|
|
574
595
|
name: string | null;
|
|
575
596
|
rating: number;
|
|
576
597
|
ratingDeviation: number;
|
|
@@ -700,4 +721,4 @@ interface ConnectionStatus {
|
|
|
700
721
|
error?: string;
|
|
701
722
|
}
|
|
702
723
|
|
|
703
|
-
export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByExternalId, findPlayerById, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
|
|
724
|
+
export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByExternalId, findPlayerById, findPlayerByPlayerNumber, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, generateUniquePlayerNumber, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, isValidPlayerNumber, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ declare function testConnection(): Promise<boolean>;
|
|
|
25
25
|
*/
|
|
26
26
|
interface CreatePlayerInput {
|
|
27
27
|
externalId?: string;
|
|
28
|
+
playerNumber?: number;
|
|
28
29
|
name?: string;
|
|
29
30
|
rating?: number;
|
|
30
31
|
ratingDeviation?: number;
|
|
@@ -56,7 +57,7 @@ interface FindPlayersOptions {
|
|
|
56
57
|
include?: Prisma.PlayerInclude;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
|
-
* Creates a new player
|
|
60
|
+
* Creates a new player with auto-generated playerNumber
|
|
60
61
|
*/
|
|
61
62
|
declare function createPlayer(data: CreatePlayerInput): Promise<Player>;
|
|
62
63
|
/**
|
|
@@ -67,6 +68,10 @@ declare function findPlayerById(id: string, include?: Prisma.PlayerInclude): Pro
|
|
|
67
68
|
* Finds a player by external ID
|
|
68
69
|
*/
|
|
69
70
|
declare function findPlayerByExternalId(externalId: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Finds a player by player number
|
|
73
|
+
*/
|
|
74
|
+
declare function findPlayerByPlayerNumber(playerNumber: number, include?: Prisma.PlayerInclude): Promise<Player | null>;
|
|
70
75
|
/**
|
|
71
76
|
* Finds a player through their linked User's email
|
|
72
77
|
*/
|
|
@@ -184,6 +189,7 @@ declare function getPlayerWithResults(id: string): Promise<{
|
|
|
184
189
|
createdAt: Date;
|
|
185
190
|
updatedAt: Date;
|
|
186
191
|
externalId: string | null;
|
|
192
|
+
playerNumber: number;
|
|
187
193
|
rating: number;
|
|
188
194
|
ratingDeviation: number;
|
|
189
195
|
ranking: number | null;
|
|
@@ -197,6 +203,18 @@ declare function getPlayerWithResults(id: string): Promise<{
|
|
|
197
203
|
*/
|
|
198
204
|
declare function searchPlayers(query: string, limit?: number): Promise<Player[]>;
|
|
199
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Generates a unique player number with collision handling.
|
|
208
|
+
* Retries up to MAX_RETRIES times if collision occurs.
|
|
209
|
+
*
|
|
210
|
+
* @throws Error if unable to generate unique number after max retries
|
|
211
|
+
*/
|
|
212
|
+
declare function generateUniquePlayerNumber(): Promise<number>;
|
|
213
|
+
/**
|
|
214
|
+
* Validates that a player number is in the valid range
|
|
215
|
+
*/
|
|
216
|
+
declare function isValidPlayerNumber(playerNumber: number): boolean;
|
|
217
|
+
|
|
200
218
|
/**
|
|
201
219
|
* Input for creating a new tournament
|
|
202
220
|
*/
|
|
@@ -299,6 +317,7 @@ declare function getTournamentWithResults(id: string): Promise<({
|
|
|
299
317
|
createdAt: Date;
|
|
300
318
|
updatedAt: Date;
|
|
301
319
|
externalId: string | null;
|
|
320
|
+
playerNumber: number;
|
|
302
321
|
rating: number;
|
|
303
322
|
ratingDeviation: number;
|
|
304
323
|
ranking: number | null;
|
|
@@ -358,6 +377,7 @@ declare function getTournamentStats(id: string): Promise<{
|
|
|
358
377
|
createdAt: Date;
|
|
359
378
|
updatedAt: Date;
|
|
360
379
|
externalId: string | null;
|
|
380
|
+
playerNumber: number;
|
|
361
381
|
rating: number;
|
|
362
382
|
ratingDeviation: number;
|
|
363
383
|
ranking: number | null;
|
|
@@ -571,6 +591,7 @@ interface UserWithPlayer {
|
|
|
571
591
|
updatedAt: Date;
|
|
572
592
|
player: {
|
|
573
593
|
id: string;
|
|
594
|
+
playerNumber: number;
|
|
574
595
|
name: string | null;
|
|
575
596
|
rating: number;
|
|
576
597
|
ratingDeviation: number;
|
|
@@ -700,4 +721,4 @@ interface ConnectionStatus {
|
|
|
700
721
|
error?: string;
|
|
701
722
|
}
|
|
702
723
|
|
|
703
|
-
export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByExternalId, findPlayerById, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
|
|
724
|
+
export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByExternalId, findPlayerById, findPlayerByPlayerNumber, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, generateUniquePlayerNumber, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, isValidPlayerNumber, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
|
package/dist/index.js
CHANGED
|
@@ -23,10 +23,40 @@ async function testConnection() {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// src/player-number.ts
|
|
27
|
+
var MIN_PLAYER_NUMBER = 1e4;
|
|
28
|
+
var MAX_PLAYER_NUMBER = 99999;
|
|
29
|
+
var MAX_RETRIES = 10;
|
|
30
|
+
function generateRandomPlayerNumber() {
|
|
31
|
+
return Math.floor(Math.random() * (MAX_PLAYER_NUMBER - MIN_PLAYER_NUMBER + 1)) + MIN_PLAYER_NUMBER;
|
|
32
|
+
}
|
|
33
|
+
async function generateUniquePlayerNumber() {
|
|
34
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
35
|
+
const candidate = generateRandomPlayerNumber();
|
|
36
|
+
const existing = await prisma.player.findUnique({
|
|
37
|
+
where: { playerNumber: candidate },
|
|
38
|
+
select: { id: true }
|
|
39
|
+
});
|
|
40
|
+
if (!existing) {
|
|
41
|
+
return candidate;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Failed to generate unique player number after ${MAX_RETRIES} attempts. Consider increasing the number range or implementing a different allocation strategy.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function isValidPlayerNumber(playerNumber) {
|
|
49
|
+
return Number.isInteger(playerNumber) && playerNumber >= MIN_PLAYER_NUMBER && playerNumber <= MAX_PLAYER_NUMBER;
|
|
50
|
+
}
|
|
51
|
+
|
|
26
52
|
// src/players.ts
|
|
27
53
|
async function createPlayer(data) {
|
|
54
|
+
const playerNumber = data.playerNumber ?? await generateUniquePlayerNumber();
|
|
28
55
|
return prisma.player.create({
|
|
29
|
-
data
|
|
56
|
+
data: {
|
|
57
|
+
...data,
|
|
58
|
+
playerNumber
|
|
59
|
+
}
|
|
30
60
|
});
|
|
31
61
|
}
|
|
32
62
|
async function findPlayerById(id, include) {
|
|
@@ -41,6 +71,12 @@ async function findPlayerByExternalId(externalId, include) {
|
|
|
41
71
|
include
|
|
42
72
|
});
|
|
43
73
|
}
|
|
74
|
+
async function findPlayerByPlayerNumber(playerNumber, include) {
|
|
75
|
+
return prisma.player.findUnique({
|
|
76
|
+
where: { playerNumber },
|
|
77
|
+
include
|
|
78
|
+
});
|
|
79
|
+
}
|
|
44
80
|
async function findPlayerByUserEmail(email, include) {
|
|
45
81
|
const user = await prisma.user.findUnique({
|
|
46
82
|
where: { email },
|
|
@@ -456,9 +492,11 @@ async function createUser(data) {
|
|
|
456
492
|
}
|
|
457
493
|
async function createUserWithPlayer(userData, playerData) {
|
|
458
494
|
return prisma.$transaction(async (tx) => {
|
|
495
|
+
const playerNumber = await generateUniquePlayerNumber();
|
|
459
496
|
const player = await tx.player.create({
|
|
460
497
|
data: {
|
|
461
|
-
name: playerData.name
|
|
498
|
+
name: playerData.name,
|
|
499
|
+
playerNumber
|
|
462
500
|
}
|
|
463
501
|
});
|
|
464
502
|
const user = await tx.user.create({
|
|
@@ -470,6 +508,7 @@ async function createUserWithPlayer(userData, playerData) {
|
|
|
470
508
|
player: {
|
|
471
509
|
select: {
|
|
472
510
|
id: true,
|
|
511
|
+
playerNumber: true,
|
|
473
512
|
name: true,
|
|
474
513
|
rating: true,
|
|
475
514
|
ratingDeviation: true,
|
|
@@ -502,6 +541,7 @@ async function getUserWithPlayer(id) {
|
|
|
502
541
|
player: {
|
|
503
542
|
select: {
|
|
504
543
|
id: true,
|
|
544
|
+
playerNumber: true,
|
|
505
545
|
name: true,
|
|
506
546
|
rating: true,
|
|
507
547
|
ratingDeviation: true,
|
|
@@ -524,6 +564,7 @@ async function getUserByEmailWithPlayer(email) {
|
|
|
524
564
|
player: {
|
|
525
565
|
select: {
|
|
526
566
|
id: true,
|
|
567
|
+
playerNumber: true,
|
|
527
568
|
name: true,
|
|
528
569
|
rating: true,
|
|
529
570
|
ratingDeviation: true,
|
|
@@ -569,6 +610,7 @@ async function findUsers(params) {
|
|
|
569
610
|
player: {
|
|
570
611
|
select: {
|
|
571
612
|
id: true,
|
|
613
|
+
playerNumber: true,
|
|
572
614
|
name: true,
|
|
573
615
|
rating: true,
|
|
574
616
|
ratingDeviation: true,
|
|
@@ -600,6 +642,7 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
600
642
|
player: {
|
|
601
643
|
select: {
|
|
602
644
|
id: true,
|
|
645
|
+
playerNumber: true,
|
|
603
646
|
name: true,
|
|
604
647
|
rating: true,
|
|
605
648
|
ratingDeviation: true,
|
|
@@ -633,6 +676,7 @@ export {
|
|
|
633
676
|
disconnect,
|
|
634
677
|
findPlayerByExternalId,
|
|
635
678
|
findPlayerById,
|
|
679
|
+
findPlayerByPlayerNumber,
|
|
636
680
|
findPlayerByUserEmail,
|
|
637
681
|
findPlayers,
|
|
638
682
|
findResultById,
|
|
@@ -644,6 +688,7 @@ export {
|
|
|
644
688
|
findUserByEmail,
|
|
645
689
|
findUserById,
|
|
646
690
|
findUsers,
|
|
691
|
+
generateUniquePlayerNumber,
|
|
647
692
|
getMajorTournaments,
|
|
648
693
|
getPlayerResults,
|
|
649
694
|
getPlayerStats,
|
|
@@ -660,6 +705,7 @@ export {
|
|
|
660
705
|
getTournamentsByDateRange,
|
|
661
706
|
getUserByEmailWithPlayer,
|
|
662
707
|
getUserWithPlayer,
|
|
708
|
+
isValidPlayerNumber,
|
|
663
709
|
linkPlayerToUser,
|
|
664
710
|
prisma,
|
|
665
711
|
recalculateTimeDecay,
|
package/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- AlterTable
|
|
2
|
+
-- First add playerNumber as nullable, populate it, then make it required
|
|
3
|
+
ALTER TABLE "Player" ADD COLUMN "playerNumber" INTEGER;
|
|
4
|
+
|
|
5
|
+
-- Populate existing players with unique 5-digit player numbers
|
|
6
|
+
-- Use a sequence starting from 10000, incrementing for each existing row
|
|
7
|
+
WITH numbered_players AS (
|
|
8
|
+
SELECT id, ROW_NUMBER() OVER (ORDER BY "createdAt") + 9999 AS num
|
|
9
|
+
FROM "Player"
|
|
10
|
+
)
|
|
11
|
+
UPDATE "Player"
|
|
12
|
+
SET "playerNumber" = numbered_players.num::INTEGER
|
|
13
|
+
FROM numbered_players
|
|
14
|
+
WHERE "Player".id = numbered_players.id;
|
|
15
|
+
|
|
16
|
+
-- Make the column required and add unique constraint
|
|
17
|
+
ALTER TABLE "Player" ALTER COLUMN "playerNumber" SET NOT NULL;
|
|
18
|
+
|
|
19
|
+
-- CreateIndex
|
|
20
|
+
CREATE UNIQUE INDEX "Player_playerNumber_key" ON "Player"("playerNumber");
|
|
21
|
+
|
|
22
|
+
-- CreateIndex
|
|
23
|
+
CREATE INDEX "Player_playerNumber_idx" ON "Player"("playerNumber");
|
package/prisma/schema.prisma
CHANGED
|
@@ -18,6 +18,7 @@ model Player {
|
|
|
18
18
|
|
|
19
19
|
// Player identification
|
|
20
20
|
externalId String? @unique // External ID from OPPR or other systems
|
|
21
|
+
playerNumber Int @unique // 5-digit unique identifier (10000-99999)
|
|
21
22
|
name String?
|
|
22
23
|
|
|
23
24
|
// OPPR Rating fields
|
|
@@ -36,6 +37,7 @@ model Player {
|
|
|
36
37
|
user User?
|
|
37
38
|
|
|
38
39
|
@@index([externalId])
|
|
40
|
+
@@index([playerNumber])
|
|
39
41
|
@@index([rating])
|
|
40
42
|
@@index([ranking])
|
|
41
43
|
}
|
package/prisma/seed.ts
CHANGED
|
@@ -13,6 +13,7 @@ async function main() {
|
|
|
13
13
|
const playerData = [
|
|
14
14
|
{
|
|
15
15
|
externalId: 'player-1',
|
|
16
|
+
playerNumber: 10001,
|
|
16
17
|
name: 'Alice Champion',
|
|
17
18
|
rating: 1850,
|
|
18
19
|
ratingDeviation: 50,
|
|
@@ -22,6 +23,7 @@ async function main() {
|
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
externalId: 'player-2',
|
|
26
|
+
playerNumber: 10002,
|
|
25
27
|
name: 'Bob Wizard',
|
|
26
28
|
rating: 1750,
|
|
27
29
|
ratingDeviation: 60,
|
|
@@ -31,6 +33,7 @@ async function main() {
|
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
externalId: 'player-3',
|
|
36
|
+
playerNumber: 10003,
|
|
34
37
|
name: 'Charlie Flipper',
|
|
35
38
|
rating: 1650,
|
|
36
39
|
ratingDeviation: 75,
|
|
@@ -40,6 +43,7 @@ async function main() {
|
|
|
40
43
|
},
|
|
41
44
|
{
|
|
42
45
|
externalId: 'player-4',
|
|
46
|
+
playerNumber: 10004,
|
|
43
47
|
name: 'Diana Tilt',
|
|
44
48
|
rating: 1550,
|
|
45
49
|
ratingDeviation: 100,
|
|
@@ -49,6 +53,7 @@ async function main() {
|
|
|
49
53
|
},
|
|
50
54
|
{
|
|
51
55
|
externalId: 'player-5',
|
|
56
|
+
playerNumber: 10005,
|
|
52
57
|
name: 'Eve Plunger',
|
|
53
58
|
rating: 1300,
|
|
54
59
|
ratingDeviation: 150,
|
|
@@ -90,27 +95,59 @@ async function main() {
|
|
|
90
95
|
|
|
91
96
|
console.log(`✓ Created ${await prisma.player.count()} players`);
|
|
92
97
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
98
|
+
// Seed users from environment variables (development only)
|
|
99
|
+
const seedAdminEmail = process.env.SEED_ADMIN_EMAIL;
|
|
100
|
+
const seedAdminPassword = process.env.SEED_ADMIN_PASSWORD;
|
|
101
|
+
const seedTestEmail = process.env.SEED_TEST_EMAIL;
|
|
102
|
+
const seedTestPassword = process.env.SEED_TEST_PASSWORD;
|
|
103
|
+
|
|
104
|
+
// Create admin user if credentials provided
|
|
105
|
+
if (seedAdminEmail && seedAdminPassword) {
|
|
106
|
+
console.log('Creating admin user...');
|
|
107
|
+
const adminPasswordHash = await bcrypt.hash(seedAdminPassword, BCRYPT_SALT_ROUNDS);
|
|
108
|
+
await prisma.user.upsert({
|
|
109
|
+
where: { email: seedAdminEmail },
|
|
110
|
+
update: { passwordHash: adminPasswordHash, role: 'ADMIN' },
|
|
111
|
+
create: { email: seedAdminEmail, passwordHash: adminPasswordHash, role: 'ADMIN' },
|
|
112
|
+
});
|
|
113
|
+
console.log(`✓ Created admin user (${seedAdminEmail})`);
|
|
114
|
+
} else {
|
|
115
|
+
console.log('⏭ Skipping admin user (SEED_ADMIN_EMAIL/SEED_ADMIN_PASSWORD not set)');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Create test user if credentials provided (linked to Alice Champion)
|
|
119
|
+
if (seedTestEmail && seedTestPassword) {
|
|
120
|
+
console.log('Creating test user...');
|
|
121
|
+
const testPasswordHash = await bcrypt.hash(seedTestPassword, BCRYPT_SALT_ROUNDS);
|
|
122
|
+
await prisma.user.upsert({
|
|
123
|
+
where: { email: seedTestEmail },
|
|
124
|
+
update: { passwordHash: testPasswordHash, role: 'USER', playerId: player1.id },
|
|
125
|
+
create: { email: seedTestEmail, passwordHash: testPasswordHash, role: 'USER', playerId: player1.id },
|
|
126
|
+
});
|
|
127
|
+
console.log(`✓ Created test user (${seedTestEmail}) linked to ${player1.name}`);
|
|
128
|
+
} else {
|
|
129
|
+
console.log('⏭ Skipping test user (SEED_TEST_EMAIL/SEED_TEST_PASSWORD not set)');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Create admin user
|
|
133
|
+
console.log('Creating admin user...');
|
|
134
|
+
const adminPassword = 'AdminPassword123!';
|
|
135
|
+
const adminPasswordHash = await bcrypt.hash(adminPassword, BCRYPT_SALT_ROUNDS);
|
|
97
136
|
|
|
98
137
|
await prisma.user.upsert({
|
|
99
|
-
where: { email: '
|
|
138
|
+
where: { email: 'admin@example.com' },
|
|
100
139
|
update: {
|
|
101
|
-
passwordHash,
|
|
102
|
-
role: '
|
|
103
|
-
playerId: player1.id,
|
|
140
|
+
passwordHash: adminPasswordHash,
|
|
141
|
+
role: 'ADMIN',
|
|
104
142
|
},
|
|
105
143
|
create: {
|
|
106
|
-
email: '
|
|
107
|
-
passwordHash,
|
|
108
|
-
role: '
|
|
109
|
-
playerId: player1.id,
|
|
144
|
+
email: 'admin@example.com',
|
|
145
|
+
passwordHash: adminPasswordHash,
|
|
146
|
+
role: 'ADMIN',
|
|
110
147
|
},
|
|
111
148
|
});
|
|
112
149
|
|
|
113
|
-
console.log(`✓ Created
|
|
150
|
+
console.log(`✓ Created admin user (admin@example.com / ${adminPassword})`);
|
|
114
151
|
|
|
115
152
|
// Create sample tournaments (using upsert for idempotency)
|
|
116
153
|
console.log('Creating tournaments...');
|