created specific route files for frontends

This commit is contained in:
2025-11-02 15:26:55 +01:00
parent 9611c03a36
commit 9cc92e5fc4
12 changed files with 76 additions and 235 deletions

View File

@@ -2,7 +2,7 @@
"""
Admin API router aggregation.
This module combines all admin-related API endpoints:
This module combines all admin-related JSON API endpoints:
- Authentication (login/logout)
- Vendor management (CRUD, bulk operations)
- Vendor domains management (custom domains, DNS verification)
@@ -13,7 +13,11 @@ This module combines all admin-related API endpoints:
- Audit logging
- Platform settings
- Notifications and alerts
- HTML Pages - Server-rendered pages using Jinja2
IMPORTANT:
- This router is for JSON API endpoints only
- HTML page routes are mounted separately in main.py at /vendor/*
- Do NOT include pages.router here - it causes route conflicts
"""
from fastapi import APIRouter
@@ -30,8 +34,7 @@ from . import (
monitoring,
audit,
settings,
notifications,
pages
notifications
)
# Create admin router
@@ -100,14 +103,5 @@ router.include_router(settings.router, tags=["admin-settings"])
# Include notifications and alerts endpoints
router.include_router(notifications.router, tags=["admin-notifications"])
# ============================================================================
# HTML Page Routes (Jinja2 Templates)
# ============================================================================
# Include HTML page routes (these return rendered templates, not JSON)
router.include_router(pages.router, tags=["admin-pages"])
# Export the router
__all__ = ["router"]

View File

@@ -1,6 +1,6 @@
# app/api/v1/vendor/__init__.py
"""
Vendor API endpoints.
Vendor API router aggregation.
This module aggregates all vendor-related JSON API endpoints.
@@ -14,7 +14,7 @@ from fastapi import APIRouter
# Import all sub-routers (JSON API only)
from . import (
info, # NEW: Vendor info endpoint
info,
auth,
dashboard,
profile,
@@ -29,7 +29,6 @@ from . import (
media,
notifications,
analytics,
# NOTE: pages is NOT imported here - it's mounted separately in main.py
)
@@ -66,14 +65,4 @@ router.include_router(media.router, tags=["vendor-media"])
router.include_router(notifications.router, tags=["vendor-notifications"])
router.include_router(analytics.router, tags=["vendor-analytics"])
# ============================================================================
# NOTE: HTML Page Routes
# ============================================================================
# HTML page routes (pages.router) are NOT included here.
# They are mounted separately in main.py at /vendor/* to avoid conflicts.
#
# This separation ensures:
# - JSON API: /api/v1/vendor/* (this router)
# - HTML Pages: /vendor/* (mounted in main.py)
__all__ = ["router"]

View File

@@ -1,8 +0,0 @@
# app/routes/__init__.py
"""
Frontend route handlers.
"""
from .frontend import router
__all__ = ["router"]

View File

@@ -1,4 +1,4 @@
# app/api/v1/admin/pages.py
# app/routes/admin_pages.py
"""
Admin HTML page routes using Jinja2 templates.

View File

