Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions apps/web/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,16 @@ const nextConfig = {
fs: false,
net: false,
tls: false,
// MetaMask SDK optional React Native dep
'@react-native-async-storage/async-storage': false,
};

// Suppress optional peer dependency warnings from WalletConnect/pino
config.externals = config.externals || []
if (Array.isArray(config.externals)) {
config.externals.push({ 'pino-pretty': 'pino-pretty' })
}

// Production optimizations
if (!dev && !isServer) {
// Split chunks more aggressively
Expand Down Expand Up @@ -197,8 +205,13 @@ const nextConfig = {
};

// Bundle analyzer - only in analyze mode
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
// Conditional: only wrap when ANALYZE=true to avoid top-level-await overhead
let exportedConfig = nextConfig

if (process.env.ANALYZE === 'true') {
const { default: BundleAnalyzer } = await import('@next/bundle-analyzer')
const withBundleAnalyzer = BundleAnalyzer({ enabled: true })
exportedConfig = withBundleAnalyzer(nextConfig)
}

export default withBundleAnalyzer(nextConfig);
export default exportedConfig
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"date-fns": "^4.1.0",
"ethers": "^5",
"framer-motion": "^11.0.0",
"lucide-react": "^0.312.0",
"next": "^14.1.0",
Expand Down
31 changes: 25 additions & 6 deletions apps/web/src/app/api/analytics/leaderboard/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'

const prisma = new PrismaClient()
import { prisma } from '@/lib/prisma'
import { cache, CacheTTL } from '@/lib/cache'

export const dynamic = 'force-dynamic'

Expand All @@ -22,6 +21,18 @@ export async function GET(request: Request) {
const sortBy = (searchParams.get('sortBy') || 'profit') as 'wagered' | 'winRate' | 'profit'
const timeframe = (searchParams.get('timeframe') || 'all') as 'all' | '30d' | '7d' | '24h'

// Check in-memory cache (leaderboard updates at most every 2 minutes)
const cacheKey = `leaderboard:${sortBy}:${timeframe}:${limit}`
const cached = cache.get(cacheKey, CacheTTL.MEDIUM)
if (cached) {
return NextResponse.json(cached, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120',
'X-Cache': 'HIT',
},
})
}

// Calculate timeframe cutoff
const timeframeCutoff = getTimeframeCutoff(timeframe)

Expand Down Expand Up @@ -118,20 +129,28 @@ export async function GET(request: Request) {
}
})

