feat(rooms): migrate multiplayer room storage from memory to PostgreSQL

This commit is contained in:
jessy-david-dev
2026-04-11 17:37:56 +02:00
parent e6aa0badac
commit 552bb17c15
12 changed files with 2103 additions and 84 deletions
+1
View File
@@ -42,3 +42,4 @@ next-env.d.ts
.claude/ .claude/
*.db *.db
*.sql
+61 -22
View File
@@ -3,18 +3,36 @@
import { NextRequest } from "next/server"; import { NextRequest } from "next/server";
import type { Room, Player } from "../route"; import type { Room, Player } from "../route";
import { prisma } from "../../../../lib/prisma";
// Acces au singleton function dbToRoom(row: {
code: string;
declare global { players: unknown;
var __wikirooms: Map<string, Room> | undefined; phase: string;
} round: number;
totalRounds: number;
function getRooms(): Map<string, Room> { maxPlayers: number;
if (!global.__wikirooms) { startArticle: string;
global.__wikirooms = new Map(); targetArticle: string;
} roundWinner: string | null;
return global.__wikirooms; countdownStart: bigint | null;
roundStart: bigint | null;
createdAt: bigint;
}): Room {
return {
code: row.code,
players: row.players as Player[],
phase: row.phase as Room["phase"],
round: row.round,
totalRounds: row.totalRounds,
maxPlayers: row.maxPlayers,
startArticle: row.startArticle,
targetArticle: row.targetArticle,
roundWinner: row.roundWinner,
countdownStart: row.countdownStart !== null ? Number(row.countdownStart) : null,
roundStart: row.roundStart !== null ? Number(row.roundStart) : null,
createdAt: Number(row.createdAt),
};
} }
function generatePlayerId(): string { function generatePlayerId(): string {
@@ -24,11 +42,9 @@ function generatePlayerId(): string {
// Timeout joueur inactif : 15s // Timeout joueur inactif : 15s
const PLAYER_TIMEOUT_MS = 15_000; const PLAYER_TIMEOUT_MS = 15_000;
function prunePlayers(room: Room) { function prunePlayers(players: Player[]): Player[] {
const now = Date.now(); const now = Date.now();
room.players = room.players.filter( return players.filter((p) => now - p.lastSeen < PLAYER_TIMEOUT_MS);
(p) => now - p.lastSeen < PLAYER_TIMEOUT_MS,
);
} }
// PATCH /api/rooms/[code] // PATCH /api/rooms/[code]
@@ -37,13 +53,14 @@ export async function PATCH(
{ params }: { params: Promise<{ code: string }> }, { params }: { params: Promise<{ code: string }> },
) { ) {
const { code } = await params; const { code } = await params;
const rooms = getRooms(); const row = await prisma.room.findUnique({ where: { code: code.toUpperCase() } });
const room = rooms.get(code.toUpperCase());
if (!room) { if (!row) {
return Response.json({ error: "Room introuvable" }, { status: 404 }); return Response.json({ error: "Room introuvable" }, { status: 404 });
} }
const room = dbToRoom(row);
const body = await request.json(); const body = await request.json();
const { action, playerId, playerName, article, startArticle, targetArticle } = const { action, playerId, playerName, article, startArticle, targetArticle } =
body as { body as {
@@ -56,7 +73,7 @@ export async function PATCH(
}; };
// Nettoyer les joueurs inactifs avant chaque action // Nettoyer les joueurs inactifs avant chaque action
prunePlayers(room); room.players = prunePlayers(room.players);
switch (action) { switch (action) {
// Rejoindre // Rejoindre
@@ -92,6 +109,7 @@ export async function PATCH(
lastSeen: Date.now(), lastSeen: Date.now(),
}; };
room.players.push(player); room.players.push(player);
await saveRoom(room);
return Response.json({ room, playerId: newId }); return Response.json({ room, playerId: newId });
} }
@@ -101,6 +119,7 @@ export async function PATCH(
if (player) { if (player) {
player.lastSeen = Date.now(); player.lastSeen = Date.now();
} }
await saveRoom(room);
return Response.json({ room }); return Response.json({ room });
} }
@@ -144,6 +163,7 @@ export async function PATCH(
p.hasWon = false; p.hasWon = false;
} }
await saveRoom(room);
return Response.json({ room }); return Response.json({ room });
} }
@@ -152,11 +172,11 @@ export async function PATCH(
if (room.phase !== "countdown") { if (room.phase !== "countdown") {
return Response.json({ room }); return Response.json({ room });
} }
// On laisse les clients gerer le timing - le 1er qui appelle play apres 3s active
const elapsed = Date.now() - (room.countdownStart ?? 0); const elapsed = Date.now() - (room.countdownStart ?? 0);
if (elapsed >= 3000) { if (elapsed >= 3000) {
room.phase = "playing"; room.phase = "playing";
room.roundStart = Date.now(); room.roundStart = Date.now();
await saveRoom(room);
} }
return Response.json({ room }); return Response.json({ room });
} }
@@ -177,7 +197,6 @@ export async function PATCH(
player.currentArticle = article; player.currentArticle = article;
player.lastSeen = Date.now(); player.lastSeen = Date.now();
// Verifier si le joueur a atteint la cible
const normalize = (s: string) => const normalize = (s: string) =>
decodeURIComponent(s).replace(/_/g, " ").toLowerCase().trim(); decodeURIComponent(s).replace(/_/g, " ").toLowerCase().trim();
@@ -187,7 +206,6 @@ export async function PATCH(
) { ) {
player.hasWon = true; player.hasWon = true;
// 1er joueur a gagner = +10 points
const alreadyWon = room.players.some( const alreadyWon = room.players.some(
(p) => p.hasWon && p.id !== player.id, (p) => p.hasWon && p.id !== player.id,
); );
@@ -198,6 +216,7 @@ export async function PATCH(
} }
} }
await saveRoom(room);
return Response.json({ room }); return Response.json({ room });
} }
@@ -218,6 +237,7 @@ export async function PATCH(
p.hasWon = false; p.hasWon = false;
p.currentArticle = ""; p.currentArticle = "";
} }
await saveRoom(room);
return Response.json({ room }); return Response.json({ room });
} }
@@ -240,6 +260,7 @@ export async function PATCH(
p.hasWon = false; p.hasWon = false;
p.currentArticle = ""; p.currentArticle = "";
} }
await saveRoom(room);
return Response.json({ room }); return Response.json({ room });
} }
@@ -247,3 +268,21 @@ export async function PATCH(
return Response.json({ error: "Action inconnue" }, { status: 400 }); return Response.json({ error: "Action inconnue" }, { status: 400 });
} }
} }
async function saveRoom(room: Room) {
await prisma.room.update({
where: { code: room.code },
data: {
players: room.players as object[],
phase: room.phase,
round: room.round,
totalRounds: room.totalRounds,
maxPlayers: room.maxPlayers,
startArticle: room.startArticle,
targetArticle: room.targetArticle,
roundWinner: room.roundWinner,
countdownStart: room.countdownStart !== null ? BigInt(room.countdownStart) : null,
roundStart: room.roundStart !== null ? BigInt(room.roundStart) : null,
},
});
}
+73 -57
View File
@@ -2,6 +2,7 @@
// GET /api/rooms?code=XXXX - recuperer l'etat d'une room // GET /api/rooms?code=XXXX - recuperer l'etat d'une room
import { NextRequest } from "next/server"; import { NextRequest } from "next/server";
import { prisma } from "../../../lib/prisma";
// Types // Types
@@ -24,25 +25,12 @@ export type Room = {
maxPlayers: number; maxPlayers: number;
startArticle: string; startArticle: string;
targetArticle: string; targetArticle: string;
roundWinner: string | null; // player id roundWinner: string | null;
countdownStart: number | null; // timestamp ms countdownStart: number | null;
roundStart: number | null; // timestamp ms roundStart: number | null;
createdAt: number; createdAt: number;
}; };
// Stockage en memoire (singleton Node.js)
declare global {
var __wikirooms: Map<string, Room> | undefined;
}
function getRooms(): Map<string, Room> {
if (!global.__wikirooms) {
global.__wikirooms = new Map();
}
return global.__wikirooms;
}
// Helpers // Helpers
function generateCode(): string { function generateCode(): string {
@@ -58,17 +46,41 @@ function generatePlayerId(): string {
return crypto.randomUUID(); return crypto.randomUUID();
} }
// Nettoie les rooms inactives depuis plus de 2h function dbToRoom(row: {
function pruneOldRooms(rooms: Map<string, Room>) { code: string;
const now = Date.now(); players: unknown;
for (const [code, room] of rooms) { phase: string;
if (now - room.createdAt > 2 * 60 * 60 * 1000) { round: number;
rooms.delete(code); totalRounds: number;
} maxPlayers: number;
} startArticle: string;
targetArticle: string;
roundWinner: string | null;
countdownStart: bigint | null;
roundStart: bigint | null;
createdAt: bigint;
}): Room {
return {
code: row.code,
players: row.players as Player[],
phase: row.phase as Room["phase"],
round: row.round,
totalRounds: row.totalRounds,
maxPlayers: row.maxPlayers,
startArticle: row.startArticle,
targetArticle: row.targetArticle,
roundWinner: row.roundWinner,
countdownStart: row.countdownStart !== null ? Number(row.countdownStart) : null,
roundStart: row.roundStart !== null ? Number(row.roundStart) : null,
createdAt: Number(row.createdAt),
};
} }
// Handlers // Nettoie les rooms inactives depuis plus de 2h
async function pruneOldRooms() {
const cutoff = BigInt(Date.now() - 2 * 60 * 60 * 1000);
await prisma.room.deleteMany({ where: { createdAt: { lt: cutoff } } });
}
// POST /api/rooms // POST /api/rooms
// Body: { playerName: string } // Body: { playerName: string }
@@ -98,46 +110,51 @@ export async function POST(request: NextRequest) {
10, 10,
); );
const rooms = getRooms(); await pruneOldRooms();
pruneOldRooms(rooms);
// Generer un code unique // Generer un code unique
let code = generateCode(); let code = generateCode();
let attempts = 0; let attempts = 0;
while (rooms.has(code) && attempts < 20) { while (attempts < 20) {
const existing = await prisma.room.findUnique({ where: { code } });
if (!existing) break;
code = generateCode(); code = generateCode();
attempts++; attempts++;
} }
const playerId = generatePlayerId(); const playerId = generatePlayerId();
const now = BigInt(Date.now());
const room: Room = { const players: Player[] = [
code, {
players: [ id: playerId,
{ name: playerName.trim().slice(0, 20),
id: playerId, score: 0,
name: playerName.trim().slice(0, 20), currentArticle: "",
score: 0, hasWon: false,
currentArticle: "", isHost: true,
hasWon: false, lastSeen: Date.now(),
isHost: true, },
lastSeen: Date.now(), ];
},
],
phase: "waiting",
round: 0,
totalRounds: clampedRounds,
maxPlayers: clampedMax,
startArticle: "",
targetArticle: "",
roundWinner: null,
countdownStart: null,
roundStart: null,
createdAt: Date.now(),
};
rooms.set(code, room); const row = await prisma.room.create({
data: {
code,
players: players as object[],
phase: "waiting",
round: 0,
totalRounds: clampedRounds,
maxPlayers: clampedMax,
startArticle: "",
targetArticle: "",
roundWinner: null,
countdownStart: null,
roundStart: null,
createdAt: now,
},
});
const room = dbToRoom(row);
return Response.json({ room, playerId }); return Response.json({ room, playerId });
} }
@@ -150,12 +167,11 @@ export async function GET(request: NextRequest) {
return Response.json({ error: "Code manquant" }, { status: 400 }); return Response.json({ error: "Code manquant" }, { status: 400 });
} }
const rooms = getRooms(); const row = await prisma.room.findUnique({ where: { code: code.toUpperCase() } });
const room = rooms.get(code.toUpperCase());
if (!room) { if (!row) {
return Response.json({ error: "Room introuvable" }, { status: 404 }); return Response.json({ error: "Room introuvable" }, { status: 404 });
} }
return Response.json({ room }); return Response.json({ room: dbToRoom(row) });
} }
+5
View File
@@ -32,6 +32,11 @@ export type Game = Prisma.GameModel
* *
*/ */
export type DailyPuzzle = Prisma.DailyPuzzleModel export type DailyPuzzle = Prisma.DailyPuzzleModel
/**
* Model Room
*
*/
export type Room = Prisma.RoomModel
/** /**
* Model DailyResult * Model DailyResult
* *
+5
View File
@@ -56,6 +56,11 @@ export type Game = Prisma.GameModel
* *
*/ */
export type DailyPuzzle = Prisma.DailyPuzzleModel export type DailyPuzzle = Prisma.DailyPuzzleModel
/**
* Model Room
*
*/
export type Room = Prisma.RoomModel
/** /**
* Model DailyResult * Model DailyResult
* *
+274
View File
@@ -139,6 +139,149 @@ export type FloatWithAggregatesFilter<$PrismaModel = never> = {
_max?: Prisma.NestedFloatFilter<$PrismaModel> _max?: Prisma.NestedFloatFilter<$PrismaModel>
} }
export type JsonFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
Required<JsonFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<JsonFilterBase<$PrismaModel>>, 'path'>>
export type JsonFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
}
export type StringNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
}
export type BigIntNullableFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel> | null
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntNullableFilter<$PrismaModel> | bigint | number | null
}
export type BigIntFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number
}
export type SortOrderInput = {
sort: Prisma.SortOrder
nulls?: Prisma.NullsOrder
}
export type JsonWithAggregatesFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
Required<JsonWithAggregatesFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedJsonFilter<$PrismaModel>
_max?: Prisma.NestedJsonFilter<$PrismaModel>
}
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type BigIntNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel> | null
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntNullableWithAggregatesFilter<$PrismaModel> | bigint | number | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
_sum?: Prisma.NestedBigIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedBigIntNullableFilter<$PrismaModel>
_max?: Prisma.NestedBigIntNullableFilter<$PrismaModel>
}
export type BigIntWithAggregatesFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number
_count?: Prisma.NestedIntFilter<$PrismaModel>
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
_sum?: Prisma.NestedBigIntFilter<$PrismaModel>
_min?: Prisma.NestedBigIntFilter<$PrismaModel>
_max?: Prisma.NestedBigIntFilter<$PrismaModel>
}
export type NestedStringFilter<$PrismaModel = never> = { export type NestedStringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
@@ -262,4 +405,135 @@ export type NestedFloatWithAggregatesFilter<$PrismaModel = never> = {
_max?: Prisma.NestedFloatFilter<$PrismaModel> _max?: Prisma.NestedFloatFilter<$PrismaModel>
} }
export type NestedStringNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
}
export type NestedBigIntNullableFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel> | null
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntNullableFilter<$PrismaModel> | bigint | number | null
}
export type NestedBigIntFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number
}
export type NestedJsonFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
Required<NestedJsonFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>
export type NestedJsonFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
}
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type NestedIntNullableFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
}
export type NestedBigIntNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel> | null
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel> | null
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntNullableWithAggregatesFilter<$PrismaModel> | bigint | number | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
_sum?: Prisma.NestedBigIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedBigIntNullableFilter<$PrismaModel>
_max?: Prisma.NestedBigIntNullableFilter<$PrismaModel>
}
export type NestedFloatNullableFilter<$PrismaModel = never> = {
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null
}
export type NestedBigIntWithAggregatesFilter<$PrismaModel = never> = {
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number
_count?: Prisma.NestedIntFilter<$PrismaModel>
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
_sum?: Prisma.NestedBigIntFilter<$PrismaModel>
_min?: Prisma.NestedBigIntFilter<$PrismaModel>
_max?: Prisma.NestedBigIntFilter<$PrismaModel>
}
File diff suppressed because one or more lines are too long
@@ -387,6 +387,7 @@ export const ModelName = {
User: 'User', User: 'User',
Game: 'Game', Game: 'Game',
DailyPuzzle: 'DailyPuzzle', DailyPuzzle: 'DailyPuzzle',
Room: 'Room',
DailyResult: 'DailyResult' DailyResult: 'DailyResult'
} as const } as const
@@ -403,7 +404,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
omit: GlobalOmitOptions omit: GlobalOmitOptions
} }
meta: { meta: {
modelProps: "user" | "game" | "dailyPuzzle" | "dailyResult" modelProps: "user" | "game" | "dailyPuzzle" | "room" | "dailyResult"
txIsolationLevel: TransactionIsolationLevel txIsolationLevel: TransactionIsolationLevel
} }
model: { model: {
@@ -629,6 +630,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
} }
} }
} }
Room: {
payload: Prisma.$RoomPayload<ExtArgs>
fields: Prisma.RoomFieldRefs
operations: {
findUnique: {
args: Prisma.RoomFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload> | null
}
findUniqueOrThrow: {
args: Prisma.RoomFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>
}
findFirst: {
args: Prisma.RoomFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload> | null
}
findFirstOrThrow: {
args: Prisma.RoomFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>
}
findMany: {
args: Prisma.RoomFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>[]
}
create: {
args: Prisma.RoomCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>
}
createMany: {
args: Prisma.RoomCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.RoomCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>[]
}
delete: {
args: Prisma.RoomDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>
}
update: {
args: Prisma.RoomUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>
}
deleteMany: {
args: Prisma.RoomDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.RoomUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.RoomUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>[]
}
upsert: {
args: Prisma.RoomUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RoomPayload>
}
aggregate: {
args: Prisma.RoomAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateRoom>
}
groupBy: {
args: Prisma.RoomGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.RoomGroupByOutputType>[]
}
count: {
args: Prisma.RoomCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.RoomCountAggregateOutputType> | number
}
}
}
DailyResult: { DailyResult: {
payload: Prisma.$DailyResultPayload<ExtArgs> payload: Prisma.$DailyResultPayload<ExtArgs>
fields: Prisma.DailyResultFieldRefs fields: Prisma.DailyResultFieldRefs
@@ -780,6 +855,25 @@ export const DailyPuzzleScalarFieldEnum = {
export type DailyPuzzleScalarFieldEnum = (typeof DailyPuzzleScalarFieldEnum)[keyof typeof DailyPuzzleScalarFieldEnum] export type DailyPuzzleScalarFieldEnum = (typeof DailyPuzzleScalarFieldEnum)[keyof typeof DailyPuzzleScalarFieldEnum]
export const RoomScalarFieldEnum = {
code: 'code',
players: 'players',
phase: 'phase',
round: 'round',
totalRounds: 'totalRounds',
maxPlayers: 'maxPlayers',
startArticle: 'startArticle',
targetArticle: 'targetArticle',
roundWinner: 'roundWinner',
countdownStart: 'countdownStart',
roundStart: 'roundStart',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type RoomScalarFieldEnum = (typeof RoomScalarFieldEnum)[keyof typeof RoomScalarFieldEnum]
export const DailyResultScalarFieldEnum = { export const DailyResultScalarFieldEnum = {
id: 'id', id: 'id',
puzzleId: 'puzzleId', puzzleId: 'puzzleId',
@@ -802,6 +896,13 @@ export const SortOrder = {
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder] export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const JsonNullValueInput = {
JsonNull: JsonNull
} as const
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
export const QueryMode = { export const QueryMode = {
default: 'default', default: 'default',
insensitive: 'insensitive' insensitive: 'insensitive'
@@ -810,6 +911,23 @@ export const QueryMode = {
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode] export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
export const JsonNullValueFilter = {
DbNull: DbNull,
JsonNull: JsonNull,
AnyNull: AnyNull
} as const
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
export const NullsOrder = {
first: 'first',
last: 'last'
} as const
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
/** /**
* Field references * Field references
@@ -878,6 +996,34 @@ export type FloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, '
export type ListFloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float[]'> export type ListFloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float[]'>
/**
* Reference to a field of type 'Json'
*/
export type JsonFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Json'>
/**
* Reference to a field of type 'QueryMode'
*/
export type EnumQueryModeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'QueryMode'>
/**
* Reference to a field of type 'BigInt'
*/
export type BigIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BigInt'>
/**
* Reference to a field of type 'BigInt[]'
*/
export type ListBigIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BigInt[]'>
/** /**
* Batch Payload for updateMany & deleteMany & createMany * Batch Payload for updateMany & deleteMany & createMany
*/ */
@@ -976,6 +1122,7 @@ export type GlobalOmitConfig = {
user?: Prisma.UserOmit user?: Prisma.UserOmit
game?: Prisma.GameOmit game?: Prisma.GameOmit
dailyPuzzle?: Prisma.DailyPuzzleOmit dailyPuzzle?: Prisma.DailyPuzzleOmit
room?: Prisma.RoomOmit
dailyResult?: Prisma.DailyResultOmit dailyResult?: Prisma.DailyResultOmit
} }
@@ -54,6 +54,7 @@ export const ModelName = {
User: 'User', User: 'User',
Game: 'Game', Game: 'Game',
DailyPuzzle: 'DailyPuzzle', DailyPuzzle: 'DailyPuzzle',
Room: 'Room',
DailyResult: 'DailyResult' DailyResult: 'DailyResult'
} as const } as const
@@ -111,6 +112,25 @@ export const DailyPuzzleScalarFieldEnum = {
export type DailyPuzzleScalarFieldEnum = (typeof DailyPuzzleScalarFieldEnum)[keyof typeof DailyPuzzleScalarFieldEnum] export type DailyPuzzleScalarFieldEnum = (typeof DailyPuzzleScalarFieldEnum)[keyof typeof DailyPuzzleScalarFieldEnum]
export const RoomScalarFieldEnum = {
code: 'code',
players: 'players',
phase: 'phase',
round: 'round',
totalRounds: 'totalRounds',
maxPlayers: 'maxPlayers',
startArticle: 'startArticle',
targetArticle: 'targetArticle',
roundWinner: 'roundWinner',
countdownStart: 'countdownStart',
roundStart: 'roundStart',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type RoomScalarFieldEnum = (typeof RoomScalarFieldEnum)[keyof typeof RoomScalarFieldEnum]
export const DailyResultScalarFieldEnum = { export const DailyResultScalarFieldEnum = {
id: 'id', id: 'id',
puzzleId: 'puzzleId', puzzleId: 'puzzleId',
@@ -133,6 +153,13 @@ export const SortOrder = {
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder] export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const JsonNullValueInput = {
JsonNull: JsonNull
} as const
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
export const QueryMode = { export const QueryMode = {
default: 'default', default: 'default',
insensitive: 'insensitive' insensitive: 'insensitive'
@@ -140,3 +167,20 @@ export const QueryMode = {
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode] export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
export const JsonNullValueFilter = {
DbNull: DbNull,
JsonNull: JsonNull,
AnyNull: AnyNull
} as const
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
export const NullsOrder = {
first: 'first',
last: 'last'
} as const
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
+1
View File
@@ -11,5 +11,6 @@
export type * from './models/User' export type * from './models/User'
export type * from './models/Game' export type * from './models/Game'
export type * from './models/DailyPuzzle' export type * from './models/DailyPuzzle'
export type * from './models/Room'
export type * from './models/DailyResult' export type * from './models/DailyResult'
export type * from './commonInputTypes' export type * from './commonInputTypes'
File diff suppressed because it is too large Load Diff
+16
View File
@@ -42,6 +42,22 @@ model DailyPuzzle {
results DailyResult[] results DailyResult[]
} }
model Room {
code String @id
players Json @default("[]")
phase String @default("waiting")
round Int @default(0)
totalRounds Int @default(3)
maxPlayers Int @default(16)
startArticle String @default("")
targetArticle String @default("")
roundWinner String?
countdownStart BigInt?
roundStart BigInt?
createdAt BigInt
updatedAt DateTime @updatedAt
}
model DailyResult { model DailyResult {
id String @id @default(uuid(7)) id String @id @default(uuid(7))
puzzleId String puzzleId String