@@ -1,183 +0,0 @@
# app/routes/frontend.py
"""
Frontend HTML route handlers.
Serves static HTML files for admin, vendor, and customer interfaces.
Supports both path-based (/vendor/{vendor_code}/) and query-based access.
"""
from fastapi import APIRouter, Path
from fastapi.responses import FileResponse
router = APIRouter(include_in_schema=False)
# ============================================================================
# ADMIN ROUTES - DISABLED (Now using Jinja2 templates in pages.py)
# ============================================================================
# @router.get("/admin/")
# @router.get("/admin/login")
# async def admin_login():
# """Serve admin login page"""
# return FileResponse("static/admin/login.html")
# @router.get("/admin/dashboard")
# async def admin_dashboard():
# """Serve admin dashboard page"""
# return FileResponse("static/admin/dashboard.html")
# @router.get("/admin/vendors")
# async def admin_vendors():
# """Serve admin vendors management page"""
# return FileResponse("static/admin/vendors.html")
# @router.get("/admin/vendor-edit")
# async def admin_vendor_edit():
# """Serve admin vendor edit page"""
# return FileResponse("static/admin/vendor-edit.html")
# ============================================================================
# VENDOR ROUTES (with vendor code in path)
# ============================================================================
@router.get("/vendor/{vendor_code}/")
@router.get("/vendor/{vendor_code}/login")
async def vendor_login_with_code(vendor_code: str = Path(...)):
"""Serve vendor login page with vendor code in path"""
return FileResponse("static/vendor/login.html")
@router.get("/vendor/{vendor_code}/dashboard")
async def vendor_dashboard_with_code(vendor_code: str = Path(...)):
"""Serve vendor dashboard page with vendor code in path"""
return FileResponse("static/vendor/dashboard.html")
@router.get("/vendor/{vendor_code}/products")
@router.get("/vendor/{vendor_code}/admin/products")
async def vendor_products_with_code(vendor_code: str = Path(...)):
"""Serve vendor products management page"""
return FileResponse("static/vendor/admin/products.html")
@router.get("/vendor/{vendor_code}/orders")
@router.get("/vendor/{vendor_code}/admin/orders")
async def vendor_orders_with_code(vendor_code: str = Path(...)):
"""Serve vendor orders management page"""
return FileResponse("static/vendor/admin/orders.html")
@router.get("/vendor/{vendor_code}/marketplace")
@router.get("/vendor/{vendor_code}/admin/marketplace")
async def vendor_marketplace_with_code(vendor_code: str = Path(...)):
"""Serve vendor marketplace import page"""
return FileResponse("static/vendor/admin/marketplace.html")
@router.get("/vendor/{vendor_code}/customers")
@router.get("/vendor/{vendor_code}/admin/customers")
async def vendor_customers_with_code(vendor_code: str = Path(...)):
"""Serve vendor customers management page"""
return FileResponse("static/vendor/admin/customers.html")
@router.get("/vendor/{vendor_code}/inventory")
@router.get("/vendor/{vendor_code}/admin/inventory")
async def vendor_inventory_with_code(vendor_code: str = Path(...)):
"""Serve vendor inventory management page"""
return FileResponse("static/vendor/admin/inventory.html")
@router.get("/vendor/{vendor_code}/team")
@router.get("/vendor/{vendor_code}/admin/team")
async def vendor_team_with_code(vendor_code: str = Path(...)):
"""Serve vendor team management page"""
return FileResponse("static/vendor/admin/team.html")
# Fallback vendor routes (without vendor code - for query parameter access)
@router.get("/vendor/")
@router.get("/vendor/login")
async def vendor_login():
"""Serve vendor login page (query parameter based)"""
return FileResponse("static/vendor/login.html")
@router.get("/vendor/dashboard")
async def vendor_dashboard():
"""Serve vendor dashboard page (query parameter based)"""
return FileResponse("static/vendor/dashboard.html")
# ============================================================================
# CUSTOMER/SHOP ROUTES
# ============================================================================
@router.get("/shop/")
@router.get("/shop/products")
async def shop_products():
"""Serve shop products catalog page"""
return FileResponse("static/shop/products.html")
@router.get("/shop/products/{product_id}")
async def shop_product_detail(product_id: int):
"""Serve product detail page"""
return FileResponse("static/shop/product.html")
@router.get("/shop/cart")
async def shop_cart():
"""Serve shopping cart page"""
return FileResponse("static/shop/cart.html")
@router.get("/shop/checkout")
async def shop_checkout():
"""Serve checkout page"""
return FileResponse("static/shop/checkout.html")
@router.get("/shop/account/register")
async def shop_register():
"""Serve customer registration page"""
return FileResponse("static/shop/account/register.html")
@router.get("/shop/account/login")
async def shop_login():
"""Serve customer login page"""
return FileResponse("static/shop/account/login.html")
@router.get("/shop/account/dashboard")
async def shop_account_dashboard():
"""Serve customer account dashboard"""
return FileResponse("static/shop/account/dashboard.html")
@router.get("/shop/account/orders")
async def shop_orders():
"""Serve customer orders history page"""
return FileResponse("static/shop/account/orders.html")
@router.get("/shop/account/orders/{order_id}")
async def shop_order_detail(order_id: int):
"""Serve customer order detail page"""
return FileResponse("static/shop/account/order-detail.html")
@router.get("/shop/account/profile")
async def shop_profile():
"""Serve customer profile page"""
return FileResponse("static/shop/account/profile.html")
@router.get("/shop/account/addresses")
async def shop_addresses():
"""Serve customer addresses management page"""
return FileResponse("static/shop/account/addresses.html")

View File

@@ -1,4 +1,4 @@
# app/api/v1/shop/pages.py
# app/routes/shop_pages.py
"""
Shop/Customer HTML page routes using Jinja2 templates.

View File

@@ -1,4 +1,4 @@
# app/api/v1/vendor/pages.py
# app/routes/vendor_pages.py
"""
Vendor HTML page routes using Jinja2 templates.

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@@ -4,8 +4,6 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vendor Dashboard - Multi-Tenant Ecommerce Platform</title>
<link rel="stylesheet" href="/static/css/shared/base.css">
<link rel="stylesheet" href="/static/css/admin/admin.css">
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body x-data="vendorDashboard()" x-init="init()" x-cloak>
@@ -155,6 +153,6 @@
</div>
<script src="/static/shared/js/api-client.js"></script>
<script src="/static/js/vendor/dashboard.js"></script>
<script src="/static/vendor/js/dashboard.js"></script>
</body>
</html>

View File

@@ -986,7 +986,7 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from app.api.v1.admin import routes as admin_api_routes
from app.api.v1.admin import pages as admin_page_routes
from app.routes import pages as admin_page_routes
app = FastAPI(title="Multi-Tenant Platform")
@@ -1010,6 +1010,7 @@ app.include_router(
tags=["admin-pages"]
)
@app.get("/")
async def root():
return {"message": "Multi-Tenant Platform API"}

View File

