# โœ… Admin Services Fixed - January 11, 2026 ## ๐Ÿ”ด Problem The Admin Dashboard was unable to load services, showing: ``` Access to XMLHttpRequest at 'http://localhost:8181/api/admin/services?include_inactive=true' from origin 'http://localhost:5300' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. GET http://localhost:8181/api/admin/services?include_inactive=true net::ERR_FAILED 500 (Internal Server Error) ``` ## ๐Ÿ” Root Cause The `/api/admin/services` endpoint was throwing a **500 Internal Server Error** because: - The `service_to_dict()` function tries to access `service.images` relationship - The admin endpoint query did NOT eager-load the `images` relationship - When SQLAlchemy tried to access the lazy-loaded relationship, it failed in async context - This caused the 500 error, which then prevented CORS headers from being sent ## โœ… Solution Added eager loading to the admin services endpoint query: **Before:** ```python @api_router.get("/admin/services") async def admin_get_services(include_inactive: bool = False, user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db)): query = select(Service) if not include_inactive: query = query.where(Service.is_active == True) query = query.order_by(desc(Service.created_at)) result = await db.execute(query) services = result.scalars().all() return [service_to_dict(s) for s in services] ``` **After:** ```python @api_router.get("/admin/services") async def admin_get_services(include_inactive: bool = False, user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db)): query = select(Service).options(selectinload(Service.images)) # โœ… ADDED THIS if not include_inactive: query = query.where(Service.is_active == True) query = query.order_by(desc(Service.created_at)) result = await db.execute(query) services = result.scalars().all() return [service_to_dict(s) for s in services] ``` ## ๐Ÿ“Š Verification ### Admin Services Endpoint Test ```bash $ curl http://localhost:8181/api/admin/services?include_inactive=true -H "Authorization: Bearer $TOKEN" โœ… SUCCESS! Returns all 8 services: 1. Updated Test Service ($149.99) - setup 2. Updated Repair Service ($149.99) - repair 3. Data Recovery ($199.99) - data 4. Virus Removal ($89.99) - software 5. Screen Repair ($149.99) - repair 6. Device Setup ($59.99) - setup 7. Hardware Upgrade ($49.99) - upgrade 8. Battery Replacement ($79.99) - repair ``` ### Backend Logs ``` INFO: 127.0.0.1:31132 - "GET /api/admin/services?include_inactive=true HTTP/1.1" 200 OK ``` ## ๐ŸŽฏ Status | Endpoint | Status | Response | Services | |----------|--------|----------|----------| | `/api/services` | โœ… Working | 200 OK | 8 services | | `/api/admin/services` | โœ… **NOW FIXED** | 200 OK | 8 services | ## ๐Ÿงช Testing ### Test Admin Dashboard ``` http://localhost:5300/admin ``` 1. Login with admin credentials 2. Navigate to **Services** tab 3. Services should now load correctly with all 8 services displayed ### Backend Direct Test ```bash cd /media/pts/Website/PromptTech_Solution_Site/backend source venv/bin/activate TOKEN=$(python test_upload.py 2>/dev/null | grep "eyJ" | head -1 | tr -d ' ') curl -s http://localhost:8181/api/admin/services?include_inactive=true \ -H "Authorization: Bearer $TOKEN" | jq ``` ## ๐Ÿ“ Technical Details ### The Issue with Async SQLAlchemy - SQLAlchemy async sessions cannot lazy-load relationships - When accessing `service.images` without eager loading, it tries to issue a new query - In async context, this fails because the session is not in an active transaction - Result: AttributeError or DetachedInstanceError ### The Solution - **Eager Loading**: Use `.options(selectinload(Service.images))` - This loads all images in a single additional query (efficient) - Images are available immediately when `service_to_dict()` accesses them - No lazy loading = No async errors ### Why CORS Error Appeared - The 500 error prevented the response from being sent - FastAPI's CORS middleware only adds headers to successful responses - Browser saw no CORS headers and blocked the request - User saw CORS error, but real issue was the 500 error underneath ## ๐Ÿ”ง Files Modified - **backend/server.py** (line 1363): Added `selectinload(Service.images)` to admin services query ## โœจ Comparison with Public Endpoint **Public Services Endpoint** (Already Working): ```python @api_router.get("/services") async def get_services(category: Optional[str] = None, db: AsyncSession = Depends(get_db)): query = select(Service).where(Service.is_active == True) if category and category != "all": query = query.where(Service.category == category) query = query.options(selectinload(Service.reviews).selectinload(Review.user)) # ... rest of query ``` โœ… This one already had eager loading for reviews, so it worked fine. **Admin Endpoint** (Was Missing Eager Loading): - Was not loading images relationship - Now fixed with same pattern ## ๐ŸŽ‰ Result - โœ… Admin services endpoint returns 200 OK - โœ… All 8 services load correctly - โœ… CORS headers present - โœ… Admin Dashboard services tab functional - โœ… No more 500 errors --- **Fixed By**: GitHub Copilot **Date**: January 11, 2026, 10:59 PM CST **Issue**: Admin services 500 error + CORS blocking **Resolution**: Added eager loading for Service.images relationship