major refactoring adding vendor and customer features
This commit is contained in:
17
app/api/v1/admin/__init__.py
Normal file
17
app/api/v1/admin/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Admin API endpoints.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from . import auth, vendors, dashboard, users
|
||||
|
||||
# Create admin router
|
||||
router = APIRouter()
|
||||
|
||||
# Include all admin sub-routers
|
||||
router.include_router(auth.router, tags=["admin-auth"])
|
||||
router.include_router(vendors.router, tags=["admin-vendors"])
|
||||
router.include_router(dashboard.router, tags=["admin-dashboard"])
|
||||
router.include_router(users.router, tags=["admin-users"])
|
||||
|
||||
__all__ = ["router"]
|
||||
58
app/api/v1/admin/auth.py
Normal file
58
app/api/v1/admin/auth.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# app/api/v1/admin/auth.py
|
||||
"""
|
||||
Admin authentication endpoints.
|
||||
|
||||
This module provides:
|
||||
- Admin user login
|
||||
- Admin token validation
|
||||
- Admin-specific authentication logic
|
||||
"""
|
||||
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.services.auth_service import auth_service
|
||||
from app.exceptions import InvalidCredentialsException
|
||||
from models.schemas.auth import LoginResponse, UserLogin
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.post("/login", response_model=LoginResponse)
|
||||
def admin_login(user_credentials: UserLogin, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Admin login endpoint.
|
||||
|
||||
Only allows users with 'admin' role to login.
|
||||
Returns JWT token for authenticated admin users.
|
||||
"""
|
||||
# Authenticate user
|
||||
login_result = auth_service.login_user(db=db, user_credentials=user_credentials)
|
||||
|
||||
# Verify user is admin
|
||||
if login_result["user"].role != "admin":
|
||||
logger.warning(f"Non-admin user attempted admin login: {user_credentials.username}")
|
||||
raise InvalidCredentialsException("Admin access required")
|
||||
|
||||
logger.info(f"Admin login successful: {login_result['user'].username}")
|
||||
|
||||
return LoginResponse(
|
||||
access_token=login_result["token_data"]["access_token"],
|
||||
token_type=login_result["token_data"]["token_type"],
|
||||
expires_in=login_result["token_data"]["expires_in"],
|
||||
user=login_result["user"],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/logout")
|
||||
def admin_logout():
|
||||
"""
|
||||
Admin logout endpoint.
|
||||
|
||||
Client should remove token from storage.
|
||||
Server-side token invalidation can be implemented here if needed.
|
||||
"""
|
||||
return {"message": "Logged out successfully"}
|
||||
90
app/api/v1/admin/dashboard.py
Normal file
90
app/api/v1/admin/dashboard.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# app/api/v1/admin/dashboard.py
|
||||
"""
|
||||
Admin dashboard and statistics endpoints.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_user
|
||||
from app.core.database import get_db
|
||||
from app.services.admin_service import admin_service
|
||||
from app.services.stats_service import stats_service
|
||||
from models.database.user import User
|
||||
from models.schemas.stats import MarketplaceStatsResponse, StatsResponse
|
||||
|
||||
router = APIRouter(prefix="/dashboard")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.get("")
|
||||
def get_admin_dashboard(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get admin dashboard with platform statistics (Admin only)."""
|
||||
return {
|
||||
"platform": {
|
||||
"name": "Multi-Tenant Ecommerce Platform",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
"users": admin_service.get_user_statistics(db),
|
||||
"vendors": admin_service.get_vendor_statistics(db),
|
||||
"recent_vendors": admin_service.get_recent_vendors(db, limit=5),
|
||||
"recent_imports": admin_service.get_recent_import_jobs(db, limit=10),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/stats", response_model=StatsResponse)
|
||||
def get_comprehensive_stats(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get comprehensive platform statistics (Admin only)."""
|
||||
stats_data = stats_service.get_comprehensive_stats(db=db)
|
||||
|
||||
return StatsResponse(
|
||||
total_products=stats_data["total_products"],
|
||||
unique_brands=stats_data["unique_brands"],
|
||||
unique_categories=stats_data["unique_categories"],
|
||||
unique_marketplaces=stats_data["unique_marketplaces"],
|
||||
unique_vendors=stats_data["unique_vendors"],
|
||||
total_inventory_entries=stats_data["total_inventory_entries"],
|
||||
total_inventory_quantity=stats_data["total_inventory_quantity"],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/stats/marketplace", response_model=List[MarketplaceStatsResponse])
|
||||
def get_marketplace_stats(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get statistics broken down by marketplace (Admin only)."""
|
||||
marketplace_stats = stats_service.get_marketplace_breakdown_stats(db=db)
|
||||
|
||||
return [
|
||||
MarketplaceStatsResponse(
|
||||
marketplace=stat["marketplace"],
|
||||
total_products=stat["total_products"],
|
||||
unique_vendors=stat["unique_vendors"],
|
||||
unique_brands=stat["unique_brands"],
|
||||
)
|
||||
for stat in marketplace_stats
|
||||
]
|
||||
|
||||
|
||||
@router.get("/stats/platform")
|
||||
def get_platform_statistics(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get comprehensive platform statistics (Admin only)."""
|
||||
return {
|
||||
"users": admin_service.get_user_statistics(db),
|
||||
"vendors": admin_service.get_vendor_statistics(db),
|
||||
"products": admin_service.get_product_statistics(db),
|
||||
"orders": admin_service.get_order_statistics(db),
|
||||
"imports": admin_service.get_import_statistics(db),
|
||||
}
|
||||
49
app/api/v1/admin/marketplace.py
Normal file
49
app/api/v1/admin/marketplace.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# app/api/v1/admin/marketplace.py
|
||||
"""
|
||||
Marketplace import job monitoring endpoints for admin.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_user
|
||||
from app.core.database import get_db
|
||||
from app.services.admin_service import admin_service
|
||||
from models.schemas.marketplace_import_job import MarketplaceImportJobResponse
|
||||
from models.database.user import User
|
||||
|
||||
router = APIRouter(prefix="/marketplace-import-jobs")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.get("", response_model=List[MarketplaceImportJobResponse])
|
||||
def get_all_marketplace_import_jobs(
|
||||
marketplace: Optional[str] = Query(None),
|
||||
vendor_name: Optional[str] = Query(None),
|
||||
status: Optional[str] = Query(None),
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=100),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get all marketplace import jobs (Admin only)."""
|
||||
return admin_service.get_marketplace_import_jobs(
|
||||
db=db,
|
||||
marketplace=marketplace,
|
||||
vendor_name=vendor_name,
|
||||
status=status,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
def get_import_statistics(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get marketplace import statistics (Admin only)."""
|
||||
return admin_service.get_import_statistics(db)
|
||||
51
app/api/v1/admin/users.py
Normal file
51
app/api/v1/admin/users.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# app/api/v1/admin/users.py
|
||||
"""
|
||||
User management endpoints for admin.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_user
|
||||
from app.core.database import get_db
|
||||
from app.services.admin_service import admin_service
|
||||
from models.schemas.auth import UserResponse
|
||||
from models.database.user import User
|
||||
|
||||
router = APIRouter(prefix="/users")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.get("", response_model=List[UserResponse])
|
||||
def get_all_users(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get all users (Admin only)."""
|
||||
users = admin_service.get_all_users(db=db, skip=skip, limit=limit)
|
||||
return [UserResponse.model_validate(user) for user in users]
|
||||
|
||||
|
||||
@router.put("/{user_id}/status")
|
||||
def toggle_user_status(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Toggle user active status (Admin only)."""
|
||||
user, message = admin_service.toggle_user_status(db, user_id, current_admin.id)
|
||||
return {"message": message}
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
def get_user_statistics(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get user statistics for admin dashboard (Admin only)."""
|
||||
return admin_service.get_user_statistics(db)
|
||||
143
app/api/v1/admin/vendors.py
Normal file
143
app/api/v1/admin/vendors.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# app/api/v1/admin/vendors.py
|
||||
"""
|
||||
Vendor management endpoints for admin.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_user
|
||||
from app.core.database import get_db
|
||||
from app.services.admin_service import admin_service
|
||||
from models.schemas.vendor import VendorListResponse, VendorResponse, VendorCreate
|
||||
from models.database.user import User
|
||||
|
||||
router = APIRouter(prefix="/vendors")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.post("", response_model=VendorResponse)
|
||||
def create_vendor_with_owner(
|
||||
vendor_data: VendorCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
Create a new vendor with owner user account (Admin only).
|
||||
|
||||
This endpoint:
|
||||
1. Creates a new vendor
|
||||
2. Creates an owner user account for the vendor
|
||||
3. Sets up default roles (Owner, Manager, Editor, Viewer)
|
||||
4. Sends welcome email to vendor owner (if email service configured)
|
||||
|
||||
Returns the created vendor with owner information.
|
||||
"""
|
||||
vendor, owner_user, temp_password = admin_service.create_vendor_with_owner(
|
||||
db=db,
|
||||
vendor_data=vendor_data
|
||||
)
|
||||
|
||||
return {
|
||||
**VendorResponse.model_validate(vendor).model_dump(),
|
||||
"owner_email": owner_user.email,
|
||||
"owner_username": owner_user.username,
|
||||
"temporary_password": temp_password, # Only shown once!
|
||||
"login_url": f"{vendor.subdomain}.platform.com/vendor/login" if vendor.subdomain else None
|
||||
}
|
||||
|
||||
|
||||
@router.get("", response_model=VendorListResponse)
|
||||
def get_all_vendors_admin(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
search: Optional[str] = Query(None, description="Search by name or vendor code"),
|
||||
is_active: Optional[bool] = Query(None),
|
||||
is_verified: Optional[bool] = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get all vendors with admin view (Admin only)."""
|
||||
vendors, total = admin_service.get_all_vendors(
|
||||
db=db,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
search=search,
|
||||
is_active=is_active,
|
||||
is_verified=is_verified
|
||||
)
|
||||
return VendorListResponse(vendors=vendors, total=total, skip=skip, limit=limit)
|
||||
|
||||
|
||||
@router.get("/{vendor_id}", response_model=VendorResponse)
|
||||
def get_vendor_details(
|
||||
vendor_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get detailed vendor information (Admin only)."""
|
||||
vendor = admin_service.get_vendor_by_id(db, vendor_id)
|
||||
return VendorResponse.model_validate(vendor)
|
||||
|
||||
|
||||
@router.put("/{vendor_id}/verify")
|
||||
def verify_vendor(
|
||||
vendor_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Verify/unverify vendor (Admin only)."""
|
||||
vendor, message = admin_service.verify_vendor(db, vendor_id)
|
||||
return {"message": message, "vendor": VendorResponse.model_validate(vendor)}
|
||||
|
||||
|
||||
@router.put("/{vendor_id}/status")
|
||||
def toggle_vendor_status(
|
||||
vendor_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Toggle vendor active status (Admin only)."""
|
||||
vendor, message = admin_service.toggle_vendor_status(db, vendor_id)
|
||||
return {"message": message, "vendor": VendorResponse.model_validate(vendor)}
|
||||
|
||||
|
||||
@router.delete("/{vendor_id}")
|
||||
def delete_vendor(
|
||||
vendor_id: int,
|
||||
confirm: bool = Query(False, description="Must be true to confirm deletion"),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
Delete vendor and all associated data (Admin only).
|
||||
|
||||
WARNING: This is destructive and will delete:
|
||||
- Vendor account
|
||||
- All products
|
||||
- All orders
|
||||
- All customers
|
||||
- All team members
|
||||
|
||||
Requires confirmation parameter.
|
||||
"""
|
||||
if not confirm:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Deletion requires confirmation parameter: confirm=true"
|
||||
)
|
||||
|
||||
message = admin_service.delete_vendor(db, vendor_id)
|
||||
return {"message": message}
|
||||
|
||||
|
||||
@router.get("/stats/vendors")
|
||||
def get_vendor_statistics(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""Get vendor statistics for admin dashboard (Admin only)."""
|
||||
return admin_service.get_vendor_statistics(db)
|
||||
Reference in New Issue
Block a user