178 lines
5.5 KiB
TypeScript
178 lines
5.5 KiB
TypeScript
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
|
import { createServerFn } from "@tanstack/react-start";
|
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
|
import { logger } from "@/lib/logger";
|
|
import { z } from "zod";
|
|
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
|
import brackets from "@/features/bracket/utils";
|
|
import { MatchInput } from "@/features/matches/types";
|
|
|
|
const orderedTeamsSchema = z.object({
|
|
tournamentId: z.string(),
|
|
orderedTeamIds: z.array(z.string()),
|
|
});
|
|
|
|
export const generateTournamentBracket = createServerFn()
|
|
.validator(orderedTeamsSchema)
|
|
.middleware([superTokensAdminFunctionMiddleware])
|
|
.handler(async ({ data: { tournamentId, orderedTeamIds } }) =>
|
|
toServerResult(async () => {
|
|
logger.info('Generating tournament bracket', { tournamentId, teamCount: orderedTeamIds.length });
|
|
|
|
const tournament = await pbAdmin.getTournament(tournamentId);
|
|
if (!tournament) {
|
|
throw new Error('Tournament not found');
|
|
}
|
|
|
|
if (tournament.matches && tournament.matches.length > 0) {
|
|
throw new Error('Tournament already has matches generated');
|
|
}
|
|
|
|
const teamCount = orderedTeamIds.length;
|
|
if (!Object.keys(brackets).includes(teamCount.toString())) {
|
|
throw new Error(`Bracket not available for ${teamCount} teams`);
|
|
}
|
|
|
|
const bracketTemplate = brackets[teamCount as keyof typeof brackets] as any;
|
|
|
|
const seedToTeamId = new Map<number, string>();
|
|
orderedTeamIds.forEach((teamId, index) => {
|
|
seedToTeamId.set(index + 1, teamId);
|
|
});
|
|
|
|
const matchInputs: MatchInput[] = [];
|
|
|
|
bracketTemplate.winners.forEach((round: any[]) => {
|
|
round.forEach((match: any) => {
|
|
const matchInput: MatchInput = {
|
|
lid: match.lid,
|
|
round: match.round,
|
|
order: match.order || 0,
|
|
reset: match.reset || false,
|
|
bye: match.bye || false,
|
|
home_cups: 0,
|
|
away_cups: 0,
|
|
ot_count: 0,
|
|
home_from_lid: match.home_from_lid,
|
|
away_from_lid: match.away_from_lid,
|
|
home_from_loser: match.home_from_loser || false,
|
|
away_from_loser: match.away_from_loser || false,
|
|
is_losers_bracket: false,
|
|
status: "tbd",
|
|
tournament: tournamentId,
|
|
};
|
|
|
|
if (match.home_seed) {
|
|
const teamId = seedToTeamId.get(match.home_seed);
|
|
if (teamId) {
|
|
matchInput.home = teamId;
|
|
matchInput.home_seed = match.home_seed;
|
|
}
|
|
}
|
|
|
|
if (match.away_seed) {
|
|
const teamId = seedToTeamId.get(match.away_seed);
|
|
if (teamId) {
|
|
matchInput.away = teamId;
|
|
matchInput.away_seed = match.away_seed;
|
|
}
|
|
}
|
|
|
|
if (matchInput.home && matchInput.away) {
|
|
matchInput.status = "ready"
|
|
}
|
|
|
|
matchInputs.push(matchInput);
|
|
});
|
|
});
|
|
|
|
bracketTemplate.losers.forEach((round: any[]) => {
|
|
round.forEach((match: any) => {
|
|
const matchInput: MatchInput = {
|
|
lid: match.lid,
|
|
round: match.round,
|
|
order: match.order || 0,
|
|
reset: match.reset || false,
|
|
bye: match.bye || false,
|
|
home_cups: 0,
|
|
away_cups: 0,
|
|
ot_count: 0,
|
|
home_from_lid: match.home_from_lid,
|
|
away_from_lid: match.away_from_lid,
|
|
home_from_loser: match.home_from_loser || false,
|
|
away_from_loser: match.away_from_loser || false,
|
|
is_losers_bracket: true,
|
|
status: "tbd",
|
|
tournament: tournamentId,
|
|
};
|
|
|
|
matchInputs.push(matchInput);
|
|
});
|
|
});
|
|
|
|
const createdMatches = await pbAdmin.createMatches(matchInputs);
|
|
|
|
const matchIds = createdMatches.map(match => match.id);
|
|
await pbAdmin.updateTournamentMatches(tournamentId, matchIds);
|
|
|
|
logger.info('Tournament bracket generated', {
|
|
tournamentId,
|
|
matchCount: createdMatches.length
|
|
});
|
|
|
|
return {
|
|
tournament,
|
|
matchCount: createdMatches.length,
|
|
matches: createdMatches,
|
|
};
|
|
})
|
|
);
|
|
|
|
export const startMatch = createServerFn()
|
|
.validator(z.string())
|
|
.middleware([superTokensAdminFunctionMiddleware])
|
|
.handler(async ({ data }) =>
|
|
toServerResult(async () => {
|
|
logger.info('Starting match', data);
|
|
|
|
let match = await pbAdmin.getMatch(data);
|
|
if (!match) {
|
|
throw new Error('Match not found');
|
|
}
|
|
|
|
match = await pbAdmin.updateMatch(data, {
|
|
start_time: new Date().toISOString(),
|
|
status: "started"
|
|
});
|
|
|
|
return match;
|
|
}
|
|
));
|
|
|
|
const endMatchSchema = z.object({
|
|
matchId: z.string(),
|
|
home_cups: z.number(),
|
|
away_cups: z.number(),
|
|
ot_count: z.number()
|
|
});
|
|
export const endMatch = createServerFn()
|
|
.validator(endMatchSchema)
|
|
.middleware([superTokensAdminFunctionMiddleware])
|
|
.handler(async ({ data: { matchId, home_cups, away_cups, ot_count } }) =>
|
|
toServerResult(async () => {
|
|
logger.info('Ending match', matchId);
|
|
|
|
let match = await pbAdmin.getMatch(matchId);
|
|
if (!match) {
|
|
throw new Error('Match not found');
|
|
}
|
|
|
|
match = await pbAdmin.updateMatch(matchId, {
|
|
end_time: new Date().toISOString(),
|
|
status: "ended",
|
|
home_cups,
|
|
away_cups,
|
|
ot_count
|
|
})
|
|
}
|
|
)); |