Initial commit - PromptTech

This commit is contained in:
2026-01-27 18:07:00 -06:00
commit 3959a223bf
262 changed files with 128736 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
# ✅ 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