diff --git a/backend/app/main.py b/backend/app/main.py
index 3710672..265212d 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -1,8 +1,13 @@
-from fastapi import FastAPI
+from fastapi import FastAPI, Depends
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
-from app.database import init_db
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy import select, func
+from decimal import Decimal
+from app.database import init_db, get_db
from app.routers import auth, users, wallet, bets, websocket, admin, sport_events, spread_bets, gamification, matches
+from app.models import User, Wallet
+from app.utils.security import get_password_hash
@asynccontextmanager
@@ -50,3 +55,64 @@ async def root():
@app.get("/health")
async def health():
return {"status": "healthy"}
+
+
+@app.get("/init")
+async def init_admin(db: AsyncSession = Depends(get_db)):
+ """
+ Initialize the application with a default admin user.
+ Only works if no admin users exist in the database.
+ Creates: admin@test.com / password123
+ """
+ # Check if any admin users exist
+ result = await db.execute(
+ select(func.count(User.id)).where(User.is_admin == True)
+ )
+ admin_count = result.scalar()
+
+ if admin_count > 0:
+ return {
+ "success": False,
+ "message": "Admin user(s) already exist. Initialization skipped.",
+ "admin_count": admin_count
+ }
+
+ # Check if user with this email already exists
+ existing = await db.execute(
+ select(User).where(User.email == "admin@test.com")
+ )
+ if existing.scalar_one_or_none():
+ return {
+ "success": False,
+ "message": "User with email admin@test.com already exists but is not an admin."
+ }
+
+ # Create admin user
+ admin_user = User(
+ email="admin@test.com",
+ username="admin",
+ password_hash=get_password_hash("password123"),
+ display_name="Administrator",
+ is_admin=True,
+ )
+ db.add(admin_user)
+ await db.flush()
+
+ # Create wallet for admin
+ wallet = Wallet(
+ user_id=admin_user.id,
+ balance=Decimal("10000.00"),
+ escrow=Decimal("0.00"),
+ )
+ db.add(wallet)
+
+ await db.commit()
+
+ return {
+ "success": True,
+ "message": "Admin user created successfully",
+ "credentials": {
+ "email": "admin@test.com",
+ "password": "password123"
+ }
+ }
diff --git a/frontend/src/components/admin/AdminDataTools.tsx b/frontend/src/components/admin/AdminDataTools.tsx
index 02073d1..acf83a7 100644
--- a/frontend/src/components/admin/AdminDataTools.tsx
+++ b/frontend/src/components/admin/AdminDataTools.tsx
@@ -44,17 +44,25 @@ export const AdminDataTools = () => {
const seedMutation = useMutation({
mutationFn: adminApi.seedDatabase,
onSuccess: (data) => {
+ console.log('Seed success:', data)
toast.success(data.message)
if (data.test_admin) {
toast.success(`Test admin created: ${data.test_admin.username} / ${data.test_admin.password}`, {
duration: 10000,
})
}
+ // Show what was created
+ const counts = data.created_counts
+ toast.success(`Created: ${counts.users} users, ${counts.events} events, ${counts.bets} bets`, {
+ duration: 5000,
+ })
queryClient.invalidateQueries()
refetchPreview()
},
onError: (error: any) => {
- toast.error(error.response?.data?.detail || 'Seed failed')
+ console.error('Seed error:', error)
+ const message = error.response?.data?.detail || error.message || 'Seed failed'
+ toast.error(message)
},
})
@@ -216,7 +224,7 @@ export const AdminDataTools = () => {
setSeedConfig({ ...seedConfig, num_users: parseInt(e.target.value) || 0 })}
+ onChange={(e) => setSeedConfig({ ...seedConfig, num_users: Math.max(1, parseInt(e.target.value) || 1) })}
min={1}
max={100}
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
@@ -228,7 +236,7 @@ export const AdminDataTools = () => {
setSeedConfig({ ...seedConfig, num_events: parseInt(e.target.value) || 0 })}
+ onChange={(e) => setSeedConfig({ ...seedConfig, num_events: Math.max(0, parseInt(e.target.value) || 0) })}
min={0}
max={50}
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
@@ -240,7 +248,7 @@ export const AdminDataTools = () => {
setSeedConfig({ ...seedConfig, num_bets_per_event: parseInt(e.target.value) || 0 })}
+ onChange={(e) => setSeedConfig({ ...seedConfig, num_bets_per_event: Math.max(0, parseInt(e.target.value) || 0) })}
min={0}
max={20}
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
@@ -252,7 +260,7 @@ export const AdminDataTools = () => {
setSeedConfig({ ...seedConfig, starting_balance: parseInt(e.target.value) || 0 })}
+ onChange={(e) => setSeedConfig({ ...seedConfig, starting_balance: parseInt(e.target.value) || 100 })}
min={100}
max={10000}
className="w-full px-3 py-2 border border-gray-300 rounded-lg"