Skip to content

Commit 6072259

Browse files
Tarteelclaude
andcommitted
feat: country + region dropdowns for signup — consistent room bucketing
- Replace free-text city/country with cascading country→region selects - 60+ countries, 400+ pre-defined regions with fixed lat/lng/timezone - Same region = identical coordinates = same Isha bucket = shared room - /regions public API endpoint for frontend dropdown data - auth.py uses pre-defined coords (no geocoding needed for known regions) - Step 2 validation: both country and city required to proceed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 74c33cb commit 6072259

File tree

7 files changed

+521
-15
lines changed

7 files changed

+521
-15
lines changed

backend/api/auth.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ async def register(body: UserRegisterFull, response: Response, db: AsyncSession
5858
if result.scalar_one_or_none():
5959
raise HTTPException(status_code=400, detail="Email already registered")
6060

61-
# Geocode city
62-
geo = await geocode_city(body.city, body.country)
63-
if not geo:
64-
raise HTTPException(status_code=400, detail=f"Could not geocode '{body.city}, {body.country}'")
65-
lat, lng, tz_name = geo
61+
# Try pre-defined region coordinates first (fast, reliable, ensures room bucketing)
62+
from utils.regions import lookup_region
63+
region_coords = lookup_region(body.country, body.city)
64+
if region_coords:
65+
lat, lng, tz_name = region_coords
66+
else:
67+
# Fall back to geocoding for any city not in our regions list
68+
geo = await geocode_city(body.city, body.country)
69+
if not geo:
70+
raise HTTPException(status_code=400, detail=f"Could not geocode '{body.city}, {body.country}'")
71+
lat, lng, tz_name = geo
6672

6773
user = User(
6874
email=body.email,

backend/api/regions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from fastapi import APIRouter
2+
from utils.regions import get_regions_map
3+
4+
router = APIRouter(prefix="/regions", tags=["regions"])
5+
6+
7+
@router.get("", response_model=dict[str, list[str]])
8+
async def list_regions():
9+
"""Public endpoint — returns {country: [city, ...]} for the registration dropdowns."""
10+
return get_regions_map()

backend/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from api.admin import router as admin_router
1616
from api.friends import router as friends_router
1717
from api.private_rooms import router as private_rooms_router
18+
from api.regions import router as regions_router
1819
from services.scheduler import start_scheduler
1920
from ws.events import sio
2021

@@ -67,6 +68,7 @@ async def lifespan(app: FastAPI):
6768
app.include_router(admin_router)
6869
app.include_router(friends_router)
6970
app.include_router(private_rooms_router)
71+
app.include_router(regions_router)
7072

7173
# Serve HLS files statically
7274
hls_dir = Path(settings.HLS_OUTPUT_DIR)

backend/schemas/user.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ class UserRegisterStep2(BaseModel):
3232
country: str
3333
calc_method: int = 3
3434

35+
@field_validator("city", "country")
36+
@classmethod
37+
def strip_required(cls, v: str) -> str:
38+
v = v.strip()
39+
if not v:
40+
raise ValueError("This field is required")
41+
return v
42+
3543

3644
class UserRegisterStep3(BaseModel):
3745
rakats: int = 8

0 commit comments

Comments
 (0)