@@ -22,9 +22,7 @@ from sqlalchemy.orm import Session
from app.api.main import api_router
# Import page routers
from app.api.v1.admin import pages as admin_pages
from app.api.v1.vendor import pages as vendor_pages
from app.api.v1.public.vendors import pages as shop_pages
from app.routes import admin_pages, vendor_pages, shop_pages
from app.core.config import settings
from app.core.database import get_db
from app.core.lifespan import lifespan
@@ -128,6 +126,7 @@ async def vendor_favicon():
# ============================================================================
# HTML PAGE ROUTES (Jinja2 Templates)
# ============================================================================
# Include HTML page routes (these return rendered templates, not JSON)
# Admin pages
app.include_router(

View File

@@ -3,9 +3,8 @@
* Vendor login page logic
*/
// ✅ Use centralized logger - ONE LINE!
// Create custom logger for login page
const loginLog = window.LogConfig.createLogger('LOGIN');
// Create custom logger for vendor login page
const vendorLoginLog = window.LogConfig.createLogger('VENDOR-LOGIN');
function vendorLogin() {
return {
@@ -23,29 +22,38 @@ function vendorLogin() {
dark: false,
async init() {
vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZING ===');
// Load theme
const theme = localStorage.getItem('theme');
if (theme === 'dark') {
this.dark = true;
}
vendorLoginLog.debug('Dark mode:', this.dark);
// Get vendor code from URL path
const pathSegments = window.location.pathname.split('/').filter(Boolean);
if (pathSegments[0] === 'vendor' && pathSegments[1]) {
this.vendorCode = pathSegments[1];
vendorLoginLog.debug('Vendor code from URL:', this.vendorCode);
await this.loadVendor();
}
this.checked = true;
vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZATION COMPLETE ===');
},
async loadVendor() {
vendorLoginLog.info('Loading vendor information...');
this.loading = true;
try {
const response = await apiClient.get(`/vendor/${this.vendorCode}`);
this.vendor = response;
logInfo('Vendor loaded', this.vendor);
vendorLoginLog.info('Vendor loaded successfully:', {
code: this.vendor.code,
name: this.vendor.name
});
} catch (error) {
logError('Failed to load vendor', error);
window.LogConfig.logError(error, 'Load Vendor');
this.error = 'Failed to load vendor information';
} finally {
this.loading = false;
@@ -53,6 +61,7 @@ function vendorLogin() {
},
async handleLogin() {
vendorLoginLog.info('=== VENDOR LOGIN ATTEMPT STARTED ===');
this.clearErrors();
this.loading = true;
@@ -65,30 +74,50 @@ function vendorLogin() {
}
if (Object.keys(this.errors).length > 0) {
vendorLoginLog.warn('Validation failed:', this.errors);
this.loading = false;
return;
}
vendorLoginLog.info('Calling vendor login API...');
vendorLoginLog.debug('Username:', this.credentials.username);
vendorLoginLog.debug('Vendor code:', this.vendorCode);
window.LogConfig.logApiCall('POST', '/vendor/auth/login', {
username: this.credentials.username,
vendor_code: this.vendorCode
}, 'request');
const startTime = performance.now();
const response = await apiClient.post('/vendor/auth/login', {
username: this.credentials.username,
password: this.credentials.password,
vendor_code: this.vendorCode
});
const duration = performance.now() - startTime;
logInfo('Login successful', response);
window.LogConfig.logApiCall('POST', '/vendor/auth/login', {
hasToken: !!response.access_token,
user: response.user?.username
}, 'response');
window.LogConfig.logPerformance('Vendor Login', duration);
vendorLoginLog.info('Login successful!');
vendorLoginLog.debug('Storing authentication data...');
localStorage.setItem('accessToken', response.access_token);
localStorage.setItem('currentUser', JSON.stringify(response.user));
localStorage.setItem('vendorCode', this.vendorCode);
this.success = 'Login successful! Redirecting...';
vendorLoginLog.info('Redirecting to vendor dashboard...');
setTimeout(() => {
window.location.href = `/vendor/${this.vendorCode}/dashboard`;
}, 1000);
} catch (error) {
logError('Login failed', error);
window.LogConfig.logError(error, 'Vendor Login');
if (error.status === 401) {
this.error = 'Invalid username or password';
@@ -97,14 +126,26 @@ function vendorLogin() {
} else {
this.error = error.message || 'Login failed. Please try again.';
}
vendorLoginLog.info('Error message displayed to user:', this.error);
} finally {
this.loading = false;
vendorLoginLog.info('=== VENDOR LOGIN ATTEMPT FINISHED ===');
}
},
clearErrors() {
vendorLoginLog.debug('Clearing form errors');
this.error = '';
this.errors = {};
},
toggleDarkMode() {
vendorLoginLog.debug('Toggling dark mode...');
this.dark = !this.dark;
localStorage.setItem('theme', this.dark ? 'dark' : 'light');
vendorLoginLog.info('Dark mode:', this.dark ? 'ON' : 'OFF');
}
};
}
vendorLoginLog.info('Vendor login module loaded');