created specific route files for frontends
This commit is contained in:
@@ -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"]
|
||||
|
||||
15
app/api/v1/vendor/__init__.py
vendored
15
app/api/v1/vendor/__init__.py
vendored
@@ -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"]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# app/routes/__init__.py
|
||||
"""
|
||||
Frontend route handlers.
|
||||
"""
|
||||
|
||||
from .frontend import router
|
||||
|
||||
__all__ = ["router"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# app/api/v1/admin/pages.py
|
||||
# app/routes/admin_pages.py
|
||||
"""
|
||||
Admin HTML page routes using Jinja2 templates.
|
||||
|
||||
@@ -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")
|
||||
@@ -1,4 +1,4 @@
|
||||
# app/api/v1/shop/pages.py
|
||||
# app/routes/shop_pages.py
|
||||
"""
|
||||
Shop/Customer HTML page routes using Jinja2 templates.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# app/api/v1/vendor/pages.py
|
||||
# app/routes/vendor_pages.py
|
||||
"""
|
||||
Vendor HTML page routes using Jinja2 templates.
|
||||
|
||||
10
app/templates/admin/vendor-create.html
Normal file
10
app/templates/admin/vendor-create.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
4
app/templates/vendor/admin/dashboard.html
vendored
4
app/templates/vendor/admin/dashboard.html
vendored
@@ -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>
|
||||
@@ -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"}
|
||||
|
||||
5
main.py
5
main.py
@@ -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(
|
||||
|
||||
55
static/vendor/js/login.js
vendored
55
static/vendor/js/login.js
vendored
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user