update
This commit is contained in:
parent
de67f784a3
commit
2675c6363c
72
package-lock.json
generated
72
package-lock.json
generated
@ -15,7 +15,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@preline/dropdown": "^3.0.1",
|
||||
"@preline/tooltip": "^3.0.0",
|
||||
"@prisma/client": "^6.9.0",
|
||||
"@prisma/client": "^6.10.1",
|
||||
"csgo-sharecode": "^3.1.2",
|
||||
"datatables.net": "^2.2.2",
|
||||
"date-fns": "^4.1.0",
|
||||
@ -51,7 +51,7 @@
|
||||
"@types/ws": "^8.18.1",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.0",
|
||||
"prisma": "^6.9.0",
|
||||
"prisma": "^6.10.1",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.19.4",
|
||||
@ -1550,9 +1550,9 @@
|
||||
"license": "Licensed under MIT and Preline UI Fair Use License"
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.9.0.tgz",
|
||||
"integrity": "sha512-Gg7j1hwy3SgF1KHrh0PZsYvAaykeR0PaxusnLXydehS96voYCGt1U5zVR31NIouYc63hWzidcrir1a7AIyCsNQ==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.10.1.tgz",
|
||||
"integrity": "sha512-Re4pMlcUsQsUTAYMK7EJ4Bw2kg3WfZAAlr8GjORJaK4VOP6LxRQUQ1TuLnxcF42XqGkWQ36q5CQF1yVadANQ6w==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -1572,9 +1572,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/config": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.9.0.tgz",
|
||||
"integrity": "sha512-Wcfk8/lN3WRJd5w4jmNQkUwhUw0eksaU/+BlAJwPQKW10k0h0LC9PD/6TQFmqKVbHQL0vG2z266r0S1MPzzhbA==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.10.1.tgz",
|
||||
"integrity": "sha512-kz4/bnqrOrzWo8KzYguN0cden4CzLJJ+2VSpKtF8utHS3l1JS0Lhv6BLwpOX6X9yNreTbZQZwewb+/BMPDCIYQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@ -1582,53 +1582,53 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.9.0.tgz",
|
||||
"integrity": "sha512-bFeur/qi/Q+Mqk4JdQ3R38upSYPebv5aOyD1RKywVD+rAMLtRkmTFn28ZuTtVOnZHEdtxnNOCH+bPIeSGz1+Fg==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.10.1.tgz",
|
||||
"integrity": "sha512-k2YT53cWxv9OLjW4zSYTZ6Z7j0gPfCzcr2Mj99qsuvlxr8WAKSZ2NcSR0zLf/mP4oxnYG842IMj3utTgcd7CaA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.9.0.tgz",
|
||||
"integrity": "sha512-im0X0bwDLA0244CDf8fuvnLuCQcBBdAGgr+ByvGfQY9wWl6EA+kRGwVk8ZIpG65rnlOwtaWIr/ZcEU5pNVvq9g==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.10.1.tgz",
|
||||
"integrity": "sha512-Q07P5rS2iPwk2IQr/rUQJ42tHjpPyFcbiH7PXZlV81Ryr9NYIgdxcUrwgVOWVm5T7ap02C0dNd1dpnNcSWig8A==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.9.0",
|
||||
"@prisma/engines-version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e",
|
||||
"@prisma/fetch-engine": "6.9.0",
|
||||
"@prisma/get-platform": "6.9.0"
|
||||
"@prisma/debug": "6.10.1",
|
||||
"@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c",
|
||||
"@prisma/fetch-engine": "6.10.1",
|
||||
"@prisma/get-platform": "6.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e.tgz",
|
||||
"integrity": "sha512-Qp9gMoBHgqhKlrvumZWujmuD7q4DV/gooEyPCLtbkc13EZdSz2RsGUJ5mHb3RJgAbk+dm6XenqG7obJEhXcJ6Q==",
|
||||
"version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c.tgz",
|
||||
"integrity": "sha512-ZJFTsEqapiTYVzXya6TUKYDFnSWCNegfUiG5ik9fleQva5Sk3DNyyUi7X1+0ZxWFHwHDr6BZV5Vm+iwP+LlciA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.9.0.tgz",
|
||||
"integrity": "sha512-PMKhJdl4fOdeE3J3NkcWZ+tf3W6rx3ht/rLU8w4SXFRcLhd5+3VcqY4Kslpdm8osca4ej3gTfB3+cSk5pGxgFg==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.10.1.tgz",
|
||||
"integrity": "sha512-clmbG/Jgmrc/n6Y77QcBmAUlq9LrwI9Dbgy4pq5jeEARBpRCWJDJ7PWW1P8p0LfFU0i5fsyO7FqRzRB8mkdS4g==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.9.0",
|
||||
"@prisma/engines-version": "6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e",
|
||||
"@prisma/get-platform": "6.9.0"
|
||||
"@prisma/debug": "6.10.1",
|
||||
"@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c",
|
||||
"@prisma/get-platform": "6.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/get-platform": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.9.0.tgz",
|
||||
"integrity": "sha512-/B4n+5V1LI/1JQcHp+sUpyRT1bBgZVPHbsC4lt4/19Xp4jvNIVcq5KYNtQDk5e/ukTSjo9PZVAxxy9ieFtlpTQ==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.10.1.tgz",
|
||||
"integrity": "sha512-4CY5ndKylcsce9Mv+VWp5obbR2/86SHOLVV053pwIkhVtT9C9A83yqiqI/5kJM9T1v1u1qco/bYjDKycmei9HA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.9.0"
|
||||
"@prisma/debug": "6.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
@ -6237,15 +6237,15 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.9.0.tgz",
|
||||
"integrity": "sha512-resJAwMyZREC/I40LF6FZ6rZTnlrlrYrb63oW37Gq+U+9xHwbyMSPJjKtM7VZf3gTO86t/Oyz+YeSXr3CmAY1Q==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.10.1.tgz",
|
||||
"integrity": "sha512-khhlC/G49E4+uyA3T3H5PRBut486HD2bDqE2+rvkU0pwk9IAqGFacLFUyIx9Uw+W2eCtf6XGwsp+/strUwMNPw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/config": "6.9.0",
|
||||
"@prisma/engines": "6.9.0"
|
||||
"@prisma/config": "6.10.1",
|
||||
"@prisma/engines": "6.10.1"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js"
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@preline/dropdown": "^3.0.1",
|
||||
"@preline/tooltip": "^3.0.0",
|
||||
"@prisma/client": "^6.9.0",
|
||||
"@prisma/client": "^6.10.1",
|
||||
"csgo-sharecode": "^3.1.2",
|
||||
"datatables.net": "^2.2.2",
|
||||
"date-fns": "^4.1.0",
|
||||
@ -54,7 +54,7 @@
|
||||
"@types/ws": "^8.18.1",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.0",
|
||||
"prisma": "^6.9.0",
|
||||
"prisma": "^6.10.1",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.19.4",
|
||||
|
||||
@ -25,6 +25,9 @@ model User {
|
||||
team Team? @relation("UserTeam", fields: [teamId], references: [id])
|
||||
ledTeam Team? @relation("TeamLeader")
|
||||
|
||||
matchesAsTeamA Match[] @relation("TeamAPlayers")
|
||||
matchesAsTeamB Match[] @relation("TeamBPlayers")
|
||||
|
||||
premierRank Int?
|
||||
authCode String?
|
||||
lastKnownShareCode String?
|
||||
@ -52,9 +55,10 @@ model Team {
|
||||
leader User? @relation("TeamLeader", fields: [leaderId], references: [steamId])
|
||||
members User[] @relation("UserTeam")
|
||||
invites TeamInvite[]
|
||||
matchesAsTeamA Match[] @relation("Match_TeamA")
|
||||
matchesAsTeamB Match[] @relation("Match_TeamB")
|
||||
matchPlayers MatchPlayer[]
|
||||
|
||||
matchesAsTeamA Match[] @relation("MatchTeamA")
|
||||
matchesAsTeamB Match[] @relation("MatchTeamB")
|
||||
}
|
||||
|
||||
model TeamInvite {
|
||||
@ -98,9 +102,13 @@ model Match {
|
||||
scoreB Int?
|
||||
|
||||
teamAId String?
|
||||
teamA Team? @relation("MatchTeamA", fields: [teamAId], references: [id])
|
||||
|
||||
teamBId String?
|
||||
teamA Team? @relation("Match_TeamA", fields: [teamAId], references: [id])
|
||||
teamB Team? @relation("Match_TeamB", fields: [teamBId], references: [id])
|
||||
teamB Team? @relation("MatchTeamB", fields: [teamBId], references: [id])
|
||||
|
||||
teamAUsers User[] @relation("TeamAPlayers")
|
||||
teamBUsers User[] @relation("TeamBPlayers")
|
||||
|
||||
filePath String?
|
||||
demoFile DemoFile?
|
||||
|
||||
@ -14,8 +14,6 @@ export async function GET(_: Request, context: { params: { id: string } }) {
|
||||
const match = await prisma.match.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
teamA: true,
|
||||
teamB: true,
|
||||
players: {
|
||||
include: {
|
||||
user: true,
|
||||
@ -23,6 +21,16 @@ export async function GET(_: Request, context: { params: { id: string } }) {
|
||||
team: true,
|
||||
},
|
||||
},
|
||||
teamAUsers: {
|
||||
include: {
|
||||
team: true,
|
||||
},
|
||||
},
|
||||
teamBUsers: {
|
||||
include: {
|
||||
team: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@ -30,49 +38,38 @@ export async function GET(_: Request, context: { params: { id: string } }) {
|
||||
return NextResponse.json({ error: 'Match nicht gefunden' }, { status: 404 })
|
||||
}
|
||||
|
||||
const teamAIds = new Set(match.teamAUsers.map(u => u.steamId));
|
||||
const teamBIds = new Set(match.teamBUsers.map(u => u.steamId));
|
||||
|
||||
const playersA = match.players
|
||||
.filter(p => p.teamId === match.teamAId)
|
||||
.filter(p => teamAIds.has(p.steamId))
|
||||
.map(p => ({
|
||||
user: p.user,
|
||||
stats: p.stats,
|
||||
team: p.team?.name ?? 'CT',
|
||||
}))
|
||||
team: p.team?.name ?? 'Team A',
|
||||
}));
|
||||
|
||||
const playersB = match.players
|
||||
.filter(p => p.teamId === match.teamBId)
|
||||
.filter(p => teamBIds.has(p.steamId))
|
||||
.map(p => ({
|
||||
user: p.user,
|
||||
stats: p.stats,
|
||||
team: p.team?.name ?? 'T',
|
||||
}))
|
||||
team: p.team?.name ?? 'Team B',
|
||||
}));
|
||||
|
||||
const teamA = match.teamA
|
||||
? {
|
||||
id: match.teamA.id,
|
||||
name: match.teamA.name,
|
||||
logo: match.teamA.logo,
|
||||
players: playersA,
|
||||
}
|
||||
: {
|
||||
id: null,
|
||||
name: 'CT',
|
||||
logo: null,
|
||||
players: playersA,
|
||||
}
|
||||
const teamA = {
|
||||
name: match.teamAUsers[0]?.team?.name ?? 'Team A',
|
||||
logo: null,
|
||||
score: match.scoreA,
|
||||
players: playersA,
|
||||
};
|
||||
|
||||
const teamB = match.teamB
|
||||
? {
|
||||
id: match.teamB.id,
|
||||
name: match.teamB.name,
|
||||
logo: match.teamB.logo,
|
||||
players: playersB,
|
||||
}
|
||||
: {
|
||||
id: null,
|
||||
name: 'T',
|
||||
logo: null,
|
||||
players: playersB,
|
||||
}
|
||||
const teamB = {
|
||||
name: match.teamBUsers[0]?.team?.name ?? 'Team B',
|
||||
logo: null,
|
||||
score: match.scoreB,
|
||||
players: playersB,
|
||||
};
|
||||
|
||||
return NextResponse.json({
|
||||
id: match.id,
|
||||
@ -81,11 +78,9 @@ export async function GET(_: Request, context: { params: { id: string } }) {
|
||||
demoDate: match.demoDate,
|
||||
matchType: match.matchType,
|
||||
map: match.map,
|
||||
scoreA: match.scoreA,
|
||||
scoreB: match.scoreB,
|
||||
teamA,
|
||||
teamB,
|
||||
})
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`GET /matches/${id} failed:`, err)
|
||||
return NextResponse.json({ error: 'Failed to load match' }, { status: 500 })
|
||||
@ -105,20 +100,21 @@ export async function PUT(req: NextRequest, context: { params: { id: string } })
|
||||
const body = await req.json()
|
||||
const { title, description, matchDate, players } = body
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { steamId: userId },
|
||||
include: { ledTeam: true },
|
||||
});
|
||||
|
||||
const match = await prisma.match.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
teamA: { include: { leader: true } },
|
||||
teamB: { include: { leader: true } },
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!match) {
|
||||
return NextResponse.json({ error: 'Match not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const isTeamLeaderA = match.teamA?.leaderId === userId
|
||||
const isTeamLeaderB = match.teamB?.leaderId === userId
|
||||
const isTeamLeaderA = match.teamAId && user?.ledTeam?.id === match.teamAId;
|
||||
const isTeamLeaderB = match.teamBId && user?.ledTeam?.id === match.teamBId;
|
||||
|
||||
if (!isAdmin && !isTeamLeaderA && !isTeamLeaderB) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
||||
@ -161,8 +157,6 @@ export async function PUT(req: NextRequest, context: { params: { id: string } })
|
||||
const updated = await prisma.match.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
teamA: true,
|
||||
teamB: true,
|
||||
players: {
|
||||
include: {
|
||||
user: true,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// /app/api/matches/route.ts
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/app/lib/prisma'
|
||||
|
||||
@ -12,12 +13,43 @@ export async function GET() {
|
||||
include: {
|
||||
user: true,
|
||||
stats: true,
|
||||
team: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const formatted = matches.map(match => ({
|
||||
id: match.id,
|
||||
map: match.map,
|
||||
demoDate: match.demoDate,
|
||||
matchType: match.matchType,
|
||||
scoreA: match.scoreA,
|
||||
scoreB: match.scoreB,
|
||||
winnerTeam: match.winnerTeam ?? null,
|
||||
teamA: {
|
||||
id: match.teamA?.id ?? null,
|
||||
name: match.teamA?.name ?? 'CT',
|
||||
logo: match.teamA?.logo ?? null,
|
||||
score: match.scoreA,
|
||||
},
|
||||
teamB: {
|
||||
id: match.teamB?.id ?? null,
|
||||
name: match.teamB?.name ?? 'T',
|
||||
logo: match.teamB?.logo ?? null,
|
||||
score: match.scoreB,
|
||||
},
|
||||
players: match.players.map(p => ({
|
||||
steamId: p.steamId,
|
||||
name: p.user?.name,
|
||||
avatar: p.user?.avatar,
|
||||
stats: p.stats,
|
||||
teamId: p.teamId,
|
||||
teamName: p.team?.name ?? null,
|
||||
})),
|
||||
}));
|
||||
|
||||
return NextResponse.json(matches)
|
||||
return NextResponse.json(formatted)
|
||||
} catch (err) {
|
||||
console.error('GET /matches failed:', err)
|
||||
return NextResponse.json({ error: 'Failed to load matches' }, { status: 500 })
|
||||
|
||||
@ -15,6 +15,7 @@ export async function GET(req: NextRequest) {
|
||||
const matchPlayers = await prisma.matchPlayer.findMany({
|
||||
where: { steamId: steamId },
|
||||
select: {
|
||||
teamId: true,
|
||||
team: true,
|
||||
match: {
|
||||
select: {
|
||||
@ -26,6 +27,8 @@ export async function GET(req: NextRequest) {
|
||||
matchType: true,
|
||||
teamAId: true,
|
||||
teamBId: true,
|
||||
winnerTeam: true,
|
||||
demoData: true,
|
||||
},
|
||||
},
|
||||
stats: true,
|
||||
@ -45,13 +48,28 @@ export async function GET(req: NextRequest) {
|
||||
const kd = deaths > 0 ? (kills / deaths).toFixed(2) : '∞';
|
||||
const rankOld = stats?.rankOld ?? null;
|
||||
const rankNew = stats?.rankNew ?? null;
|
||||
const rankChange = typeof rankNew === 'number' && typeof rankOld === 'number' ? rankNew - rankOld : null;
|
||||
const rankChange =
|
||||
typeof rankNew === 'number' && typeof rankOld === 'number'
|
||||
? rankNew - rankOld
|
||||
: null;
|
||||
const matchType = match.matchType ?? 'community';
|
||||
const demoData = match.demoData as any;
|
||||
|
||||
// Spielerteam: CT oder T
|
||||
let playerTeam: string | null = null;
|
||||
let isInTeamA = false;
|
||||
let isInTeamB = false;
|
||||
|
||||
// Spieler war Team A, wenn seine teamId == match.teamAId
|
||||
const isTeamA = mp.team === 'CT';
|
||||
const scoreLeft = isTeamA ? match.scoreA : match.scoreB;
|
||||
const scoreRight = isTeamA ? match.scoreB : match.scoreA;
|
||||
if (demoData?.teamA?.players && demoData?.teamB?.players) {
|
||||
isInTeamA = demoData.teamA.players.some((p: any) => p?.steamId === steamId);
|
||||
isInTeamB = demoData.teamB.players.some((p: any) => p?.steamId === steamId);
|
||||
|
||||
if (isInTeamA) playerTeam = 'CT';
|
||||
if (isInTeamB) playerTeam = 'T';
|
||||
}
|
||||
|
||||
const scoreLeft = isInTeamA ? match.scoreA : match.scoreB;
|
||||
const scoreRight = isInTeamB ? match.scoreB : match.scoreA;
|
||||
|
||||
return {
|
||||
id: match.id,
|
||||
@ -65,6 +83,8 @@ export async function GET(req: NextRequest) {
|
||||
kills,
|
||||
deaths,
|
||||
kd,
|
||||
winnerTeam: match.winnerTeam ?? null,
|
||||
team: mp.team?.name ?? null,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -45,15 +45,14 @@ export default function CompRankBadge({ rank }: Props) {
|
||||
|
||||
return (
|
||||
<Tooltip content={altText}>
|
||||
<div style={{ position: 'relative', width: 70, height: 40 }}>
|
||||
<Image
|
||||
src={`/assets/img/skillgroups/${imageName}`}
|
||||
alt={altText}
|
||||
fill
|
||||
style={{ objectFit: 'contain' }}
|
||||
sizes="(max-width: 768px) 100px, 70px"
|
||||
/>
|
||||
</div>
|
||||
<Image
|
||||
src={`/assets/img/skillgroups/${imageName}`}
|
||||
alt={altText}
|
||||
width={60}
|
||||
height={60}
|
||||
sizes="(max-width: 768px) 100px, 70px"
|
||||
style={{ objectFit: 'contain' }} // ← korrekt!
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import InvitePlayersModal from './InvitePlayersModal'
|
||||
import Modal from './Modal'
|
||||
import { Player, Team } from '../types/team'
|
||||
import { useSession } from 'next-auth/react'
|
||||
import { useWS } from '@/app/lib/useSSEStore'
|
||||
import { useSSE } from '@/app/lib/useSSEStore'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { useTeamManager } from '../hooks/useTeamManager'
|
||||
import Button from './Button'
|
||||
@ -51,7 +51,7 @@ export default function TeamMemberView({
|
||||
setInactivePlayers,
|
||||
}: Props) {
|
||||
const { data: session } = useSession()
|
||||
const { socket } = useWS()
|
||||
const { source, connect } = useSSE()
|
||||
const [kickCandidate, setKickCandidate] = useState<Player | null>(null)
|
||||
const [promoteCandidate, setPromoteCandidate] = useState<Player | null>(null)
|
||||
|
||||
@ -67,44 +67,51 @@ export default function TeamMemberView({
|
||||
const [logoFile, setLogoFile] = useState<File | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket || !team?.id) return
|
||||
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data)
|
||||
|
||||
const relevantTypes = [
|
||||
'team-updated',
|
||||
'team-kick',
|
||||
'team-kick-other',
|
||||
'team-member-joined',
|
||||
'team-member-left',
|
||||
'team-leader-changed',
|
||||
'team-renamed',
|
||||
'team-logo-updated',
|
||||
]
|
||||
|
||||
if (relevantTypes.includes(data.type) && typeof data.teamId === 'string') {
|
||||
fetch(`/api/team/${encodeURIComponent(data.teamId)}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setactivePlayers(
|
||||
(data.activePlayers ?? [])
|
||||
.filter((p: Player) => p?.name)
|
||||
.sort((a: Player, b: Player) => a.name.localeCompare(b.name))
|
||||
);
|
||||
|
||||
setInactivePlayers(
|
||||
(data.inactivePlayers ?? [])
|
||||
.filter((p: Player) => p?.name)
|
||||
.sort((a: Player, b: Player) => a.name.localeCompare(b.name))
|
||||
);
|
||||
})
|
||||
if (session?.user?.steamId) {
|
||||
connect(session.user.steamId)
|
||||
}
|
||||
}
|
||||
}, [session?.user?.steamId])
|
||||
|
||||
socket.addEventListener('message', handleMessage)
|
||||
return () => socket.removeEventListener('message', handleMessage)
|
||||
}, [socket, team?.id])
|
||||
|
||||
useEffect(() => {
|
||||
if (!source || !team?.id) return
|
||||
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data)
|
||||
|
||||
const relevantTypes = [
|
||||
'team-updated',
|
||||
'team-kick',
|
||||
'team-kick-other',
|
||||
'team-member-joined',
|
||||
'team-member-left',
|
||||
'team-leader-changed',
|
||||
'team-renamed',
|
||||
'team-logo-updated',
|
||||
]
|
||||
|
||||
if (relevantTypes.includes(data.type) && typeof data.teamId === 'string') {
|
||||
fetch(`/api/team/${encodeURIComponent(data.teamId)}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setactivePlayers(
|
||||
(data.activePlayers ?? [])
|
||||
.filter((p: Player) => p?.name)
|
||||
.sort((a: Player, b: Player) => a.name.localeCompare(b.name))
|
||||
);
|
||||
|
||||
setInactivePlayers(
|
||||
(data.inactivePlayers ?? [])
|
||||
.filter((p: Player) => p?.name)
|
||||
.sort((a: Player, b: Player) => a.name.localeCompare(b.name))
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
source.addEventListener('message', handleMessage)
|
||||
return () => source.removeEventListener('message', handleMessage)
|
||||
}, [source, team?.id])
|
||||
|
||||
|
||||
const handleDragStart = (event: any) => {
|
||||
|
||||
@ -13,6 +13,8 @@ interface Match {
|
||||
map: string;
|
||||
date: string;
|
||||
score: string;
|
||||
winnerTeam?: string;
|
||||
team?: 'CT' | 'T';
|
||||
matchType: string;
|
||||
rating: string;
|
||||
kills: number;
|
||||
@ -50,7 +52,17 @@ export default function UserMatchesTable() {
|
||||
<Table.Body>
|
||||
{matches.map((m) => {
|
||||
const mapInfo = mapNameMap[m.map] ?? mapNameMap['lobby_mapveto'];
|
||||
const [left, right] = m.score.split(':').map(s => parseInt(s.trim(), 10));
|
||||
const [scoreCT, scoreT] = m.score.split(':').map(s => parseInt(s.trim(), 10));
|
||||
|
||||
let left = scoreCT;
|
||||
let right = scoreT;
|
||||
|
||||
// Score-Reihenfolge anhand des eigenen Teams und Sieger drehen
|
||||
if (m.team === 'T') {
|
||||
left = scoreT;
|
||||
right = scoreCT;
|
||||
}
|
||||
|
||||
|
||||
// Score-Farbe bestimmen
|
||||
let scoreClass = '';
|
||||
|
||||
@ -65,12 +65,21 @@ export const authOptions = (req: NextRequest): NextAuthOptions => ({
|
||||
return session
|
||||
},
|
||||
|
||||
async redirect({ url, baseUrl }) {
|
||||
if (url.includes('/api/auth/signout')) {
|
||||
return `${baseUrl}/` // Zurück zur Startseite
|
||||
redirect({ url, baseUrl }) {
|
||||
const isSignIn = url.startsWith(`${baseUrl}/api/auth/signin`);
|
||||
const isSignOut = url.startsWith(`${baseUrl}/api/auth/signout`);
|
||||
|
||||
if (isSignOut) {
|
||||
return `${baseUrl}/`; // Nach Logout auf Startseite
|
||||
}
|
||||
return `${baseUrl}/dashboard`
|
||||
},
|
||||
|
||||
// Standard-Redirect nach Login
|
||||
if (isSignIn || url === baseUrl) {
|
||||
return `${baseUrl}/dashboard`; // z. B. Dashboard als Startpunkt
|
||||
}
|
||||
|
||||
return url.startsWith(baseUrl) ? url : baseUrl;
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@ -12,12 +12,11 @@ export const useSSE = create<SSEState>((set, get) => {
|
||||
|
||||
const connect = (steamId: string): EventSource | undefined => {
|
||||
const current = get().source
|
||||
if (current) return current // bereits verbunden
|
||||
if (current) return current
|
||||
|
||||
const source = new EventSource(`http://localhost:3001/events?steamId=${steamId}`)
|
||||
|
||||
source.onopen = () => {
|
||||
console.log('[SSE] Verbunden')
|
||||
set({ source, isConnected: true })
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -20,12 +20,12 @@ exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 6.9.0
|
||||
* Query Engine version: 81e4af48011447c3cc503a190e86995b66d2a28e
|
||||
* Prisma Client JS version: 6.10.1
|
||||
* Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "6.9.0",
|
||||
engine: "81e4af48011447c3cc503a190e86995b66d2a28e"
|
||||
client: "6.10.1",
|
||||
engine: "9b628578b3b7cae625e8c927178f15a170e74a9c"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = () => {
|
||||
|
||||
1378
src/generated/prisma/index.d.ts
vendored
1378
src/generated/prisma/index.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "prisma-client-7dc6d5614c138eb3659cdd5dbb69f5d3b2be84ae9983dbfa39f9833b9ad6da8c",
|
||||
"name": "prisma-client-81fe4a88a75a445ee239171472edc8b1edb558143434347db08d32212685268e",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"browser": "index-browser.js",
|
||||
@ -141,6 +141,6 @@
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"version": "6.9.0",
|
||||
"version": "6.10.1",
|
||||
"sideEffects": false
|
||||
}
|
||||
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp12760
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp12760
Normal file
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp23572
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp23572
Normal file
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp24776
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp24776
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
40
src/generated/prisma/runtime/library.d.ts
vendored
40
src/generated/prisma/runtime/library.d.ts
vendored
@ -209,7 +209,7 @@ declare const ColumnTypeEnum: {
|
||||
|
||||
declare type CompactedBatchResponse = {
|
||||
type: 'compacted';
|
||||
plan: object;
|
||||
plan: {};
|
||||
arguments: Record<string, {}>[];
|
||||
nestedSelection: string[];
|
||||
keys: string[];
|
||||
@ -255,6 +255,7 @@ declare type ComputedFieldsMap = {
|
||||
declare type ConnectionInfo = {
|
||||
schemaName?: string;
|
||||
maxBindValues?: number;
|
||||
supportsRelationJoins: boolean;
|
||||
};
|
||||
|
||||
declare type ConnectorType = 'mysql' | 'mongodb' | 'sqlite' | 'postgresql' | 'postgres' | 'prisma+postgres' | 'sqlserver' | 'cockroachdb';
|
||||
@ -1153,10 +1154,22 @@ declare type Error_2 = {
|
||||
column?: string;
|
||||
} | {
|
||||
kind: 'UniqueConstraintViolation';
|
||||
fields: string[];
|
||||
constraint?: {
|
||||
fields: string[];
|
||||
} | {
|
||||
index: string;
|
||||
} | {
|
||||
foreignKey: {};
|
||||
};
|
||||
} | {
|
||||
kind: 'NullConstraintViolation';
|
||||
fields: string[];
|
||||
constraint?: {
|
||||
fields: string[];
|
||||
} | {
|
||||
index: string;
|
||||
} | {
|
||||
foreignKey: {};
|
||||
};
|
||||
} | {
|
||||
kind: 'ForeignKeyConstraintViolation';
|
||||
constraint?: {
|
||||
@ -1189,8 +1202,19 @@ declare type Error_2 = {
|
||||
} | {
|
||||
kind: 'TooManyConnections';
|
||||
cause: string;
|
||||
} | {
|
||||
kind: 'ValueOutOfRange';
|
||||
cause: string;
|
||||
} | {
|
||||
kind: 'MissingFullTextSearchIndex';
|
||||
} | {
|
||||
kind: 'SocketTimeout';
|
||||
} | {
|
||||
kind: 'InconsistentColumnData';
|
||||
cause: string;
|
||||
} | {
|
||||
kind: 'TransactionAlreadyClosed';
|
||||
cause: string;
|
||||
} | {
|
||||
kind: 'postgres';
|
||||
code: string;
|
||||
@ -1211,6 +1235,10 @@ declare type Error_2 = {
|
||||
*/
|
||||
extendedCode: number;
|
||||
message: string;
|
||||
} | {
|
||||
kind: 'mssql';
|
||||
code: number;
|
||||
message: string;
|
||||
};
|
||||
|
||||
declare type ErrorCapturingFunction<T> = T extends (...args: infer A) => Promise<infer R> ? (...args: A) => Promise<Result_4<ErrorCapturingInterface<R>>> : T extends (...args: infer A) => infer R ? (...args: A) => Result_4<ErrorCapturingInterface<R>> : T;
|
||||
@ -2383,7 +2411,7 @@ export declare const objectEnumValues: {
|
||||
};
|
||||
};
|
||||
|
||||
declare const officialPrismaAdapters: readonly ["@prisma/adapter-planetscale", "@prisma/adapter-neon", "@prisma/adapter-libsql", "@prisma/adapter-d1", "@prisma/adapter-pg", "@prisma/adapter-pg-worker"];
|
||||
declare const officialPrismaAdapters: readonly ["@prisma/adapter-planetscale", "@prisma/adapter-neon", "@prisma/adapter-libsql", "@prisma/adapter-d1", "@prisma/adapter-pg", "@prisma/adapter-mssql"];
|
||||
|
||||
export declare type Omission = Record<string, boolean | Skip>;
|
||||
|
||||
@ -2661,7 +2689,7 @@ declare type PrismaPromiseTransaction<PayloadType = unknown> = PrismaPromiseBatc
|
||||
|
||||
export declare const PrivateResultType: unique symbol;
|
||||
|
||||
declare type Provider = 'mysql' | 'postgres' | 'sqlite';
|
||||
declare type Provider = 'mysql' | 'postgres' | 'sqlite' | 'sqlserver';
|
||||
|
||||
declare namespace Public {
|
||||
export {
|
||||
@ -2699,7 +2727,7 @@ declare interface Queryable<Query, Result> extends AdapterInfo {
|
||||
}
|
||||
|
||||
declare type QueryCompiler = {
|
||||
compile(request: string): string;
|
||||
compile(request: string): {};
|
||||
compileBatch(batchRequest: string): BatchResponse;
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
4
src/generated/prisma/runtime/react-native.js
vendored
4
src/generated/prisma/runtime/react-native.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -25,6 +25,9 @@ model User {
|
||||
team Team? @relation("UserTeam", fields: [teamId], references: [id])
|
||||
ledTeam Team? @relation("TeamLeader")
|
||||
|
||||
matchesAsTeamA Match[] @relation("TeamAPlayers")
|
||||
matchesAsTeamB Match[] @relation("TeamBPlayers")
|
||||
|
||||
premierRank Int?
|
||||
authCode String?
|
||||
lastKnownShareCode String?
|
||||
@ -49,12 +52,13 @@ model Team {
|
||||
activePlayers String[]
|
||||
inactivePlayers String[]
|
||||
|
||||
leader User? @relation("TeamLeader", fields: [leaderId], references: [steamId])
|
||||
members User[] @relation("UserTeam")
|
||||
invites TeamInvite[]
|
||||
matchesAsTeamA Match[] @relation("Match_TeamA")
|
||||
matchesAsTeamB Match[] @relation("Match_TeamB")
|
||||
matchPlayers MatchPlayer[]
|
||||
leader User? @relation("TeamLeader", fields: [leaderId], references: [steamId])
|
||||
members User[] @relation("UserTeam")
|
||||
invites TeamInvite[]
|
||||
matchPlayers MatchPlayer[]
|
||||
|
||||
matchesAsTeamA Match[] @relation("MatchTeamA")
|
||||
matchesAsTeamB Match[] @relation("MatchTeamB")
|
||||
}
|
||||
|
||||
model TeamInvite {
|
||||
@ -98,9 +102,13 @@ model Match {
|
||||
scoreB Int?
|
||||
|
||||
teamAId String?
|
||||
teamA Team? @relation("MatchTeamA", fields: [teamAId], references: [id])
|
||||
|
||||
teamBId String?
|
||||
teamA Team? @relation("Match_TeamA", fields: [teamAId], references: [id])
|
||||
teamB Team? @relation("Match_TeamB", fields: [teamBId], references: [id])
|
||||
teamB Team? @relation("MatchTeamB", fields: [teamBId], references: [id])
|
||||
|
||||
teamAUsers User[] @relation("TeamAPlayers")
|
||||
teamBUsers User[] @relation("TeamBPlayers")
|
||||
|
||||
filePath String?
|
||||
demoFile DemoFile?
|
||||
|
||||
@ -20,12 +20,12 @@ exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 6.9.0
|
||||
* Query Engine version: 81e4af48011447c3cc503a190e86995b66d2a28e
|
||||
* Prisma Client JS version: 6.10.1
|
||||
* Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "6.9.0",
|
||||
engine: "81e4af48011447c3cc503a190e86995b66d2a28e"
|
||||
client: "6.10.1",
|
||||
engine: "9b628578b3b7cae625e8c927178f15a170e74a9c"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = () => {
|
||||
|
||||
@ -38,21 +38,24 @@ interface DemoMatchData {
|
||||
map: string;
|
||||
filePath: string;
|
||||
meta: {
|
||||
tickRate: number;
|
||||
duration: number;
|
||||
map: string;
|
||||
players: PlayerStatsExtended[];
|
||||
scoreCT?: number;
|
||||
scoreT?: number;
|
||||
demoDate?: Date;
|
||||
teamA?: {
|
||||
name: string;
|
||||
score: number;
|
||||
players: PlayerStatsExtended[];
|
||||
};
|
||||
teamB?: {
|
||||
name: string;
|
||||
score: number;
|
||||
players: PlayerStatsExtended[];
|
||||
};
|
||||
winnerTeam?: string;
|
||||
roundCount?: number;
|
||||
roundHistory?: { round: number; winner: string; winReason: string }[];
|
||||
demoDate?: Date;
|
||||
teamCT: PlayerStatsExtended[];
|
||||
teamT: PlayerStatsExtended[];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export async function parseAndStoreDemo(
|
||||
demoPath: string,
|
||||
steamId: string,
|
||||
@ -62,6 +65,7 @@ export async function parseAndStoreDemo(
|
||||
if (!parsed) return null;
|
||||
|
||||
let actualDemoPath = demoPath;
|
||||
|
||||
if (parsed.map && parsed.map !== 'unknownmap' && demoPath.includes('unknownmap')) {
|
||||
const oldName = path.basename(demoPath);
|
||||
const newName = oldName.replace('unknownmap', parsed.map);
|
||||
@ -92,9 +96,57 @@ export async function parseAndStoreDemo(
|
||||
const existing = await prisma.match.findUnique({
|
||||
where: { id: parsed.matchId },
|
||||
});
|
||||
|
||||
if (existing) return null;
|
||||
|
||||
const teamAIds: string[] = [];
|
||||
const teamBIds: string[] = [];
|
||||
|
||||
const allPlayers = [
|
||||
...(parsed.meta.teamA?.players || []),
|
||||
...(parsed.meta.teamB?.players || []),
|
||||
];
|
||||
|
||||
for (const player of allPlayers) {
|
||||
let playerUser = await prisma.user.findUnique({
|
||||
where: { steamId: player.steamId },
|
||||
});
|
||||
|
||||
let steamProfile = null;
|
||||
if (!playerUser?.name || !playerUser?.avatar) {
|
||||
steamProfile = await fetchSteamProfile(player.steamId).catch(() => null);
|
||||
await delay(5000);
|
||||
}
|
||||
|
||||
const isPremier = path.basename(actualDemoPath).toLowerCase().endsWith('_premier.dem');
|
||||
const updatedFields: Partial<{ name: string; avatar: string; premierRank: number }> = {};
|
||||
|
||||
if (!playerUser) {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
steamId: player.steamId,
|
||||
name: steamProfile?.name ?? player.name,
|
||||
avatar: steamProfile?.avatar ?? undefined,
|
||||
premierRank: isPremier ? player.rankNew ?? undefined : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
if (steamProfile?.name && playerUser.name !== steamProfile.name) updatedFields.name = steamProfile.name;
|
||||
if (steamProfile?.avatar && playerUser.avatar !== steamProfile.avatar) updatedFields.avatar = steamProfile.avatar;
|
||||
if (Object.keys(updatedFields).length > 0) {
|
||||
await prisma.user.update({
|
||||
where: { steamId: player.steamId },
|
||||
data: updatedFields,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed.meta.teamA?.players.some(p => p.steamId === player.steamId)) {
|
||||
teamAIds.push(player.steamId);
|
||||
} else if (parsed.meta.teamB?.players.some(p => p.steamId === player.steamId)) {
|
||||
teamBIds.push(player.steamId);
|
||||
}
|
||||
}
|
||||
|
||||
const match = await prisma.match.create({
|
||||
data: {
|
||||
id: parsed.matchId,
|
||||
@ -106,12 +158,18 @@ export async function parseAndStoreDemo(
|
||||
: demoPath.endsWith('_competitive.dem')
|
||||
? 'competitive'
|
||||
: 'community',
|
||||
scoreA: parsed.meta.scoreCT,
|
||||
scoreB: parsed.meta.scoreT,
|
||||
scoreA: parsed.meta.teamA?.score,
|
||||
scoreB: parsed.meta.teamB?.score,
|
||||
winnerTeam: parsed.meta.winnerTeam ?? null,
|
||||
roundCount: parsed.meta.roundCount ?? null,
|
||||
roundHistory: parsed.meta.roundHistory ?? undefined,
|
||||
demoDate: parsed.meta.demoDate ?? null,
|
||||
teamAUsers: {
|
||||
connect: teamAIds.map(steamId => ({ steamId })),
|
||||
},
|
||||
teamBUsers: {
|
||||
connect: teamBIds.map(steamId => ({ steamId })),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -125,50 +183,11 @@ export async function parseAndStoreDemo(
|
||||
},
|
||||
});
|
||||
|
||||
for (const player of parsed.meta.players) {
|
||||
let playerUser = await prisma.user.findUnique({
|
||||
where: { steamId: player.steamId },
|
||||
});
|
||||
|
||||
let steamProfile = null;
|
||||
if (!playerUser?.name || !playerUser?.avatar) {
|
||||
steamProfile = await fetchSteamProfile(player.steamId).catch(() => null);
|
||||
await delay(5000);
|
||||
}
|
||||
|
||||
const isPremier = path.basename(actualDemoPath).toLowerCase().endsWith('_premier.dem');
|
||||
|
||||
const updatedFields: Partial<{ name: string; avatar: string; premierRank: number }> = {};
|
||||
|
||||
if (!playerUser) {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
steamId: player.steamId,
|
||||
name: steamProfile?.name ?? player.name,
|
||||
avatar: steamProfile?.avatar ?? undefined,
|
||||
premierRank: isPremier ? player.rankNew ?? undefined : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
if (steamProfile?.name && playerUser.name !== steamProfile.name) {
|
||||
updatedFields.name = steamProfile.name;
|
||||
}
|
||||
if (steamProfile?.avatar && playerUser.avatar !== steamProfile.avatar) {
|
||||
updatedFields.avatar = steamProfile.avatar;
|
||||
}
|
||||
|
||||
if (Object.keys(updatedFields).length > 0) {
|
||||
await prisma.user.update({
|
||||
where: { steamId: player.steamId },
|
||||
data: updatedFields,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const player of allPlayers) {
|
||||
const teamId =
|
||||
parsed.meta.teamCT && player.team === 'CT'
|
||||
match.teamAId && parsed.meta.teamA?.players.some(p => p.steamId === player.steamId)
|
||||
? match.teamAId
|
||||
: parsed.meta.teamT && player.team === 'T'
|
||||
: match.teamBId && parsed.meta.teamB?.players.some(p => p.steamId === player.steamId)
|
||||
? match.teamBId
|
||||
: undefined;
|
||||
|
||||
@ -185,7 +204,7 @@ export async function parseAndStoreDemo(
|
||||
matchId_steamId: {
|
||||
matchId: match.id,
|
||||
steamId: player.steamId,
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
kills: player.kills,
|
||||
@ -261,6 +280,8 @@ async function parseDemoViaGo(filePath: string, shareCode: string): Promise<Demo
|
||||
if (code === 0) {
|
||||
try {
|
||||
const parsed = JSON.parse(output);
|
||||
console.log(parsed.teamA.players);
|
||||
console.log(parsed.teamB.players);
|
||||
resolve({
|
||||
matchId,
|
||||
map: parsed.map,
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import cron from 'node-cron';
|
||||
import { prisma } from '../app/lib/prisma.js';
|
||||
import { runDownloaderForUser } from './runDownloaderForUser.js';
|
||||
import { sendServerWebSocketMessage } from '../app/lib/sse-server-client.js';
|
||||
import { sendServerSSEMessage } from '../app/lib/sse-server-client.js';
|
||||
import { decrypt } from '../app/lib/crypto.js';
|
||||
import { encodeMatch, decodeMatchShareCode } from 'csgo-sharecode';
|
||||
import { log } from '../../scripts/cs2-cron-runner.js';
|
||||
import { getNextShareCodeFromAPI } from './getNextShareCodeFromAPI.js';
|
||||
import { updatePremierRanksForUser } from './updatePremierRanks';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
|
||||
let isRunning = false;
|
||||
|
||||
@ -64,7 +67,7 @@ async function runMatchCheck() {
|
||||
},
|
||||
});
|
||||
|
||||
await sendServerWebSocketMessage({
|
||||
await sendServerSSEMessage({
|
||||
type: 'expired-sharecode',
|
||||
targetUserIds: [user.steamId],
|
||||
message: notification.message,
|
||||
@ -117,6 +120,22 @@ async function runMatchCheck() {
|
||||
|
||||
const shareCode = encodeMatch(matchInfo);
|
||||
|
||||
const expectedFilename = `${matchInfo.matchId}.dem`;
|
||||
const expectedFilePath = path.join(process.cwd(), 'demos', expectedFilename);
|
||||
|
||||
if (fs.existsSync(expectedFilePath)) {
|
||||
log(`[${user.steamId}] 📁 Match ${matchInfo.matchId} wurde bereits als Datei gespeichert – übersprungen`);
|
||||
|
||||
await prisma.user.update({
|
||||
where: { steamId: user.steamId },
|
||||
data: { lastKnownShareCode: nextShareCode },
|
||||
});
|
||||
|
||||
latestKnownCode = nextShareCode;
|
||||
nextShareCode = await getNextShareCodeFromAPI(user.steamId, decryptedAuthCode, latestKnownCode);
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = await runDownloaderForUser({
|
||||
...user,
|
||||
lastKnownShareCode: shareCode,
|
||||
@ -170,7 +189,7 @@ async function runMatchCheck() {
|
||||
},
|
||||
});
|
||||
|
||||
await sendServerWebSocketMessage({
|
||||
await sendServerSSEMessage({
|
||||
type: 'new-cs2-match',
|
||||
targetUserIds: [user.steamId],
|
||||
message: notification.message,
|
||||
|
||||
@ -3,6 +3,7 @@ import path from 'path';
|
||||
import { Match, User } from '@/generated/prisma';
|
||||
import { parseAndStoreDemo } from './parseAndStoreDemo';
|
||||
import { log } from '../../scripts/cs2-cron-runner.js';
|
||||
import { prisma } from '../app/lib/prisma.js';
|
||||
|
||||
export async function runDownloaderForUser(user: User): Promise<{
|
||||
newMatches: Match[];
|
||||
@ -37,7 +38,19 @@ export async function runDownloaderForUser(user: User): Promise<{
|
||||
return { newMatches: [], latestShareCode: shareCode };
|
||||
}
|
||||
|
||||
log(`[${steamId}] 📂 Analysiere: ${path.basename(demoPath)}`);
|
||||
const filename = path.basename(demoPath);
|
||||
const matchId = filename.replace(/\.dem$/, '');
|
||||
|
||||
const existing = await prisma.match.findUnique({
|
||||
where: { id: matchId },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
log(`[${steamId}] 🔁 Match ${matchId} wurde bereits analysiert – übersprungen`, 'info');
|
||||
return { newMatches: [], latestShareCode: shareCode };
|
||||
}
|
||||
|
||||
log(`[${steamId}] 📂 Analysiere: ${filename}`);
|
||||
|
||||
const absolutePath = path.resolve(__dirname, '../../../cs2-demo-downloader', demoPath);
|
||||
const match = await parseAndStoreDemo(absolutePath, steamId, shareCode);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user