return NextResponse.json({
const responseData = {
leaderboard,
sortBy,
timeframe,
timestamp: new Date().toISOString(),
}

// Cache for 2 minutes (leaderboard is expensive to compute)
cache.set(cacheKey, responseData)

return NextResponse.json(responseData, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120',
'X-Cache': 'MISS',
},
})
} catch (error) {
console.error('Leaderboard API error:', error)
return NextResponse.json(
{ error: 'Failed to fetch leaderboard' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
10 changes: 2 additions & 8 deletions apps/web/src/app/api/analytics/stats/route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'
import { cache, CacheTTL } from '@/lib/cache'
import { prisma } from '@/lib/prisma'

const prisma = new PrismaClient()
import { cache, CacheTTL } from '@/lib/cache'

// Mark as dynamic due to search params
export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'

// Revalidate every 60 seconds (stats change less frequently)
export const revalidate = 60

/**
* GET /api/analytics/stats
*
Expand Down Expand Up @@ -194,8 +190,6 @@ export async function GET(request: Request) {
{ error: 'Failed to fetch stats' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
6 changes: 1 addition & 5 deletions apps/web/src/app/api/badges/[userId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'

const prisma = new PrismaClient()
import { prisma } from '@/lib/prisma'

export const dynamic = 'force-dynamic'

Expand Down Expand Up @@ -38,7 +36,5 @@ export async function GET(
{ error: 'Failed to fetch user badges' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}
6 changes: 1 addition & 5 deletions apps/web/src/app/api/badges/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'

const prisma = new PrismaClient()
import { prisma } from '@/lib/prisma'

export const dynamic = 'force-dynamic'

Expand All @@ -25,7 +23,5 @@ export async function GET() {
{ error: 'Failed to fetch badges' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}
9 changes: 2 additions & 7 deletions apps/web/src/app/api/betting/platform-stats/route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'
import { cache, CacheTTL } from '@/lib/cache'
import { prisma } from '@/lib/prisma'

const prisma = new PrismaClient()
import { cache, CacheTTL } from '@/lib/cache'

// Mark as dynamic due to search params
export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'
// Revalidate every 60 seconds
export const revalidate = 60

/**
* GET /api/betting/platform-stats
Expand Down Expand Up @@ -144,8 +141,6 @@ export async function GET(request: Request) {
{ error: 'Failed to fetch platform stats' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
9 changes: 2 additions & 7 deletions apps/web/src/app/api/betting/recent/route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'
import { cache, CacheTTL } from '@/lib/cache'
import { prisma } from '@/lib/prisma'

const prisma = new PrismaClient()
import { cache, CacheTTL } from '@/lib/cache'

// Explicitly mark as dynamic (uses request.url for search params)
export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'
// Revalidate every 30 seconds
export const revalidate = 30

/**
* GET /api/betting/recent
Expand Down Expand Up @@ -109,8 +106,6 @@ export async function GET(request: Request) {
{ error: 'Failed to fetch recent bets' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
7 changes: 2 additions & 5 deletions apps/web/src/app/api/betting/trending/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'
import { cache, CacheTTL } from '@/lib/cache'
import { prisma } from '@/lib/prisma'

const prisma = new PrismaClient()
import { cache, CacheTTL } from '@/lib/cache'

// Revalidate every 30 seconds (trending data changes frequently)
export const revalidate = 30
Expand Down Expand Up @@ -138,7 +137,5 @@ export async function GET(request: Request) {
{ error: 'Failed to fetch trending data' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}
6 changes: 1 addition & 5 deletions apps/web/src/app/api/share/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'

const prisma = new PrismaClient()
import { prisma } from '@/lib/prisma'

export const dynamic = 'force-dynamic'

Expand Down Expand Up @@ -145,8 +143,6 @@ export async function POST(request: Request) {
{ error: 'Failed to generate share link' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
6 changes: 1 addition & 5 deletions apps/web/src/app/api/users/[walletAddress]/bets/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'

const prisma = new PrismaClient()
import { prisma } from '@/lib/prisma'

export const dynamic = 'force-dynamic'

Expand Down Expand Up @@ -258,8 +256,6 @@ export async function GET(
{ error: 'Failed to fetch user bets' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@voidborne/database'

const prisma = new PrismaClient()
import { prisma } from '@/lib/prisma'

export const dynamic = 'force-dynamic'

Expand Down Expand Up @@ -194,8 +192,6 @@ export async function GET(
{ error: 'Failed to fetch performance data' },
{ status: 500 }
)
} finally {
await prisma.$disconnect()
}
}

Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/components/effects/DustParticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function DustParticles() {
xOffset: string
color: string
size: string
opacity: number
}>>([])

useEffect(() => {
Expand All @@ -22,6 +23,8 @@ export function DustParticles() {
xOffset: `${Math.random() * 100 - 50}px`,
color: Math.random() > 0.5 ? 'hsl(40, 80%, 60%)' : 'hsl(170, 100%, 45%)',
size: `${Math.random() * 2 + 1}px`,
// Stable opacity: generated once, not on every render
opacity: Math.random() * 0.4 + 0.1,
}))
setParticles(newParticles)
}, [])
Expand All @@ -41,7 +44,7 @@ export function DustParticles() {
backgroundColor: particle.color,
width: particle.size,
height: particle.size,
opacity: Math.random() * 0.4 + 0.1,
opacity: particle.opacity,
zIndex: 1,
} as React.CSSProperties}
/>
Expand Down
7 changes: 4 additions & 3 deletions apps/web/src/components/effects/Starfield.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function Starfield() {
speedX: Math.random() * 0.1 - 0.05,
}))

let frame = 0
let animationId: number

// Animation loop
const animate = () => {
Expand Down Expand Up @@ -109,13 +109,14 @@ export function Starfield() {
}
})

frame++
requestAnimationFrame(animate)
animationId = requestAnimationFrame(animate)
}

animate()

return () => {
// Cancel animation frame to prevent memory leak on unmount
cancelAnimationFrame(animationId)
window.removeEventListener('resize', resize)
}
}, [])
Expand Down
Loading