520 lines
15 KiB
Markdown
520 lines
15 KiB
Markdown
# Jinja2 Migration Progress - Admin Panel
|
|
|
|
**Date:** October 20, 2025
|
|
**Project:** Multi-Tenant E-commerce Platform
|
|
**Goal:** Migrate from static HTML files to Jinja2 server-rendered templates
|
|
|
|
---
|
|
|
|
## 🎯 Current Status: DEBUGGING AUTH LOOP
|
|
|
|
We successfully set up the Jinja2 infrastructure but are experiencing authentication redirect loops. We're in the process of simplifying the auth flow to resolve this.
|
|
|
|
---
|
|
|
|
## ✅ What's Been Completed
|
|
|
|
### 1. Infrastructure Setup ✅
|
|
|
|
- [x] Added Jinja2Templates to `main.py`
|
|
- [x] Created `app/templates/` directory structure
|
|
- [x] Created `app/api/v1/admin/pages.py` for HTML routes
|
|
- [x] Integrated pages router into the main app
|
|
|
|
**Files Created:**
|
|
```
|
|
app/
|
|
├── templates/
|
|
│ ├── admin/
|
|
│ │ ├── base.html ✅ Created
|
|
│ │ ├── login.html ✅ Created
|
|
│ │ └── dashboard.html ✅ Created
|
|
│ └── partials/
|
|
│ ├── header.html ✅ Moved from static
|
|
│ └── sidebar.html ✅ Moved from static
|
|
└── api/
|
|
└── v1/
|
|
└── admin/
|
|
└── pages.py ✅ Created
|
|
```
|
|
|
|
### 2. Route Configuration ✅
|
|
|
|
**New Jinja2 Routes (working):**
|
|
- `/admin/` → redirects to `/admin/dashboard`
|
|
- `/admin/login` → login page (no auth)
|
|
- `/admin/dashboard` → dashboard page (requires auth)
|
|
- `/admin/vendors` → vendors page (requires auth)
|
|
- `/admin/users` → users page (requires auth)
|
|
|
|
**Old Static Routes (disabled):**
|
|
- Commented out admin routes in `app/routes/frontend.py`
|
|
- Old `/static/admin/*.html` routes no longer active
|
|
|
|
### 3. Exception Handler Updates ✅
|
|
|
|
- [x] Updated `app/exceptions/handler.py` to redirect HTML requests on 401
|
|
- [x] Added `_is_html_page_request()` helper function
|
|
- [x] Server-side redirects working for unauthenticated page access
|
|
|
|
### 4. JavaScript Updates ✅
|
|
|
|
Updated all JavaScript files to use new routes:
|
|
|
|
**Files Updated:**
|
|
- `static/admin/js/dashboard.js` - viewVendor() uses `/admin/vendors`
|
|
- `static/admin/js/login.js` - redirects to `/admin/dashboard`
|
|
- `static/admin/js/vendors.js` - auth checks use `/admin/login`
|
|
- `static/admin/js/vendor-edit.js` - all redirects updated
|
|
- `static/shared/js/api-client.js` - handleUnauthorized() uses `/admin/login`
|
|
|
|
### 5. Template Structure ✅
|
|
|
|
**Base Template (`app/templates/admin/base.html`):**
|
|
- Server-side includes for header and sidebar (no more AJAX loading!)
|
|
- Proper script loading order
|
|
- Alpine.js integration
|
|
- No more `partial-loader.js`
|
|
|
|
**Dashboard Template (`app/templates/admin/dashboard.html`):**
|
|
- Extends base template
|
|
- Uses Alpine.js `adminDashboard()` component
|
|
- Stats cards and recent vendors table
|
|
|
|
**Login Template (`app/templates/admin/login.html`):**
|
|
- Standalone page (doesn't extend base)
|
|
- Uses Alpine.js `adminLogin()` component
|
|
|
|
---
|
|
|
|
## ❌ Current Problem: Authentication Loop
|
|
|
|
### Issue Description
|
|
|
|
Getting infinite redirect loops in various scenarios:
|
|
1. After login → redirects back to login
|
|
2. On login page → continuous API calls to `/admin/auth/me`
|
|
3. Dashboard → redirects to login → redirects to dashboard
|
|
|
|
### Root Causes Identified
|
|
|
|
1. **Multiple redirect handlers fighting:**
|
|
- Server-side: `handler.py` redirects on 401 for HTML pages
|
|
- Client-side: `api-client.js` also redirects on 401
|
|
- Both triggering simultaneously
|
|
|
|
2. **Login page checking auth on init:**
|
|
- Calls `/admin/auth/me` on page load
|
|
- Gets 401 → triggers redirect
|
|
- Creates loop
|
|
|
|
3. **Token not being sent properly:**
|
|
- Token stored but API calls not including it
|
|
- Gets 401 even with valid token
|
|
|
|
### Latest Approach (In Progress)
|
|
|
|
Simplifying to minimal working version:
|
|
- Login page does NOTHING on init (no auth checking)
|
|
- API client does NOT redirect (just throws errors)
|
|
- Server ONLY redirects browser HTML requests (not API calls)
|
|
- One source of truth for auth handling
|
|
|
|
---
|
|
|
|
## 📝 Files Modified (Complete List)
|
|
|
|
### Backend Files
|
|
|
|
1. **`main.py`**
|
|
```python
|
|
# Added:
|
|
- Jinja2Templates import and configuration
|
|
- admin_pages router include at /admin prefix
|
|
```
|
|
|
|
2. **`app/api/main.py`** (unchanged - just includes v1 routes)
|
|
|
|
3. **`app/api/v1/admin/__init__.py`**
|
|
```python
|
|
# Added:
|
|
- import pages
|
|
- router.include_router(pages.router, tags=["admin-pages"])
|
|
```
|
|
|
|
4. **`app/api/v1/admin/pages.py`** (NEW FILE)
|
|
```python
|
|
# Contains:
|
|
- @router.get("/") - root redirect
|
|
- @router.get("/login") - login page
|
|
- @router.get("/dashboard") - dashboard page
|
|
- @router.get("/vendors") - vendors page
|
|
- @router.get("/users") - users page
|
|
```
|
|
|
|
5. **`app/routes/frontend.py`**
|
|
```python
|
|
# Changed:
|
|
- Commented out all /admin/ routes
|
|
- Left vendor and shop routes active
|
|
```
|
|
|
|
6. **`app/exceptions/handler.py`**
|
|
```python
|
|
# Added:
|
|
- 401 redirect logic for HTML pages
|
|
- _is_html_page_request() helper
|
|
# Status: Needs simplification
|
|
```
|
|
|
|
### Frontend Files
|
|
|
|
1. **`static/admin/js/login.js`**
|
|
```javascript
|
|
// Changed:
|
|
- Removed /static/admin/ paths
|
|
- Updated to /admin/ paths
|
|
- checkExistingAuth() logic
|
|
# Status: Needs simplification
|
|
```
|
|
|
|
2. **`static/admin/js/dashboard.js`**
|
|
```javascript
|
|
// Changed:
|
|
- viewVendor() uses /admin/vendors
|
|
# Status: Working
|
|
```
|
|
|
|
3. **`static/admin/js/vendors.js`**
|
|
```javascript
|
|
// Changed:
|
|
- checkAuth() redirects to /admin/login
|
|
- handleLogout() redirects to /admin/login
|
|
# Status: Not tested yet
|
|
```
|
|
|
|
4. **`static/admin/js/vendor-edit.js`**
|
|
```javascript
|
|
// Changed:
|
|
- All /static/admin/ paths to /admin/
|
|
# Status: Not tested yet
|
|
```
|
|
|
|
5. **`static/shared/js/api-client.js`**
|
|
```javascript
|
|
// Changed:
|
|
- handleUnauthorized() uses /admin/login
|
|
# Status: Needs simplification - causing loops
|
|
```
|
|
|
|
6. **`static/shared/js/utils.js`** (unchanged - working fine)
|
|
|
|
### Template Files (NEW)
|
|
|
|
1. **`app/templates/admin/base.html`** ✅
|
|
- Master layout with sidebar and header
|
|
- Script loading in correct order
|
|
- No partial-loader.js
|
|
|
|
2. **`app/templates/admin/login.html`** ✅
|
|
- Standalone login page
|
|
- Alpine.js adminLogin() component
|
|
|
|
3. **`app/templates/admin/dashboard.html`** ✅
|
|
- Extends base.html
|
|
- Alpine.js adminDashboard() component
|
|
|
|
4. **`app/templates/partials/header.html`** ✅
|
|
- Top navigation bar
|
|
- Updated logout link to /admin/login
|
|
|
|
5. **`app/templates/partials/sidebar.html`** ✅
|
|
- Side navigation menu
|
|
- Updated all links to /admin/* paths
|
|
|
|
---
|
|
|
|
## 🔧 Next Steps (Tomorrow)
|
|
|
|
### Immediate Priority: Fix Auth Loop
|
|
|
|
Apply the simplified approach from the last message:
|
|
|
|
1. **Simplify `login.js`:**
|
|
```javascript
|
|
// Remove all auth checking on init
|
|
// Just show login form
|
|
// Only redirect after successful login
|
|
```
|
|
|
|
2. **Simplify `api-client.js`:**
|
|
```javascript
|
|
// Remove handleUnauthorized() redirect logic
|
|
// Just throw errors, don't redirect
|
|
// Let server handle redirects
|
|
```
|
|
|
|
3. **Simplify `handler.py`:**
|
|
```javascript
|
|
// Only redirect browser HTML requests (text/html accept header)
|
|
// Don't redirect API calls (application/json)
|
|
// Don't redirect if already on login page
|
|
```
|
|
|
|
**Test Flow:**
|
|
1. Navigate to `/admin/login` → should show form (no loops)
|
|
2. Login → should redirect to `/admin/dashboard`
|
|
3. Dashboard → should load with sidebar/header
|
|
4. No console errors, no 404s for partials
|
|
|
|
### After Auth Works
|
|
|
|
1. **Create remaining page templates:**
|
|
- `app/templates/admin/vendors.html`
|
|
- `app/templates/admin/users.html`
|
|
- `app/templates/admin/vendor-edit.html`
|
|
|
|
2. **Test all admin flows:**
|
|
- Login ✓
|
|
- Dashboard ✓
|
|
- Vendors list
|
|
- Vendor create
|
|
- Vendor edit
|
|
- User management
|
|
|
|
3. **Cleanup:**
|
|
- Remove old static HTML files
|
|
- Remove `app/routes/frontend.py` admin routes completely
|
|
- Remove `partial-loader.js`
|
|
|
|
4. **Migrate vendor portal:**
|
|
- Same process for `/vendor/*` routes
|
|
- Create vendor templates
|
|
- Update vendor JavaScript files
|
|
|
|
---
|
|
|
|
## 📚 Key Learnings
|
|
|
|
### What Worked
|
|
|
|
1. ✅ **Server-side template rendering** - Clean, fast, no AJAX for partials
|
|
2. ✅ **Jinja2 integration** - Easy to set up, works with FastAPI
|
|
3. ✅ **Route separation** - HTML routes in `pages.py`, API routes separate
|
|
4. ✅ **Template inheritance** - `base.html` + `{% extends %}` pattern
|
|
|
|
### What Caused Issues
|
|
|
|
1. ❌ **Multiple redirect handlers** - Client + server both handling 401
|
|
2. ❌ **Auth checking on login page** - Created loops
|
|
3. ❌ **Complex error handling** - Too many places making decisions
|
|
4. ❌ **Path inconsistencies** - Old `/static/admin/` vs new `/admin/`
|
|
|
|
### Best Practices Identified
|
|
|
|
1. **Single source of truth for redirects** - Choose server OR client, not both
|
|
2. **Login page should be dumb** - No auth checking, just show form
|
|
3. **API client should be simple** - Fetch data, throw errors, don't redirect
|
|
4. **Server handles page-level auth** - FastAPI dependencies + exception handler
|
|
5. **Clear separation** - HTML pages vs API endpoints
|
|
|
|
---
|
|
|
|
## 🗂️ Project Structure (Current)
|
|
|
|
```
|
|
project/
|
|
├── main.py ✅ Updated
|
|
├── app/
|
|
│ ├── api/
|
|
│ │ ├── main.py ✅ Unchanged
|
|
│ │ └── v1/
|
|
│ │ └── admin/
|
|
│ │ ├── __init__.py ✅ Updated
|
|
│ │ ├── pages.py ✅ NEW
|
|
│ │ ├── auth.py ✅ Existing (API routes)
|
|
│ │ ├── vendors.py ✅ Existing (API routes)
|
|
│ │ └── dashboard.py ✅ Existing (API routes)
|
|
│ ├── routes/
|
|
│ │ └── frontend.py ⚠️ Partially disabled
|
|
│ ├── exceptions/
|
|
│ │ └── handler.py ⚠️ Needs simplification
|
|
│ └── templates/ ✅ NEW
|
|
│ ├── admin/
|
|
│ │ ├── base.html
|
|
│ │ ├── login.html
|
|
│ │ └── dashboard.html
|
|
│ └── partials/
|
|
│ ├── header.html
|
|
│ └── sidebar.html
|
|
└── static/
|
|
├── admin/
|
|
│ ├── js/
|
|
│ │ ├── login.js ⚠️ Needs simplification
|
|
│ │ ├── dashboard.js ✅ Updated
|
|
│ │ ├── vendors.js ✅ Updated
|
|
│ │ └── vendor-edit.js ✅ Updated
|
|
│ └── css/
|
|
│ └── tailwind.output.css ✅ Unchanged
|
|
└── shared/
|
|
└── js/
|
|
├── api-client.js ⚠️ Needs simplification
|
|
├── utils.js ✅ Working
|
|
└── icons.js ✅ Working
|
|
```
|
|
|
|
**Legend:**
|
|
- ✅ = Working correctly
|
|
- ⚠️ = Needs attention/debugging
|
|
- ❌ = Not working/causing issues
|
|
|
|
---
|
|
|
|
## 🐛 Debug Commands
|
|
|
|
### Clear localStorage (Browser Console)
|
|
```javascript
|
|
localStorage.clear();
|
|
```
|
|
|
|
### Check stored tokens
|
|
```javascript
|
|
console.log('admin_token:', localStorage.getItem('admin_token'));
|
|
console.log('admin_user:', localStorage.getItem('admin_user'));
|
|
```
|
|
|
|
### Test API call manually
|
|
```javascript
|
|
fetch('/api/v1/admin/auth/me', {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('admin_token')}`
|
|
}
|
|
}).then(r => r.json()).then(d => console.log(d));
|
|
```
|
|
|
|
### Check current route
|
|
```javascript
|
|
console.log('Current path:', window.location.pathname);
|
|
console.log('Full URL:', window.location.href);
|
|
```
|
|
|
|
---
|
|
|
|
## 📖 Reference: Working Code Snippets
|
|
|
|
### Minimal Login.js (To Try Tomorrow)
|
|
|
|
```javascript
|
|
function adminLogin() {
|
|
return {
|
|
dark: false,
|
|
credentials: { username: '', password: '' },
|
|
loading: false,
|
|
error: null,
|
|
success: null,
|
|
errors: {},
|
|
|
|
init() {
|
|
this.dark = localStorage.getItem('theme') === 'dark';
|
|
// NO AUTH CHECKING - just show form
|
|
},
|
|
|
|
async handleLogin() {
|
|
if (!this.validateForm()) return;
|
|
|
|
this.loading = true;
|
|
try {
|
|
const response = await fetch('/api/v1/admin/auth/login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
username: this.credentials.username,
|
|
password: this.credentials.password
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (!response.ok) throw new Error(data.message);
|
|
|
|
localStorage.setItem('admin_token', data.access_token);
|
|
localStorage.setItem('admin_user', JSON.stringify(data.user));
|
|
|
|
this.success = 'Login successful!';
|
|
setTimeout(() => window.location.href = '/admin/dashboard', 500);
|
|
} catch (error) {
|
|
this.error = error.message;
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Simplified API Client Request Method
|
|
|
|
```javascript
|
|
async request(endpoint, options = {}) {
|
|
const url = `${this.baseURL}${endpoint}`;
|
|
const config = {
|
|
...options,
|
|
headers: this.getHeaders(options.headers)
|
|
};
|
|
|
|
const response = await fetch(url, config);
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.message || 'Request failed');
|
|
}
|
|
|
|
return data;
|
|
// NO REDIRECT LOGIC HERE!
|
|
}
|
|
```
|
|
|
|
### Simplified Exception Handler
|
|
|
|
```python
|
|
if exc.status_code == 401:
|
|
accept_header = request.headers.get("accept", "")
|
|
is_browser = "text/html" in accept_header
|
|
|
|
if is_browser and not request.url.path.endswith("/login"):
|
|
if request.url.path.startswith("/admin"):
|
|
return RedirectResponse(url="/admin/login", status_code=302)
|
|
|
|
# Return JSON for API calls
|
|
return JSONResponse(status_code=exc.status_code, content=exc.to_dict())
|
|
```
|
|
|
|
---
|
|
|
|
## 💡 Questions to Answer Tomorrow
|
|
|
|
1. Does the simplified auth flow work without loops?
|
|
2. Can we successfully login and access dashboard?
|
|
3. Are tokens being sent correctly in API requests?
|
|
4. Do we need the auth check on login page at all?
|
|
5. Should we move ALL redirect logic to server-side?
|
|
|
|
---
|
|
|
|
## 🎯 Success Criteria
|
|
|
|
The migration will be considered successful when:
|
|
|
|
- [ ] Login page loads without loops
|
|
- [ ] Login succeeds and redirects to dashboard
|
|
- [ ] Dashboard displays with sidebar and header
|
|
- [ ] No 404 errors for partials
|
|
- [ ] Icons display correctly
|
|
- [ ] Stats cards load data from API
|
|
- [ ] Navigation between admin pages works
|
|
- [ ] Logout works correctly
|
|
|
|
---
|
|
|
|
**End of Session - October 20, 2025**
|
|
|
|
Good work today! We made significant progress on the infrastructure. Tomorrow we'll resolve the auth loop and complete the admin panel migration. |