fix auth
Browse files- app/auth/controls.py +48 -21
- app/itineraries/router.py +50 -35
app/auth/controls.py
CHANGED
|
@@ -78,14 +78,12 @@ async def login_control(access_token: str, db: AsyncSession) -> dict:
|
|
| 78 |
detail="Email not provided by Google"
|
| 79 |
)
|
| 80 |
|
| 81 |
-
# Check if user exists
|
| 82 |
result = await db.execute(
|
| 83 |
text("""
|
| 84 |
-
SELECT id, full_name, avatar_url, role
|
| 85 |
-
FROM profiles
|
| 86 |
-
WHERE
|
| 87 |
-
SELECT id FROM auth.users WHERE email = :email
|
| 88 |
-
)
|
| 89 |
"""),
|
| 90 |
{"email": email}
|
| 91 |
)
|
|
@@ -106,28 +104,57 @@ async def login_control(access_token: str, db: AsyncSession) -> dict:
|
|
| 106 |
)
|
| 107 |
await db.commit()
|
| 108 |
else:
|
| 109 |
-
# Create new user
|
| 110 |
-
|
| 111 |
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
-
# Create profile
|
| 124 |
await db.execute(
|
| 125 |
text("""
|
| 126 |
-
INSERT INTO profiles (id, full_name, avatar_url, role, locale, created_at, updated_at)
|
| 127 |
-
VALUES (:id, :full_name, :avatar_url, 'tourist', 'vi_VN', NOW(), NOW())
|
|
|
|
|
|
|
| 128 |
"""),
|
| 129 |
{
|
| 130 |
"id": user_id,
|
|
|
|
| 131 |
"full_name": full_name,
|
| 132 |
"avatar_url": avatar_url
|
| 133 |
}
|
|
|
|
| 78 |
detail="Email not provided by Google"
|
| 79 |
)
|
| 80 |
|
| 81 |
+
# Check if user exists by email in profiles table
|
| 82 |
result = await db.execute(
|
| 83 |
text("""
|
| 84 |
+
SELECT p.id, p.full_name, p.avatar_url, p.role, p.email
|
| 85 |
+
FROM profiles p
|
| 86 |
+
WHERE p.email = :email
|
|
|
|
|
|
|
| 87 |
"""),
|
| 88 |
{"email": email}
|
| 89 |
)
|
|
|
|
| 104 |
)
|
| 105 |
await db.commit()
|
| 106 |
else:
|
| 107 |
+
# Create new user using Supabase Admin API
|
| 108 |
+
from app.shared.integrations.supabase_client import supabase
|
| 109 |
|
| 110 |
+
try:
|
| 111 |
+
# Create user in auth.users using Supabase Admin API
|
| 112 |
+
auth_response = supabase.auth.admin.create_user({
|
| 113 |
+
"email": email,
|
| 114 |
+
"email_confirm": True, # Auto-confirm email for OAuth users
|
| 115 |
+
"user_metadata": {
|
| 116 |
+
"full_name": full_name,
|
| 117 |
+
"avatar_url": avatar_url,
|
| 118 |
+
"provider": "google"
|
| 119 |
+
}
|
| 120 |
+
})
|
| 121 |
+
|
| 122 |
+
user_id = auth_response.user.id
|
| 123 |
+
|
| 124 |
+
except Exception as e:
|
| 125 |
+
# If user already exists in auth.users, try to get their ID
|
| 126 |
+
try:
|
| 127 |
+
# Query auth.users to get existing user
|
| 128 |
+
auth_result = await db.execute(
|
| 129 |
+
text("SELECT id FROM auth.users WHERE email = :email"),
|
| 130 |
+
{"email": email}
|
| 131 |
+
)
|
| 132 |
+
auth_row = auth_result.fetchone()
|
| 133 |
+
|
| 134 |
+
if auth_row:
|
| 135 |
+
user_id = str(auth_row.id)
|
| 136 |
+
else:
|
| 137 |
+
raise HTTPException(
|
| 138 |
+
status_code=500,
|
| 139 |
+
detail=f"Failed to create or retrieve user: {str(e)}"
|
| 140 |
+
)
|
| 141 |
+
except Exception as inner_e:
|
| 142 |
+
raise HTTPException(
|
| 143 |
+
status_code=500,
|
| 144 |
+
detail=f"Failed to create user: {str(e)}, {str(inner_e)}"
|
| 145 |
+
)
|
| 146 |
|
| 147 |
+
# Create profile in profiles table
|
| 148 |
await db.execute(
|
| 149 |
text("""
|
| 150 |
+
INSERT INTO profiles (id, email, full_name, avatar_url, role, locale, created_at, updated_at)
|
| 151 |
+
VALUES (:id, :email, :full_name, :avatar_url, 'tourist', 'vi_VN', NOW(), NOW())
|
| 152 |
+
ON CONFLICT (id) DO UPDATE
|
| 153 |
+
SET email = :email, full_name = :full_name, avatar_url = :avatar_url, updated_at = NOW()
|
| 154 |
"""),
|
| 155 |
{
|
| 156 |
"id": user_id,
|
| 157 |
+
"email": email,
|
| 158 |
"full_name": full_name,
|
| 159 |
"avatar_url": avatar_url
|
| 160 |
}
|
app/itineraries/router.py
CHANGED
|
@@ -336,42 +336,57 @@ async def add_stop(
|
|
| 336 |
# Get place snapshot - prefer from request, otherwise from DB
|
| 337 |
snapshot = request.snapshot
|
| 338 |
if not snapshot:
|
| 339 |
-
# Try to fetch from database
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
|
| 376 |
stop = Stop(
|
| 377 |
id=str(row.id),
|
|
|
|
| 336 |
# Get place snapshot - prefer from request, otherwise from DB
|
| 337 |
snapshot = request.snapshot
|
| 338 |
if not snapshot:
|
| 339 |
+
# Try to fetch from database (optional - don't fail if not found)
|
| 340 |
+
try:
|
| 341 |
+
place_result = await db.execute(
|
| 342 |
+
text("SELECT name, category, address, rating FROM places_metadata WHERE place_id = :place_id"),
|
| 343 |
+
{"place_id": request.place_id}
|
| 344 |
+
)
|
| 345 |
+
place_row = place_result.fetchone()
|
| 346 |
+
if place_row:
|
| 347 |
+
snapshot = {
|
| 348 |
+
"name": place_row.name,
|
| 349 |
+
"category": place_row.category,
|
| 350 |
+
"address": place_row.address,
|
| 351 |
+
"rating": float(place_row.rating) if place_row.rating else None,
|
| 352 |
+
}
|
| 353 |
+
except Exception as e:
|
| 354 |
+
# Log but don't fail - snapshot is optional
|
| 355 |
+
print(f"Warning: Could not fetch place metadata for {request.place_id}: {e}")
|
| 356 |
|
| 357 |
+
try:
|
| 358 |
+
# Insert stop
|
| 359 |
+
result = await db.execute(
|
| 360 |
+
text("""
|
| 361 |
+
INSERT INTO itinerary_stops (itinerary_id, day_index, order_index, place_id, arrival_time, stay_minutes, notes, tags, snapshot)
|
| 362 |
+
VALUES (:itinerary_id, :day_index, :order_index, :place_id, :arrival_time, :stay_minutes, :notes, :tags, :snapshot)
|
| 363 |
+
RETURNING id, itinerary_id, day_index, order_index, place_id, arrival_time, stay_minutes, notes, tags, snapshot, created_at, updated_at
|
| 364 |
+
"""),
|
| 365 |
+
{
|
| 366 |
+
"itinerary_id": itinerary_id,
|
| 367 |
+
"day_index": request.day_index,
|
| 368 |
+
"order_index": request.order_index,
|
| 369 |
+
"place_id": request.place_id,
|
| 370 |
+
"arrival_time": request.arrival_time,
|
| 371 |
+
"stay_minutes": request.stay_minutes,
|
| 372 |
+
"notes": request.notes,
|
| 373 |
+
"tags": request.tags,
|
| 374 |
+
"snapshot": snapshot,
|
| 375 |
+
}
|
| 376 |
+
)
|
| 377 |
+
await db.commit()
|
| 378 |
+
row = result.fetchone()
|
| 379 |
+
except Exception as e:
|
| 380 |
+
# Rollback and provide detailed error
|
| 381 |
+
await db.rollback()
|
| 382 |
+
error_msg = str(e)
|
| 383 |
+
print(f"❌ Database error adding stop: {error_msg}")
|
| 384 |
+
print(f" place_id: {request.place_id}")
|
| 385 |
+
print(f" snapshot: {snapshot}")
|
| 386 |
+
raise HTTPException(
|
| 387 |
+
status_code=500,
|
| 388 |
+
detail=f"Database error: {error_msg}"
|
| 389 |
+
)
|
| 390 |
|
| 391 |
stop = Stop(
|
| 392 |
id=str(row.id),
|