feat(db): add lobbies and lobby_players tables + model

This commit is contained in:
lila 2026-04-16 14:45:45 +02:00
parent a7be7152cc
commit 47a68c0315
8 changed files with 1310 additions and 10 deletions

View file

@ -9,6 +9,7 @@ import {
primaryKey,
index,
boolean,
integer,
} from "drizzle-orm/pg-core";
import { sql, relations } from "drizzle-orm";
@ -19,6 +20,7 @@ import {
CEFR_LEVELS,
SUPPORTED_DECK_TYPES,
DIFFICULTY_LEVELS,
LOBBY_STATUSES,
} from "@lila/shared";
export const terms = pgTable(
@ -252,12 +254,53 @@ export const accountRelations = relations(account, ({ one }) => ({
user: one(user, { fields: [account.userId], references: [user.id] }),
}));
/*
* INTENTIONAL DESIGN DECISIONS see decisions.md for full reasoning
*
* source + source_id (terms): idempotency key per import pipeline
* display_name UNIQUE (users): multiplayer requires distinguishable names
* UNIQUE(term_id, language_code, text): allows synonyms, prevents exact duplicates
* updated_at omitted: misleading without a trigger to maintain it
* FK indexes: all FK columns covered, no sequential scans on joins
*/
export const lobbies = pgTable(
"lobbies",
{
id: uuid().primaryKey().defaultRandom(),
code: varchar({ length: 10 }).notNull().unique(),
hostUserId: text("host_user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
status: varchar({ length: 20 }).notNull().default("waiting"),
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
},
(table) => [
check(
"lobby_status_check",
sql`${table.status} IN (${sql.raw(LOBBY_STATUSES.map((s) => `'${s}'`).join(", "))})`,
),
],
);
export const lobby_players = pgTable(
"lobby_players",
{
lobbyId: uuid("lobby_id")
.notNull()
.references(() => lobbies.id, { onDelete: "cascade" }),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
score: integer().notNull().default(0),
joinedAt: timestamp("joined_at", { withTimezone: true })
.defaultNow()
.notNull(),
},
(table) => [primaryKey({ columns: [table.lobbyId, table.userId] })],
);
export const lobbyRelations = relations(lobbies, ({ one, many }) => ({
host: one(user, { fields: [lobbies.hostUserId], references: [user.id] }),
players: many(lobby_players),
}));
export const lobbyPlayersRelations = relations(lobby_players, ({ one }) => ({
lobby: one(lobbies, {
fields: [lobby_players.lobbyId],
references: [lobbies.id],
}),
user: one(user, { fields: [lobby_players.userId], references: [user.id] }),
}));