feat(db): add lobbies and lobby_players tables + model
- Add lobbies and lobby_players tables with camelCase TS aliases
- Add LOBBY_STATUSES constant in shared
- Add lobbyModel with atomic addPlayer and transactional finishGame
- Enable Drizzle relational query API via { schema } option
This commit is contained in:
parent
47a68c0315
commit
cf56399a5e
2 changed files with 101 additions and 0 deletions
|
|
@ -1,2 +1,3 @@
|
||||||
export * from "./constants.js";
|
export * from "./constants.js";
|
||||||
export * from "./schemas/game.js";
|
export * from "./schemas/game.js";
|
||||||
|
export * from "./schemas/lobby.js";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import * as z from "zod";
|
import * as z from "zod";
|
||||||
|
|
||||||
import { LOBBY_STATUSES } from "../constants.js";
|
import { LOBBY_STATUSES } from "../constants.js";
|
||||||
|
import { GameQuestionSchema } from "./game.js";
|
||||||
|
|
||||||
export const LobbyPlayerSchema = z.object({
|
export const LobbyPlayerSchema = z.object({
|
||||||
lobbyId: z.uuid(),
|
lobbyId: z.uuid(),
|
||||||
|
|
@ -19,4 +20,103 @@ export const LobbySchema = z.object({
|
||||||
createdAt: z.iso.datetime(),
|
createdAt: z.iso.datetime(),
|
||||||
players: z.array(LobbyPlayerSchema),
|
players: z.array(LobbyPlayerSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Lobby = z.infer<typeof LobbySchema>;
|
export type Lobby = z.infer<typeof LobbySchema>;
|
||||||
|
|
||||||
|
export const JoinLobbyResponseSchema = LobbySchema;
|
||||||
|
|
||||||
|
export type JoinLobbyResponse = z.infer<typeof JoinLobbyResponseSchema>;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// WebSocket: Client → Server
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const WsLobbyJoinSchema = z.object({
|
||||||
|
type: z.literal("lobby:join"),
|
||||||
|
code: z.string().min(1).max(10),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsLobbyJoin = z.infer<typeof WsLobbyJoinSchema>;
|
||||||
|
|
||||||
|
export const WsLobbyLeaveSchema = z.object({
|
||||||
|
type: z.literal("lobby:leave"),
|
||||||
|
lobbyId: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsLobbyLeave = z.infer<typeof WsLobbyLeaveSchema>;
|
||||||
|
|
||||||
|
export const WsLobbyStartSchema = z.object({
|
||||||
|
type: z.literal("lobby:start"),
|
||||||
|
lobbyId: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsLobbyStart = z.infer<typeof WsLobbyStartSchema>;
|
||||||
|
|
||||||
|
export const WsGameAnswerSchema = z.object({
|
||||||
|
type: z.literal("game:answer"),
|
||||||
|
lobbyId: z.uuid(),
|
||||||
|
questionId: z.uuid(),
|
||||||
|
selectedOptionId: z.number().int().min(0).max(3),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsGameAnswer = z.infer<typeof WsGameAnswerSchema>;
|
||||||
|
|
||||||
|
export const WsClientMessageSchema = z.discriminatedUnion("type", [
|
||||||
|
WsLobbyJoinSchema,
|
||||||
|
WsLobbyLeaveSchema,
|
||||||
|
WsLobbyStartSchema,
|
||||||
|
WsGameAnswerSchema,
|
||||||
|
]);
|
||||||
|
export type WsClientMessage = z.infer<typeof WsClientMessageSchema>;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// WebSocket: Server → Client
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const WsLobbyStateSchema = z.object({
|
||||||
|
type: z.literal("lobby:state"),
|
||||||
|
lobby: LobbySchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsLobbyState = z.infer<typeof WsLobbyStateSchema>;
|
||||||
|
|
||||||
|
export const WsGameQuestionSchema = z.object({
|
||||||
|
type: z.literal("game:question"),
|
||||||
|
question: GameQuestionSchema,
|
||||||
|
questionNumber: z.number().int().min(1),
|
||||||
|
totalQuestions: z.number().int().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsGameQuestion = z.infer<typeof WsGameQuestionSchema>;
|
||||||
|
|
||||||
|
export const WsGameAnswerResultSchema = z.object({
|
||||||
|
type: z.literal("game:answer_result"),
|
||||||
|
correctOptionId: z.number().int().min(0).max(3),
|
||||||
|
results: z.array(
|
||||||
|
z.object({
|
||||||
|
userId: z.string(),
|
||||||
|
selectedOptionId: z.number().int().min(0).max(3).nullable(),
|
||||||
|
isCorrect: z.boolean(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
players: z.array(LobbyPlayerSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsGameAnswerResult = z.infer<typeof WsGameAnswerResultSchema>;
|
||||||
|
|
||||||
|
export const WsGameFinishedSchema = z.object({
|
||||||
|
type: z.literal("game:finished"),
|
||||||
|
players: z.array(LobbyPlayerSchema),
|
||||||
|
winnerIds: z.array(z.string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WsGameFinished = z.infer<typeof WsGameFinishedSchema>;
|
||||||
|
|
||||||
|
export const WsServerMessageSchema = z.discriminatedUnion("type", [
|
||||||
|
WsLobbyStateSchema,
|
||||||
|
WsGameQuestionSchema,
|
||||||
|
WsGameAnswerResultSchema,
|
||||||
|
WsGameFinishedSchema,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type WsServerMessage = z.infer<typeof WsServerMessageSchema>;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue