@@ -94,5 +96,7 @@ LoadingSpinner.UWSpinner = ({ size = 'md' }) => {
);
};
+UWSpinner.displayName = 'LoadingSpinner.UWSpinner';
+LoadingSpinner.UWSpinner = UWSpinner;
export default LoadingSpinner;
diff --git a/src/middleware.ts b/src/middleware.ts
index 36a861612..4f7f5681c 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -43,8 +43,6 @@ export default authMiddleware({
'/games',
'/leaderboard',
'/tasks',
- '/change-password',
- '/change-username',
];
const isProtectedRoute = protectedRoutes.some(route => pathname.startsWith(route));
diff --git a/src/pages/api/bets/auto-settle-user.js b/src/pages/api/bets/auto-settle-user.js
index bc1d41704..65b50a4ee 100644
--- a/src/pages/api/bets/auto-settle-user.js
+++ b/src/pages/api/bets/auto-settle-user.js
@@ -13,7 +13,6 @@
*/
import connectDB from '@server/db';
-import Game from '@server/models/Game';
import Bet from '@server/models/Bet';
import User from '@server/models/User';
import { getAuth } from '@clerk/nextjs/server';
diff --git a/src/pages/api/bets/settle-all.js b/src/pages/api/bets/settle-all.js
deleted file mode 100644
index d28ae01cd..000000000
--- a/src/pages/api/bets/settle-all.js
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * Settle All Completed Games
- * Processes all completed games with pending bets
- */
-
-import connectDB from '@server/db';
-import Game from '@server/models/Game';
-import Bet from '@server/models/Bet';
-import User from '@server/models/User';
-import { getAuth } from '@clerk/nextjs/server';
-
-export default async function handler(req, res) {
- if (req.method !== 'POST') {
- return res.status(405).json({ error: 'Method not allowed' });
- }
-
- try {
- // Only allow authenticated users (you can add admin check here)
- const { userId } = getAuth(req);
- if (!userId) {
- return res.status(401).json({ error: 'Unauthorized' });
- }
-
- await connectDB();
-
- // Find all completed games with a winner set
- const completedGames = await Game.find({
- status: 'completed',
- winner: { $in: ['home', 'away'] }, // Exclude ties and null
- });
-
- if (completedGames.length === 0) {
- return res.status(200).json({
- success: true,
- message: 'No completed games to settle',
- gamesProcessed: 0,
- });
- }
-
- console.log(`šÆ Processing ${completedGames.length} completed games`);
-
- let totalGamesSettled = 0;
- let totalBetsSettled = 0;
- let totalWon = 0;
- let totalLost = 0;
- let totalPayout = 0;
- const gameResults = [];
-
- // Process each game
- for (const game of completedGames) {
- try {
- // Get all pending bets for this game
- const pendingBets = await Bet.find({
- gameId: game._id,
- status: 'pending'
- }).populate('userId');
-
- if (pendingBets.length === 0) {
- console.log(`āļø No pending bets for ${game.homeTeam} vs ${game.awayTeam}`);
- continue;
- }
-
- console.log(`\nš Settling ${pendingBets.length} bets for: ${game.homeTeam} vs ${game.awayTeam}`);
- console.log(` Winner: ${game.winner === 'home' ? game.homeTeam : game.awayTeam}`);
-
- let gameWon = 0;
- let gameLost = 0;
- let gamePayout = 0;
-
- // Process each bet
- for (const bet of pendingBets) {
- try {
- const didWin = bet.predictedWinner === game.winner;
-
- if (didWin) {
- // Settle as won
- await bet.settleAsWon();
-
- // Add winnings to user's account
- const user = await User.findById(bet.userId);
- if (user) {
- user.biscuits += bet.actualWin;
- user.winningBets = (user.winningBets || 0) + 1;
- user.totalBiscuitsWon = (user.totalBiscuitsWon || 0) + bet.actualWin;
- await user.save();
-
- gamePayout += bet.actualWin;
- gameWon++;
- }
- } else {
- // Settle as lost
- await bet.settleAsLost();
-
- // Update user stats
- const user = await User.findById(bet.userId);
- if (user) {
- user.losingBets = (user.losingBets || 0) + 1;
- user.totalBiscuitsLost = (user.totalBiscuitsLost || 0) + bet.betAmount;
- await user.save();
- }
-
- gameLost++;
- }
- } catch (betError) {
- console.error(` ā ļø Error settling bet ${bet._id}:`, betError.message);
- }
- }
-
- totalGamesSettled++;
- totalBetsSettled += pendingBets.length;
- totalWon += gameWon;
- totalLost += gameLost;
- totalPayout += gamePayout;
-
- gameResults.push({
- gameId: game._id,
- homeTeam: game.homeTeam,
- awayTeam: game.awayTeam,
- winner: game.winner,
- betsSettled: pendingBets.length,
- won: gameWon,
- lost: gameLost,
- payout: gamePayout,
- });
-
- console.log(` ā
Settled: ${gameWon} won, ${gameLost} lost, ${gamePayout} biscuits paid`);
- } catch (gameError) {
- console.error(`Error processing game ${game._id}:`, gameError.message);
- }
- }
-
- console.log(`\nš SETTLEMENT SUMMARY:`);
- console.log(` Games processed: ${totalGamesSettled}`);
- console.log(` Bets settled: ${totalBetsSettled}`);
- console.log(` Won: ${totalWon}, Lost: ${totalLost}`);
- console.log(` Total payout: ${totalPayout} biscuits`);
-
- res.status(200).json({
- success: true,
- message: 'All completed games settled',
- gamesProcessed: totalGamesSettled,
- betsSettled: totalBetsSettled,
- won: totalWon,
- lost: totalLost,
- totalPayout,
- games: gameResults,
- });
- } catch (error) {
- console.error('Error settling all games:', error);
- res.status(500).json({
- success: false,
- error: error.message,
- });
- }
-}
diff --git a/src/pages/api/bets/settle-game.js b/src/pages/api/bets/settle-game.js
deleted file mode 100644
index ad10cfef2..000000000
--- a/src/pages/api/bets/settle-game.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Settle Bets for a Completed Game
- * Processes all pending bets for a game and updates user balances
- */
-
-import connectDB from '@server/db';
-import Game from '@server/models/Game';
-import Bet from '@server/models/Bet';
-import User from '@server/models/User';
-import { getAuth } from '@clerk/nextjs/server';
-
-export default async function handler(req, res) {
- if (req.method !== 'POST') {
- return res.status(405).json({ error: 'Method not allowed' });
- }
-
- try {
- // Only allow authenticated admins (you can add admin check here)
- const { userId } = getAuth(req);
- if (!userId) {
- return res.status(401).json({ error: 'Unauthorized' });
- }
-
- await connectDB();
-
- const { gameId } = req.body;
-
- if (!gameId) {
- return res.status(400).json({ error: 'Game ID is required' });
- }
-
- // Find the game
- const game = await Game.findById(gameId);
- if (!game) {
- return res.status(404).json({ error: 'Game not found' });
- }
-
- // Verify game is completed
- if (game.status !== 'completed') {
- return res.status(400).json({
- error: 'Game is not completed yet',
- gameStatus: game.status
- });
- }
-
- // Verify winner is set
- if (!game.winner || game.winner === 'tie') {
- return res.status(400).json({
- error: game.winner === 'tie'
- ? 'Tie games require manual settlement'
- : 'Winner not set for this game'
- });
- }
-
- // Get all pending bets for this game
- const pendingBets = await Bet.find({
- gameId: game._id,
- status: 'pending'
- }).populate('userId');
-
- if (pendingBets.length === 0) {
- return res.status(200).json({
- success: true,
- message: 'No pending bets to settle',
- settled: 0,
- });
- }
-
- console.log(`šÆ Settling ${pendingBets.length} bets for game: ${game.homeTeam} vs ${game.awayTeam}`);
- console.log(`Winner: ${game.winner === 'home' ? game.homeTeam : game.awayTeam}`);
-
- let wonCount = 0;
- let lostCount = 0;
- let totalPayout = 0;
- const errors = [];
-
- // Process each bet
- for (const bet of pendingBets) {
- try {
- const didWin = bet.predictedWinner === game.winner;
-
- if (didWin) {
- // Settle as won
- await bet.settleAsWon();
-
- // Add winnings to user's account
- const user = await User.findById(bet.userId);
- if (user) {
- user.biscuits += bet.actualWin;
- user.winningBets = (user.winningBets || 0) + 1;
- user.totalBiscuitsWon = (user.totalBiscuitsWon || 0) + bet.actualWin;
- await user.save();
-
- totalPayout += bet.actualWin;
- wonCount++;
-
- console.log(`ā
Paid out ${bet.actualWin} biscuits to user ${user.clerkId}`);
- }
- } else {
- // Settle as lost
- await bet.settleAsLost();
-
- // Update user stats
- const user = await User.findById(bet.userId);
- if (user) {
- user.losingBets = (user.losingBets || 0) + 1;
- user.totalBiscuitsLost = (user.totalBiscuitsLost || 0) + bet.betAmount;
- await user.save();
- }
-
- lostCount++;
- console.log(`ā Bet lost: ${bet.betAmount} biscuits`);
- }
- } catch (error) {
- console.error(`Error settling bet ${bet._id}:`, error);
- errors.push({
- betId: bet._id,
- error: error.message,
- });
- }
- }
-
- console.log(`š Settlement complete: ${wonCount} won, ${lostCount} lost, ${totalPayout} biscuits paid out`);
-
- res.status(200).json({
- success: true,
- message: 'Bets settled successfully',
- game: {
- id: game._id,
- homeTeam: game.homeTeam,
- awayTeam: game.awayTeam,
- winner: game.winner,
- },
- settled: pendingBets.length,
- won: wonCount,
- lost: lostCount,
- totalPayout,
- errors: errors.length > 0 ? errors : undefined,
- });
- } catch (error) {
- console.error('Error settling bets:', error);
- res.status(500).json({
- success: false,
- error: error.message,
- });
- }
-}
diff --git a/src/pages/api/games/sync-from-espn.js b/src/pages/api/games/sync-from-espn.js
index 89aac1315..1c76e4c29 100644
--- a/src/pages/api/games/sync-from-espn.js
+++ b/src/pages/api/games/sync-from-espn.js
@@ -86,7 +86,6 @@ export default async function handler(req, res) {
if (game) {
// Update existing game
- const wasCompleted = game.status === 'completed';
const isNowCompleted = gameData.status === 'completed';
// Update fields
diff --git a/src/server/db.js b/src/server/db.js
index 11779257c..dbaf3bfc9 100644
--- a/src/server/db.js
+++ b/src/server/db.js
@@ -27,7 +27,7 @@ async function connectDB() {
// IMPORTANT: Even with cached connection, ensure models are registered
// This handles cases where the serverless function instance is reused
// but models aren't in memory
- registerModels();
+ await registerModels();
return cached.conn;
}
@@ -38,11 +38,11 @@ async function connectDB() {
};
console.log('š Connecting to MongoDB...');
- cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
+ cached.promise = mongoose.connect(MONGODB_URI, opts).then(async (mongoose) => {
console.log('ā
MongoDB connected successfully');
// Register all models immediately after connection
- registerModels();
+ await registerModels();
return mongoose;
});
diff --git a/src/server/models/index.js b/src/server/models/index.js
index 2a6aec886..6d272d838 100644
--- a/src/server/models/index.js
+++ b/src/server/models/index.js
@@ -17,22 +17,23 @@ let modelsRegistered = false;
* Register all Mongoose models
* This function should be called after MongoDB connection is established
*/
-export function registerModels() {
+export async function registerModels() {
// Only register once per serverless function instance
- if (modelsRegistered) {
+ // We also check mongoose.models to ensure they persist across hot reloads
+ if (modelsRegistered && mongoose.models.User && mongoose.models.Game && mongoose.models.Bet) {
return;
}
try {
- // Import models in dependency order
+ // Import models in dependency order using dynamic imports
// User has no dependencies
- require('./User');
+ await import('./User');
// Game has no dependencies
- require('./Game');
+ await import('./Game');
// Bet depends on User and Game (via references)
- require('./Bet');
+ await import('./Bet');
modelsRegistered = true;
console.log('ā
All Mongoose models registered successfully');
diff --git a/src/server/services/StatisticsService.js b/src/server/services/StatisticsService.js
index 5873819fb..196b72daa 100644
--- a/src/server/services/StatisticsService.js
+++ b/src/server/services/StatisticsService.js
@@ -1,5 +1,4 @@
import User from '@server/models/User';
-import Game from '@server/models/Game';
import Bet from '@server/models/Bet';
import { calculateWinRate, calculateROI } from '@shared/utils/stats-utils';
@@ -218,19 +217,6 @@ class StatisticsService {
const user = await User.findOne({ clerkId: userId });
if (!user) throw new Error('User not found');
- // Determine sort order
- let sortField;
- switch (sortBy) {
- case 'biscuits':
- sortField = { biscuits: -1 };
- break;
- case 'totalBets':
- sortField = { totalBets: -1, biscuits: -1 };
- break;
- default:
- sortField = { biscuits: -1 };
- }
-
// Count how many users are ranked higher
const query = {
isActive: true,