fix: update admin scripts to use PostgreSQL adapter and dotenv
This commit is contained in:
@@ -1,7 +1,13 @@
|
|||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,22 +1,42 @@
|
|||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const { betterAuth } = require('better-auth');
|
const { betterAuth } = require('better-auth');
|
||||||
const { prismaAdapter } = require('better-auth/adapters/prisma');
|
const { prismaAdapter } = require('better-auth/adapters/prisma');
|
||||||
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Check if DATABASE_URL is set
|
||||||
|
if (!process.env.DATABASE_URL) {
|
||||||
|
console.error('❌ DATABASE_URL environment variable is not set');
|
||||||
|
console.error(' Please set DATABASE_URL in your environment or .env file');
|
||||||
|
console.error(' Example: DATABASE_URL="postgresql://user:password@localhost:5432/dbname"');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create PrismaClient with adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
// Create Better Auth instance
|
// Create Better Auth instance
|
||||||
const auth = betterAuth({
|
const auth = betterAuth({
|
||||||
database: prismaAdapter(prisma, {
|
database: prismaAdapter(prisma, {
|
||||||
provider: 'sqlite',
|
provider: 'postgresql',
|
||||||
}),
|
}),
|
||||||
emailAndPassword: {
|
emailAndPassword: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
baseURL: process.env.BETTER_AUTH_URL || 'http://localhost:3000',
|
baseURL: process.env.BETTER_AUTH_URL || 'http://localhost:3000',
|
||||||
|
secret: process.env.BETTER_AUTH_SECRET || process.env.NEXTAUTH_SECRET || 'your-secret-key-here-change-this-in-production',
|
||||||
});
|
});
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
|
// Test database connection first
|
||||||
|
console.log('Connecting to database...');
|
||||||
|
await prisma.$connect();
|
||||||
|
console.log('✅ Connected to database');
|
||||||
|
|
||||||
// Check if user exists
|
// Check if user exists
|
||||||
const existing = await prisma.user.findUnique({
|
const existing = await prisma.user.findUnique({
|
||||||
where: { email: 'david@dhg.lol' }
|
where: { email: 'david@dhg.lol' }
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function listUsers() {
|
async function listUsers() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,7 +7,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const prisma = new PrismaClient();
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
// Pattern to extract base name from "Name-Timestamp-RandomString" format
|
// Pattern to extract base name from "Name-Timestamp-RandomString" format
|
||||||
const NAME_PATTERN = /^(.+?)-(\d{13})-([a-z0-9]{6})$/;
|
const NAME_PATTERN = /^(.+?)-(\d{13})-([a-z0-9]{6})$/;
|
||||||
|
|||||||
+281
-2
@@ -8,9 +8,288 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const { recalculateAllElo } = require('../src/lib/elo-utils');
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
|
// Simple Elo calculation functions (copied from src/lib/elo-utils.ts)
|
||||||
|
function calculateTeamElo(rating1, rating2) {
|
||||||
|
return (rating1 + rating2) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateExpectedScore(ratingA, ratingB) {
|
||||||
|
const ratingDiff = ratingA - ratingB;
|
||||||
|
return 1 / (1 + Math.pow(10, -ratingDiff / 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateEloChange(rating, opponentRating, actualScore, kFactor = 32) {
|
||||||
|
const expectedScore = calculateExpectedScore(rating, opponentRating);
|
||||||
|
return kFactor * (actualScore - expectedScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateTeamEloChange(team1Rating, team2Rating, team1Score, kFactor = 32) {
|
||||||
|
return calculateEloChange(team1Rating, team2Rating, team1Score, kFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate all Elo ratings
|
||||||
|
async function recalculateAllElo(prisma) {
|
||||||
|
// Get all matches in chronological order
|
||||||
|
const matches = await prisma.match.findMany({
|
||||||
|
orderBy: { playedAt: 'asc' },
|
||||||
|
include: {
|
||||||
|
team1P1: true,
|
||||||
|
team1P2: true,
|
||||||
|
team2P1: true,
|
||||||
|
team2P2: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset all player stats to zero
|
||||||
|
await prisma.player.updateMany({
|
||||||
|
data: {
|
||||||
|
currentElo: 1000,
|
||||||
|
gamesPlayed: 0,
|
||||||
|
wins: 0,
|
||||||
|
losses: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete all existing elo snapshots
|
||||||
|
await prisma.eloSnapshot.deleteMany({});
|
||||||
|
|
||||||
|
// Delete all existing partnership stats (will be rebuilt)
|
||||||
|
await prisma.partnershipStat.deleteMany({});
|
||||||
|
|
||||||
|
// Initialize in-memory tracking for all players
|
||||||
|
const playerStats = new Map();
|
||||||
|
|
||||||
|
// Initialize partnership tracking
|
||||||
|
const partnershipStats = new Map();
|
||||||
|
|
||||||
|
// Helper to get/create player stats
|
||||||
|
const getPlayerStats = (playerId) => {
|
||||||
|
if (!playerStats.has(playerId)) {
|
||||||
|
playerStats.set(playerId, { rating: 1000, gamesPlayed: 0, wins: 0, losses: 0 });
|
||||||
|
}
|
||||||
|
return playerStats.get(playerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to get partnership key (sorted player IDs)
|
||||||
|
const getPartnershipKey = (p1Id, p2Id) => {
|
||||||
|
return [Math.min(p1Id, p2Id), Math.max(p1Id, p2Id)].join('-');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to get/update partnership stats
|
||||||
|
const getPartnershipStats = (p1Id, p2Id) => {
|
||||||
|
const key = getPartnershipKey(p1Id, p2Id);
|
||||||
|
if (!partnershipStats.has(key)) {
|
||||||
|
partnershipStats.set(key, { gamesPlayed: 0, wins: 0, losses: 0, totalEloChange: 0, lastPlayed: null });
|
||||||
|
}
|
||||||
|
return partnershipStats.get(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process each match in chronological order
|
||||||
|
for (const match of matches) {
|
||||||
|
const { team1P1, team1P2, team2P1, team2P2, team1Score, team2Score, id: matchId, playedAt } = match;
|
||||||
|
|
||||||
|
// Get current ratings for all players
|
||||||
|
const p1Rating = getPlayerStats(team1P1.id).rating;
|
||||||
|
const p2Rating = getPlayerStats(team1P2.id).rating;
|
||||||
|
const p3Rating = getPlayerStats(team2P1.id).rating;
|
||||||
|
const p4Rating = getPlayerStats(team2P2.id).rating;
|
||||||
|
|
||||||
|
// Calculate team ratings
|
||||||
|
const team1Rating = calculateTeamElo(p1Rating, p2Rating);
|
||||||
|
const team2Rating = calculateTeamElo(p3Rating, p4Rating);
|
||||||
|
|
||||||
|
// Determine match result (team1 score based on points)
|
||||||
|
const team1ScoreNormalized = team1Score > team2Score ? 1 : team1Score === team2Score ? 0.5 : 0;
|
||||||
|
|
||||||
|
// Calculate team ELO changes
|
||||||
|
const team1Change = calculateTeamEloChange(team1Rating, team2Rating, team1ScoreNormalized);
|
||||||
|
const team2Change = -team1Change; // Equal and opposite
|
||||||
|
|
||||||
|
// Split changes evenly between partners
|
||||||
|
const p1Change = Math.round(team1Change / 2);
|
||||||
|
const p2Change = Math.round(team1Change / 2);
|
||||||
|
const p3Change = Math.round(team2Change / 2);
|
||||||
|
const p4Change = Math.round(team2Change / 2);
|
||||||
|
|
||||||
|
// Update player stats
|
||||||
|
const team1Won = team1Score > team2Score;
|
||||||
|
const team2Won = team2Score > team1Score;
|
||||||
|
const isTie = team1Score === team2Score;
|
||||||
|
|
||||||
|
// Update Player 1 (team 1, player 1)
|
||||||
|
const stats1 = getPlayerStats(team1P1.id);
|
||||||
|
stats1.rating += p1Change;
|
||||||
|
stats1.gamesPlayed += 1;
|
||||||
|
if (isTie) {
|
||||||
|
// For ties, don't increment wins or losses (gamesPlayed is already incremented)
|
||||||
|
} else if (team1Won) {
|
||||||
|
stats1.wins += 1;
|
||||||
|
} else {
|
||||||
|
stats1.losses += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Player 2 (team 1, player 2)
|
||||||
|
const stats2 = getPlayerStats(team1P2.id);
|
||||||
|
stats2.rating += p2Change;
|
||||||
|
stats2.gamesPlayed += 1;
|
||||||
|
if (isTie) {
|
||||||
|
// For ties, don't increment wins or losses (gamesPlayed is already incremented)
|
||||||
|
} else if (team1Won) {
|
||||||
|
stats2.wins += 1;
|
||||||
|
} else {
|
||||||
|
stats2.losses += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Player 3 (team 2, player 1)
|
||||||
|
const stats3 = getPlayerStats(team2P1.id);
|
||||||
|
stats3.rating += p3Change;
|
||||||
|
stats3.gamesPlayed += 1;
|
||||||
|
if (isTie) {
|
||||||
|
// For ties, don't increment wins or losses (gamesPlayed is already incremented)
|
||||||
|
} else if (team2Won) {
|
||||||
|
stats3.wins += 1;
|
||||||
|
} else {
|
||||||
|
stats3.losses += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Player 4 (team 2, player 2)
|
||||||
|
const stats4 = getPlayerStats(team2P2.id);
|
||||||
|
stats4.rating += p4Change;
|
||||||
|
stats4.gamesPlayed += 1;
|
||||||
|
if (isTie) {
|
||||||
|
// For ties, don't increment wins or losses (gamesPlayed is already incremented)
|
||||||
|
} else if (team2Won) {
|
||||||
|
stats4.wins += 1;
|
||||||
|
} else {
|
||||||
|
stats4.losses += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update partnership stats for team 1
|
||||||
|
const partnership1 = getPartnershipStats(team1P1.id, team1P2.id);
|
||||||
|
partnership1.gamesPlayed += 1;
|
||||||
|
if (isTie) {
|
||||||
|
// For ties, don't increment wins or losses (gamesPlayed is already incremented)
|
||||||
|
} else if (team1Won) {
|
||||||
|
partnership1.wins += 1;
|
||||||
|
} else {
|
||||||
|
partnership1.losses += 1;
|
||||||
|
}
|
||||||
|
partnership1.totalEloChange += p1Change + p2Change;
|
||||||
|
if (playedAt && (!partnership1.lastPlayed || playedAt > partnership1.lastPlayed)) {
|
||||||
|
partnership1.lastPlayed = playedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update partnership stats for team 2
|
||||||
|
const partnership2 = getPartnershipStats(team2P1.id, team2P2.id);
|
||||||
|
partnership2.gamesPlayed += 1;
|
||||||
|
if (isTie) {
|
||||||
|
// For ties, don't increment wins or losses (gamesPlayed is already incremented)
|
||||||
|
} else if (team2Won) {
|
||||||
|
partnership2.wins += 1;
|
||||||
|
} else {
|
||||||
|
partnership2.losses += 1;
|
||||||
|
}
|
||||||
|
partnership2.totalEloChange += p3Change + p4Change;
|
||||||
|
if (playedAt && (!partnership2.lastPlayed || playedAt > partnership2.lastPlayed)) {
|
||||||
|
partnership2.lastPlayed = playedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create elo snapshots for all players
|
||||||
|
const snapshotData = [
|
||||||
|
{ playerId: team1P1.id, ratingBefore: p1Rating, ratingChange: p1Change },
|
||||||
|
{ playerId: team1P2.id, ratingBefore: p2Rating, ratingChange: p2Change },
|
||||||
|
{ playerId: team2P1.id, ratingBefore: p3Rating, ratingChange: p3Change },
|
||||||
|
{ playerId: team2P2.id, ratingBefore: p4Rating, ratingChange: p4Change },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const snapshot of snapshotData) {
|
||||||
|
await prisma.eloSnapshot.create({
|
||||||
|
data: {
|
||||||
|
playerId: snapshot.playerId,
|
||||||
|
matchId: matchId,
|
||||||
|
ratingBefore: snapshot.ratingBefore,
|
||||||
|
ratingAfter: snapshot.ratingBefore + snapshot.ratingChange,
|
||||||
|
ratingChange: snapshot.ratingChange,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write all updated player stats to database
|
||||||
|
const playerUpdatePromises = Array.from(playerStats.entries()).map(
|
||||||
|
async ([playerId, stats]) =>
|
||||||
|
await prisma.player.update({
|
||||||
|
where: { id: playerId },
|
||||||
|
data: {
|
||||||
|
currentElo: stats.rating,
|
||||||
|
gamesPlayed: stats.gamesPlayed,
|
||||||
|
wins: stats.wins,
|
||||||
|
losses: stats.losses,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(playerUpdatePromises);
|
||||||
|
|
||||||
|
// Write all partnership stats to database
|
||||||
|
const partnershipUpdatePromises = Array.from(partnershipStats.entries()).map(async ([key, stats]) => {
|
||||||
|
const [player1Id, player2Id] = key.split('-').map(Number);
|
||||||
|
|
||||||
|
// Check if the partnership stat already exists
|
||||||
|
const existingStat = await prisma.partnershipStat.findFirst({
|
||||||
|
where: {
|
||||||
|
player1Id,
|
||||||
|
player2Id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingStat) {
|
||||||
|
// Update existing record
|
||||||
|
return await prisma.partnershipStat.update({
|
||||||
|
where: {
|
||||||
|
id: existingStat.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
gamesPlayed: stats.gamesPlayed,
|
||||||
|
wins: stats.wins,
|
||||||
|
losses: stats.losses,
|
||||||
|
totalEloChange: stats.totalEloChange,
|
||||||
|
lastPlayed: stats.lastPlayed,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create new record
|
||||||
|
return await prisma.partnershipStat.create({
|
||||||
|
data: {
|
||||||
|
player1Id,
|
||||||
|
player2Id,
|
||||||
|
gamesPlayed: stats.gamesPlayed,
|
||||||
|
wins: stats.wins,
|
||||||
|
losses: stats.losses,
|
||||||
|
totalEloChange: stats.totalEloChange,
|
||||||
|
lastPlayed: stats.lastPlayed,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(partnershipUpdatePromises);
|
||||||
|
|
||||||
|
return {
|
||||||
|
matchesProcessed: matches.length,
|
||||||
|
playersUpdated: playerStats.size,
|
||||||
|
partnershipsUpdated: partnershipStats.size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('Starting ELO recalculation...');
|
console.log('Starting ELO recalculation...');
|
||||||
|
|||||||
+7
-1
@@ -7,8 +7,14 @@
|
|||||||
|
|
||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('🌱 Seeding database...\n');
|
console.log('🌱 Seeding database...\n');
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -6,7 +6,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const prisma = new PrismaClient();
|
const { PrismaPg } = require('@prisma/adapter-pg');
|
||||||
|
|
||||||
|
// Load environment variables from .env file
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Create PrismaClient with PostgreSQL adapter (matching src/lib/prisma.ts)
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function updateUserName(userId, newName) {
|
async function updateUserName(userId, newName) {
|
||||||
if (!userId || !newName) {
|
if (!userId || !newName) {
|
||||||
|
|||||||
Reference in New Issue
Block a user