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"