From da3f28849e2614eb2bf994e0bed1a09e0055748c Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sat, 31 Jan 2026 15:09:41 +0100 Subject: [PATCH] refactor: migrate vendor auth, profile, team, dashboard, settings to modules Tenancy module (identity & organizational hierarchy): - vendor_auth.py: login, logout, /me endpoints - vendor_profile.py: vendor profile get/update - vendor_team.py: team management, roles, permissions, invitations Core module (foundational non-domain features): - vendor_dashboard.py: dashboard statistics - vendor_settings.py: localization, business info, letzshop settings All routes auto-discovered via is_self_contained=True. Deleted 5 legacy files from app/api/v1/vendor/. Co-Authored-By: Claude Opus 4.5 --- app/api/v1/vendor/__init__.py | 21 +++++----------- app/modules/core/definition.py | 1 + app/modules/core/routes/__init__.py | 4 +++ app/modules/core/routes/api/__init__.py | 12 +++++++++ app/modules/core/routes/api/vendor.py | 19 ++++++++++++++ .../core/routes/api/vendor_dashboard.py} | 6 ++--- .../core/routes/api/vendor_settings.py} | 12 ++++----- app/modules/tenancy/routes/api/__init__.py | 15 +++++++++-- app/modules/tenancy/routes/api/vendor.py | 25 +++++++++++++++---- .../tenancy/routes/api/vendor_auth.py} | 10 ++++---- .../tenancy/routes/api/vendor_profile.py} | 8 +++--- .../tenancy/routes/api/vendor_team.py} | 24 +++++++++--------- 12 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 app/modules/core/routes/__init__.py create mode 100644 app/modules/core/routes/api/__init__.py create mode 100644 app/modules/core/routes/api/vendor.py rename app/{api/v1/vendor/dashboard.py => modules/core/routes/api/vendor_dashboard.py} (93%) rename app/{api/v1/vendor/settings.py => modules/core/routes/api/vendor_settings.py} (98%) rename app/{api/v1/vendor/auth.py => modules/tenancy/routes/api/vendor_auth.py} (95%) rename app/{api/v1/vendor/profile.py => modules/tenancy/routes/api/vendor_profile.py} (84%) rename app/{api/v1/vendor/team.py => modules/tenancy/routes/api/vendor_team.py} (93%) diff --git a/app/api/v1/vendor/__init__.py b/app/api/v1/vendor/__init__.py index 6bf8ede4..2eca6cfd 100644 --- a/app/api/v1/vendor/__init__.py +++ b/app/api/v1/vendor/__init__.py @@ -24,23 +24,18 @@ Self-contained modules (auto-discovered from app/modules/{module}/routes/api/ven - cms: Content pages management - customers: Customer management - payments: Payment configuration, Stripe connect, transactions -- tenancy: Public vendor info lookup +- tenancy: Vendor info, auth, profile, team management """ from fastapi import APIRouter # Import all sub-routers (legacy routes that haven't been migrated to modules) from . import ( - auth, - dashboard, email_settings, email_templates, media, messages, notifications, - profile, - settings, - team, ) # Create vendor router @@ -51,18 +46,14 @@ router = APIRouter() # ============================================================================ # These routes return JSON and are mounted at /api/v1/vendor/* -# Authentication (no prefix, specific routes like /auth/login) -router.include_router(auth.router, tags=["vendor-auth"]) - -# Vendor management (with prefixes: /dashboard/*, /profile/*, /settings/*) -router.include_router(dashboard.router, tags=["vendor-dashboard"]) -router.include_router(profile.router, tags=["vendor-profile"]) -router.include_router(settings.router, tags=["vendor-settings"]) +# Email configuration router.include_router(email_templates.router, tags=["vendor-email-templates"]) router.include_router(email_settings.router, tags=["vendor-email-settings"]) -# Business operations (with prefixes: /team/*) -router.include_router(team.router, tags=["vendor-team"]) +# Services (with prefixes: /media/*, etc.) +router.include_router(media.router, tags=["vendor-media"]) +router.include_router(notifications.router, tags=["vendor-notifications"]) +router.include_router(messages.router, tags=["vendor-messages"]) # Services (with prefixes: /media/*, etc.) router.include_router(media.router, tags=["vendor-media"]) diff --git a/app/modules/core/definition.py b/app/modules/core/definition.py index 47caa196..f34815ca 100644 --- a/app/modules/core/definition.py +++ b/app/modules/core/definition.py @@ -15,6 +15,7 @@ core_module = ModuleDefinition( description="Dashboard, settings, and profile management. Required for basic operation.", version="1.0.0", is_core=True, + is_self_contained=True, features=[ "dashboard", "settings", diff --git a/app/modules/core/routes/__init__.py b/app/modules/core/routes/__init__.py new file mode 100644 index 00000000..86b40285 --- /dev/null +++ b/app/modules/core/routes/__init__.py @@ -0,0 +1,4 @@ +# app/modules/core/routes/__init__.py +""" +Core module route registration. +""" diff --git a/app/modules/core/routes/api/__init__.py b/app/modules/core/routes/api/__init__.py new file mode 100644 index 00000000..2a00dde4 --- /dev/null +++ b/app/modules/core/routes/api/__init__.py @@ -0,0 +1,12 @@ +# app/modules/core/routes/api/__init__.py +""" +Core module API routes. + +Vendor routes: +- /dashboard/* - Dashboard statistics +- /settings/* - Vendor settings management +""" + +from .vendor import vendor_router + +__all__ = ["vendor_router"] diff --git a/app/modules/core/routes/api/vendor.py b/app/modules/core/routes/api/vendor.py new file mode 100644 index 00000000..f8adf59c --- /dev/null +++ b/app/modules/core/routes/api/vendor.py @@ -0,0 +1,19 @@ +# app/modules/core/routes/api/vendor.py +""" +Core module vendor API routes. + +Aggregates: +- /dashboard/* - Dashboard statistics +- /settings/* - Vendor settings management +""" + +from fastapi import APIRouter + +from .vendor_dashboard import vendor_dashboard_router +from .vendor_settings import vendor_settings_router + +vendor_router = APIRouter() + +# Aggregate sub-routers +vendor_router.include_router(vendor_dashboard_router, tags=["vendor-dashboard"]) +vendor_router.include_router(vendor_settings_router, tags=["vendor-settings"]) diff --git a/app/api/v1/vendor/dashboard.py b/app/modules/core/routes/api/vendor_dashboard.py similarity index 93% rename from app/api/v1/vendor/dashboard.py rename to app/modules/core/routes/api/vendor_dashboard.py index c501e168..e6a68bd9 100644 --- a/app/api/v1/vendor/dashboard.py +++ b/app/modules/core/routes/api/vendor_dashboard.py @@ -1,4 +1,4 @@ -# app/api/v1/vendor/dashboard.py +# app/modules/core/routes/api/vendor_dashboard.py """ Vendor dashboard and statistics endpoints. @@ -26,11 +26,11 @@ from app.modules.analytics.schemas import ( VendorRevenueStats, ) -router = APIRouter(prefix="/dashboard") +vendor_dashboard_router = APIRouter(prefix="/dashboard") logger = logging.getLogger(__name__) -@router.get("/stats", response_model=VendorDashboardStatsResponse) +@vendor_dashboard_router.get("/stats", response_model=VendorDashboardStatsResponse) def get_vendor_dashboard_stats( request: Request, current_user: UserContext = Depends(get_current_vendor_api), diff --git a/app/api/v1/vendor/settings.py b/app/modules/core/routes/api/vendor_settings.py similarity index 98% rename from app/api/v1/vendor/settings.py rename to app/modules/core/routes/api/vendor_settings.py index 9a149f94..2491e5ba 100644 --- a/app/api/v1/vendor/settings.py +++ b/app/modules/core/routes/api/vendor_settings.py @@ -1,4 +1,4 @@ -# app/api/v1/vendor/settings.py +# app/modules/core/routes/api/vendor_settings.py """ Vendor settings and configuration endpoints. @@ -18,7 +18,7 @@ from app.services.platform_settings_service import platform_settings_service from app.services.vendor_service import vendor_service from models.schema.auth import UserContext -router = APIRouter(prefix="/settings") +vendor_settings_router = APIRouter(prefix="/settings") logger = logging.getLogger(__name__) # Supported languages for dropdown @@ -154,7 +154,7 @@ class LetzshopFeedSettingsUpdate(BaseModel): return v -@router.get("") +@vendor_settings_router.get("") def get_vendor_settings( current_user: UserContext = Depends(get_current_vendor_api), db: Session = Depends(get_db), @@ -318,7 +318,7 @@ def get_vendor_settings( } -@router.put("/business-info") +@vendor_settings_router.put("/business-info") def update_business_info( business_info: BusinessInfoUpdate, current_user: UserContext = Depends(get_current_vendor_api), @@ -360,7 +360,7 @@ def update_business_info( } -@router.put("/letzshop") +@vendor_settings_router.put("/letzshop") def update_letzshop_settings( letzshop_config: LetzshopFeedSettingsUpdate, current_user: UserContext = Depends(get_current_vendor_api), @@ -394,7 +394,7 @@ def update_letzshop_settings( } -@router.put("/localization") +@vendor_settings_router.put("/localization") def update_localization_settings( localization_config: LocalizationSettingsUpdate, current_user: UserContext = Depends(get_current_vendor_api), diff --git a/app/modules/tenancy/routes/api/__init__.py b/app/modules/tenancy/routes/api/__init__.py index e55c6b5f..d7009d7a 100644 --- a/app/modules/tenancy/routes/api/__init__.py +++ b/app/modules/tenancy/routes/api/__init__.py @@ -2,10 +2,21 @@ """ Tenancy module API routes. -Includes: +Vendor routes: - /info/{vendor_code} - Public vendor info lookup +- /auth/* - Vendor authentication (login, logout, /me) +- /profile/* - Vendor profile management +- /team/* - Team member management, roles, permissions """ from .vendor import vendor_router +from .vendor_auth import vendor_auth_router +from .vendor_profile import vendor_profile_router +from .vendor_team import vendor_team_router -__all__ = ["vendor_router"] +__all__ = [ + "vendor_router", + "vendor_auth_router", + "vendor_profile_router", + "vendor_team_router", +] diff --git a/app/modules/tenancy/routes/api/vendor.py b/app/modules/tenancy/routes/api/vendor.py index 27d9c48f..38e06b8b 100644 --- a/app/modules/tenancy/routes/api/vendor.py +++ b/app/modules/tenancy/routes/api/vendor.py @@ -2,12 +2,13 @@ """ Tenancy module vendor API routes. -Provides public vendor information lookup for: -- Vendor login pages to display branding -- Public vendor profile lookup +Aggregates all vendor tenancy routes: +- /info/{vendor_code} - Public vendor info lookup +- /auth/* - Vendor authentication (login, logout, /me) +- /profile/* - Vendor profile management +- /team/* - Team member management, roles, permissions -These endpoints do NOT require authentication - they provide -public information about vendors. +The tenancy module owns identity and organizational hierarchy. """ import logging @@ -80,3 +81,17 @@ def get_vendor_info( owner_email=vendor.company.owner.email, owner_username=vendor.company.owner.username, ) + + +# ============================================================================ +# Aggregate Sub-Routers +# ============================================================================ +# Include all tenancy vendor routes (auth, profile, team) + +from .vendor_auth import vendor_auth_router +from .vendor_profile import vendor_profile_router +from .vendor_team import vendor_team_router + +vendor_router.include_router(vendor_auth_router, tags=["vendor-auth"]) +vendor_router.include_router(vendor_profile_router, tags=["vendor-profile"]) +vendor_router.include_router(vendor_team_router, tags=["vendor-team"]) diff --git a/app/api/v1/vendor/auth.py b/app/modules/tenancy/routes/api/vendor_auth.py similarity index 95% rename from app/api/v1/vendor/auth.py rename to app/modules/tenancy/routes/api/vendor_auth.py index 8e02fdda..0ec9f9f8 100644 --- a/app/api/v1/vendor/auth.py +++ b/app/modules/tenancy/routes/api/vendor_auth.py @@ -1,4 +1,4 @@ -# app/api/v1/vendor/auth.py +# app/modules/tenancy/routes/api/vendor_auth.py """ Vendor team authentication endpoints. @@ -27,7 +27,7 @@ from middleware.vendor_context import get_current_vendor from models.schema.auth import UserContext from models.schema.auth import LogoutResponse, UserLogin, VendorUserResponse -router = APIRouter(prefix="/auth") +vendor_auth_router = APIRouter(prefix="/auth") logger = logging.getLogger(__name__) @@ -41,7 +41,7 @@ class VendorLoginResponse(BaseModel): vendor_role: str -@router.post("/login", response_model=VendorLoginResponse) +@vendor_auth_router.post("/login", response_model=VendorLoginResponse) def vendor_login( user_credentials: UserLogin, request: Request, @@ -156,7 +156,7 @@ def vendor_login( ) -@router.post("/logout", response_model=LogoutResponse) +@vendor_auth_router.post("/logout", response_model=LogoutResponse) def vendor_logout(response: Response): """ Vendor team member logout. @@ -177,7 +177,7 @@ def vendor_logout(response: Response): return LogoutResponse(message="Logged out successfully") -@router.get("/me", response_model=VendorUserResponse) +@vendor_auth_router.get("/me", response_model=VendorUserResponse) def get_current_vendor_user( user: UserContext = Depends(get_current_vendor_api), db: Session = Depends(get_db) ): diff --git a/app/api/v1/vendor/profile.py b/app/modules/tenancy/routes/api/vendor_profile.py similarity index 84% rename from app/api/v1/vendor/profile.py rename to app/modules/tenancy/routes/api/vendor_profile.py index fefc1239..14ff9344 100644 --- a/app/api/v1/vendor/profile.py +++ b/app/modules/tenancy/routes/api/vendor_profile.py @@ -1,4 +1,4 @@ -# app/api/v1/vendor/profile.py +# app/modules/tenancy/routes/api/vendor_profile.py """ Vendor profile management endpoints. @@ -17,11 +17,11 @@ from app.services.vendor_service import vendor_service from models.schema.auth import UserContext from models.schema.vendor import VendorResponse, VendorUpdate -router = APIRouter(prefix="/profile") +vendor_profile_router = APIRouter(prefix="/profile") logger = logging.getLogger(__name__) -@router.get("", response_model=VendorResponse) +@vendor_profile_router.get("", response_model=VendorResponse) def get_vendor_profile( current_user: UserContext = Depends(get_current_vendor_api), db: Session = Depends(get_db), @@ -31,7 +31,7 @@ def get_vendor_profile( return vendor -@router.put("", response_model=VendorResponse) +@vendor_profile_router.put("", response_model=VendorResponse) def update_vendor_profile( vendor_update: VendorUpdate, current_user: UserContext = Depends(get_current_vendor_api), diff --git a/app/api/v1/vendor/team.py b/app/modules/tenancy/routes/api/vendor_team.py similarity index 93% rename from app/api/v1/vendor/team.py rename to app/modules/tenancy/routes/api/vendor_team.py index e82eeaef..bf9eddae 100644 --- a/app/api/v1/vendor/team.py +++ b/app/modules/tenancy/routes/api/vendor_team.py @@ -1,4 +1,4 @@ -# app/api/v1/vendor/teams.py +# app/modules/tenancy/routes/api/vendor_team.py """ Vendor team member management endpoints. @@ -40,7 +40,7 @@ from models.schema.team import ( UserPermissionsResponse, ) -router = APIRouter(prefix="/team") +vendor_team_router = APIRouter(prefix="/team") logger = logging.getLogger(__name__) @@ -49,7 +49,7 @@ logger = logging.getLogger(__name__) # ============================================================================ -@router.get("/members", response_model=TeamMemberListResponse) +@vendor_team_router.get("/members", response_model=TeamMemberListResponse) def list_team_members( request: Request, include_inactive: bool = False, @@ -91,7 +91,7 @@ def list_team_members( ) -@router.post("/invite", response_model=InvitationResponse) +@vendor_team_router.post("/invite", response_model=InvitationResponse) def invite_team_member( invitation: TeamMemberInvite, request: Request, @@ -165,7 +165,7 @@ def invite_team_member( ) -@router.post("/accept-invitation", response_model=InvitationAcceptResponse) # public +@vendor_team_router.post("/accept-invitation", response_model=InvitationAcceptResponse) # public def accept_invitation(acceptance: InvitationAccept, db: Session = Depends(get_db)): """ Accept a team invitation and activate account. @@ -215,7 +215,7 @@ def accept_invitation(acceptance: InvitationAccept, db: Session = Depends(get_db ) -@router.get("/members/{user_id}", response_model=TeamMemberResponse) +@vendor_team_router.get("/members/{user_id}", response_model=TeamMemberResponse) def get_team_member( user_id: int, request: Request, @@ -244,7 +244,7 @@ def get_team_member( return TeamMemberResponse(**member) -@router.put("/members/{user_id}", response_model=TeamMemberResponse) +@vendor_team_router.put("/members/{user_id}", response_model=TeamMemberResponse) def update_team_member( user_id: int, update_data: TeamMemberUpdate, @@ -288,7 +288,7 @@ def update_team_member( return TeamMemberResponse(**member) -@router.delete("/members/{user_id}") +@vendor_team_router.delete("/members/{user_id}") def remove_team_member( user_id: int, request: Request, @@ -320,7 +320,7 @@ def remove_team_member( return {"message": "Team member removed successfully", "user_id": user_id} -@router.post("/members/bulk-remove", response_model=BulkRemoveResponse) +@vendor_team_router.post("/members/bulk-remove", response_model=BulkRemoveResponse) def bulk_remove_team_members( bulk_remove: BulkRemoveRequest, request: Request, @@ -365,7 +365,7 @@ def bulk_remove_team_members( # ============================================================================ -@router.get("/roles", response_model=RoleListResponse) +@vendor_team_router.get("/roles", response_model=RoleListResponse) def list_roles( request: Request, db: Session = Depends(get_db), @@ -395,7 +395,7 @@ def list_roles( # ============================================================================ -@router.get("/me/permissions", response_model=UserPermissionsResponse) +@vendor_team_router.get("/me/permissions", response_model=UserPermissionsResponse) def get_my_permissions( request: Request, permissions: list[str] = Depends(get_user_permissions), @@ -434,7 +434,7 @@ def get_my_permissions( # ============================================================================ -@router.get("/statistics", response_model=TeamStatistics) +@vendor_team_router.get("/statistics", response_model=TeamStatistics) def get_team_statistics( request: Request, db: Session = Depends(get_db),