303 lines
11 KiB
JavaScript
303 lines
11 KiB
JavaScript
import { drizzle } from "drizzle-orm/postgres-js";
|
|
import postgres from "postgres";
|
|
import { pgTable, timestamp, text, boolean, index, uuid, real, integer, jsonb, uniqueIndex } from "drizzle-orm/pg-core";
|
|
import { relations } from "drizzle-orm";
|
|
import { b as private_env } from "./shared-server.js";
|
|
const user = pgTable("user", {
|
|
id: text("id").primaryKey(),
|
|
name: text("name").notNull(),
|
|
email: text("email").notNull().unique(),
|
|
emailVerified: boolean("email_verified").default(false).notNull(),
|
|
image: text("image"),
|
|
// ── Staff role: 'player' | 'referee' | 'admin' ──
|
|
role: text("role").default("player").notNull(),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => /* @__PURE__ */ new Date()).notNull()
|
|
});
|
|
const session = pgTable(
|
|
"session",
|
|
{
|
|
id: text("id").primaryKey(),
|
|
expiresAt: timestamp("expires_at").notNull(),
|
|
token: text("token").notNull().unique(),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
updatedAt: timestamp("updated_at").$onUpdate(() => /* @__PURE__ */ new Date()).notNull(),
|
|
ipAddress: text("ip_address"),
|
|
userAgent: text("user_agent"),
|
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" })
|
|
},
|
|
(table) => [index("session_userId_idx").on(table.userId)]
|
|
);
|
|
const account = pgTable(
|
|
"account",
|
|
{
|
|
id: text("id").primaryKey(),
|
|
accountId: text("account_id").notNull(),
|
|
providerId: text("provider_id").notNull(),
|
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
accessToken: text("access_token"),
|
|
refreshToken: text("refresh_token"),
|
|
idToken: text("id_token"),
|
|
accessTokenExpiresAt: timestamp("access_token_expires_at"),
|
|
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
|
|
scope: text("scope"),
|
|
password: text("password"),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
updatedAt: timestamp("updated_at").$onUpdate(() => /* @__PURE__ */ new Date()).notNull()
|
|
},
|
|
(table) => [index("account_userId_idx").on(table.userId)]
|
|
);
|
|
const verification = pgTable(
|
|
"verification",
|
|
{
|
|
id: text("id").primaryKey(),
|
|
identifier: text("identifier").notNull(),
|
|
value: text("value").notNull(),
|
|
expiresAt: timestamp("expires_at").notNull(),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => /* @__PURE__ */ new Date()).notNull()
|
|
},
|
|
(table) => [index("verification_identifier_idx").on(table.identifier)]
|
|
);
|
|
const userRelations = relations(user, ({ many }) => ({
|
|
sessions: many(session),
|
|
accounts: many(account)
|
|
}));
|
|
const sessionRelations = relations(session, ({ one }) => ({
|
|
user: one(user, {
|
|
fields: [session.userId],
|
|
references: [user.id]
|
|
})
|
|
}));
|
|
const accountRelations = relations(account, ({ one }) => ({
|
|
user: one(user, {
|
|
fields: [account.userId],
|
|
references: [user.id]
|
|
})
|
|
}));
|
|
const team = pgTable("team", {
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
name: text("name").notNull(),
|
|
isPersonal: boolean("is_personal").default(false).notNull(),
|
|
ownerId: text("owner_id"),
|
|
avatarUrl: text("avatar_url"),
|
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
});
|
|
const teamMember = pgTable("team_member", {
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
teamId: uuid("team_id").notNull().references(() => team.id, { onDelete: "cascade" }),
|
|
userId: text("user_id").notNull(),
|
|
role: text("role").default("member"),
|
|
joinedAt: timestamp("joined_at", { withTimezone: true }).defaultNow()
|
|
});
|
|
const mappool = pgTable("mappool", {
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
name: text("name").notNull(),
|
|
createdBy: text("created_by"),
|
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
});
|
|
const mappoolSlot = pgTable("mappool_slot", {
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
mappoolId: uuid("mappool_id").notNull().references(() => mappool.id, { onDelete: "cascade" }),
|
|
category: text("category").notNull(),
|
|
orderInCategory: integer("order_in_category").notNull(),
|
|
beatmapId: text("beatmap_id").notNull(),
|
|
starRating: real("star_rating"),
|
|
mods: text("mods").array().default([]).notNull()
|
|
});
|
|
const match = pgTable(
|
|
"match",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
name: text("name"),
|
|
state: text("state").default("CREATED").notNull(),
|
|
config: jsonb("config").notNull(),
|
|
mappoolId: uuid("mappool_id").references(() => mappool.id),
|
|
osuLobbyId: integer("osu_lobby_id"),
|
|
scheduledAt: timestamp("scheduled_at", { withTimezone: true }),
|
|
startedAt: timestamp("started_at", { withTimezone: true }),
|
|
finishedAt: timestamp("finished_at", { withTimezone: true }),
|
|
winnerId: uuid("winner_id").references(() => team.id),
|
|
createdBy: text("created_by"),
|
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
},
|
|
(t) => [index("idx_match_state").on(t.state)]
|
|
);
|
|
const matchParticipant = pgTable(
|
|
"match_participant",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
matchId: uuid("match_id").notNull().references(() => match.id, { onDelete: "cascade" }),
|
|
teamId: uuid("team_id").notNull().references(() => team.id),
|
|
slot: integer("slot").notNull(),
|
|
score: integer("score").default(0).notNull(),
|
|
rollValue: integer("roll_value"),
|
|
pickOrder: integer("pick_order")
|
|
},
|
|
(t) => [
|
|
index("idx_mp_match_team").on(t.matchId, t.teamId),
|
|
uniqueIndex("idx_mp_match_slot").on(t.matchId, t.slot),
|
|
index("idx_mp_match").on(t.matchId)
|
|
]
|
|
);
|
|
const matchParticipantPlayer = pgTable(
|
|
"match_participant_player",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
participantId: uuid("participant_id").notNull().references(() => matchParticipant.id, { onDelete: "cascade" }),
|
|
userId: text("user_id").notNull()
|
|
},
|
|
(t) => [
|
|
uniqueIndex("idx_mpp_participant_user").on(t.participantId, t.userId),
|
|
index("idx_mpp_participant").on(t.participantId)
|
|
]
|
|
);
|
|
const matchGame = pgTable(
|
|
"match_game",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
matchId: uuid("match_id").notNull().references(() => match.id, { onDelete: "cascade" }),
|
|
gameNumber: integer("game_number").notNull(),
|
|
mappoolSlotId: uuid("mappool_slot_id").notNull().references(() => mappoolSlot.id),
|
|
pickedByParticipantId: uuid("picked_by_participant_id").references(() => matchParticipant.id),
|
|
winnerParticipantId: uuid("winner_participant_id").references(() => matchParticipant.id),
|
|
state: text("state").default("PENDING").notNull(),
|
|
startedAt: timestamp("started_at", { withTimezone: true }),
|
|
finishedAt: timestamp("finished_at", { withTimezone: true })
|
|
},
|
|
(t) => [index("idx_match_game_match").on(t.matchId)]
|
|
);
|
|
const matchGameScore = pgTable(
|
|
"match_game_score",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
matchGameId: uuid("match_game_id").notNull().references(() => matchGame.id, { onDelete: "cascade" }),
|
|
playerId: uuid("player_id").notNull().references(() => matchParticipantPlayer.id),
|
|
score: integer("score").default(0).notNull(),
|
|
accuracy: real("accuracy").default(0).notNull(),
|
|
maxCombo: integer("max_combo").default(0).notNull(),
|
|
count300: integer("count_300").default(0).notNull(),
|
|
count100: integer("count_100").default(0).notNull(),
|
|
count50: integer("count_50").default(0).notNull(),
|
|
countMiss: integer("count_miss").default(0).notNull(),
|
|
mods: text("mods").array().default([]).notNull(),
|
|
passed: boolean("passed").default(false).notNull()
|
|
},
|
|
(t) => [index("idx_mgs_game").on(t.matchGameId)]
|
|
);
|
|
const matchQueue = pgTable(
|
|
"match_queue",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
userId: text("user_id").notNull(),
|
|
teamId: uuid("team_id").notNull().references(() => team.id),
|
|
elo: integer("elo").default(1e3).notNull(),
|
|
joinedAt: timestamp("joined_at", { withTimezone: true }).defaultNow().notNull()
|
|
},
|
|
(t) => [
|
|
uniqueIndex("idx_queue_user").on(t.userId),
|
|
index("idx_queue_elo").on(t.elo)
|
|
]
|
|
);
|
|
const playerRating = pgTable("player_rating", {
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
userId: text("user_id").notNull().unique(),
|
|
elo: integer("elo").default(1e3).notNull(),
|
|
wins: integer("wins").default(0).notNull(),
|
|
losses: integer("losses").default(0).notNull(),
|
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
});
|
|
const teamRelations = relations(team, ({ many }) => ({
|
|
members: many(teamMember)
|
|
}));
|
|
const teamMemberRelations = relations(teamMember, ({ one }) => ({
|
|
team: one(team, { fields: [teamMember.teamId], references: [team.id] })
|
|
}));
|
|
const mappoolRelations = relations(mappool, ({ many }) => ({
|
|
slots: many(mappoolSlot)
|
|
}));
|
|
const mappoolSlotRelations = relations(mappoolSlot, ({ one }) => ({
|
|
mappool: one(mappool, { fields: [mappoolSlot.mappoolId], references: [mappool.id] })
|
|
}));
|
|
const matchRelations = relations(match, ({ one, many }) => ({
|
|
mappool: one(mappool, { fields: [match.mappoolId], references: [mappool.id] }),
|
|
participants: many(matchParticipant),
|
|
games: many(matchGame)
|
|
}));
|
|
const matchParticipantRelations = relations(matchParticipant, ({ one, many }) => ({
|
|
match: one(match, { fields: [matchParticipant.matchId], references: [match.id] }),
|
|
team: one(team, { fields: [matchParticipant.teamId], references: [team.id] }),
|
|
players: many(matchParticipantPlayer)
|
|
}));
|
|
const matchParticipantPlayerRelations = relations(matchParticipantPlayer, ({ one }) => ({
|
|
participant: one(matchParticipant, {
|
|
fields: [matchParticipantPlayer.participantId],
|
|
references: [matchParticipant.id]
|
|
})
|
|
}));
|
|
const matchGameRelations = relations(matchGame, ({ one, many }) => ({
|
|
match: one(match, { fields: [matchGame.matchId], references: [match.id] }),
|
|
slot: one(mappoolSlot, { fields: [matchGame.mappoolSlotId], references: [mappoolSlot.id] }),
|
|
scores: many(matchGameScore)
|
|
}));
|
|
const matchGameScoreRelations = relations(matchGameScore, ({ one }) => ({
|
|
game: one(matchGame, { fields: [matchGameScore.matchGameId], references: [matchGame.id] }),
|
|
player: one(matchParticipantPlayer, {
|
|
fields: [matchGameScore.playerId],
|
|
references: [matchParticipantPlayer.id]
|
|
})
|
|
}));
|
|
const matchQueueRelations = relations(matchQueue, ({ one }) => ({
|
|
team: one(team, { fields: [matchQueue.teamId], references: [team.id] })
|
|
}));
|
|
const schema = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
__proto__: null,
|
|
account,
|
|
accountRelations,
|
|
mappool,
|
|
mappoolRelations,
|
|
mappoolSlot,
|
|
mappoolSlotRelations,
|
|
match,
|
|
matchGame,
|
|
matchGameRelations,
|
|
matchGameScore,
|
|
matchGameScoreRelations,
|
|
matchParticipant,
|
|
matchParticipantPlayer,
|
|
matchParticipantPlayerRelations,
|
|
matchParticipantRelations,
|
|
matchQueue,
|
|
matchQueueRelations,
|
|
matchRelations,
|
|
playerRating,
|
|
session,
|
|
sessionRelations,
|
|
team,
|
|
teamMember,
|
|
teamMemberRelations,
|
|
teamRelations,
|
|
user,
|
|
userRelations,
|
|
verification
|
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
if (!private_env.DATABASE_URL) throw new Error("DATABASE_URL is not set");
|
|
const client = postgres(private_env.DATABASE_URL);
|
|
const db = drizzle(client, { schema });
|
|
export {
|
|
mappoolSlot as a,
|
|
match as b,
|
|
team as c,
|
|
db as d,
|
|
matchParticipant as e,
|
|
matchParticipantPlayer as f,
|
|
matchGame as g,
|
|
matchGameScore as h,
|
|
matchQueue as i,
|
|
account as j,
|
|
mappool as m,
|
|
playerRating as p,
|
|
teamMember as t,
|
|
user as u
|
|
};
|