from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from sqlalchemy import select, func from typing import List import ulid from app.core.database.db_session import get_db from app.core.permissions.RoleChecker import get_current_user, RoleChecker from app.models.UserModel import User from app.models.RoleModel import Role from app.models.PermissionModel import Permission, RolePermission from app.schemas.RolePermission import RoleResponse, RoleCreate, PermissionResponse, RolePermissionUpdate router = APIRouter(prefix="/api/v1/role-permissions", tags=["Roles & Permissions Control"]) @router.get("/roles/all", response_model=List[RoleResponse]) def get_all_roles( db: Session = Depends(get_db), current_user: User = Depends(RoleChecker(["Super Admin", "Admin"])) ): stmt = select(Role).where(Role.deleted_at.is_(None)) roles = db.execute(stmt).scalars().all() return roles @router.post("/roles/create", response_model=RoleResponse, status_code=status.HTTP_201_CREATED) def create_role( data: RoleCreate, db: Session = Depends(get_db), current_user: User = Depends(RoleChecker(["Super Admin", "Admin"])) ): existing = db.execute(select(Role).where(Role.role_name == data.role_name)).scalar_one_or_none() if existing: raise HTTPException(status_code=400, detail="Role name already exists") role_id = str(ulid.ULID()) new_role = Role( role_id=role_id, role_name=data.role_name, role_prefix=data.role_prefix, description=data.description, is_system=False, is_active=True ) db.add(new_role) db.commit() db.refresh(new_role) return new_role @router.delete("/roles/delete/{role_id}", status_code=status.HTTP_200_OK) def delete_role( role_id: str, db: Session = Depends(get_db), current_user: User = Depends(RoleChecker(["Super Admin", "Admin"])) ): role = db.execute(select(Role).where(Role.role_id == role_id)).scalar_one_or_none() if not role: raise HTTPException(status_code=404, detail="Role not found") if role.is_system: raise HTTPException(status_code=400, detail="System roles cannot be deleted") user_assigned = db.execute(select(User).where(User.role_id == role_id, User.deleted_at.is_(None))).scalars().all() if len(user_assigned) > 0: raise HTTPException(status_code=400, detail="Cannot delete role because it is currently assigned to users") role.deleted_at = db.execute(select(func.now())).scalar() db.commit() return {"detail": "Role deleted successfully"} @router.get("/permissions/all", response_model=List[PermissionResponse]) def get_all_permissions( db: Session = Depends(get_db), current_user: User = Depends(RoleChecker(["Super Admin", "Admin"])) ): stmt = select(Permission).where(Permission.is_active.is_(True)) permissions = db.execute(stmt).scalars().all() if not permissions: # Auto-seed standard platform permissions if permissions table is empty seed_data = [ ("users:read", "User Management", "View user lists and profile details"), ("users:write", "User Management", "Create and modify staff user accounts"), ("users:delete", "User Management", "Delete or deactivate staff user accounts"), ("roles:manage", "User Management", "Create, edit and assign roles & access control policies"), ("catalog:read", "Catalog Management", "View categories, brands, devices, and sales products"), ("catalog:write", "Catalog Management", "Create and edit product catalog items"), ("catalog:delete", "Catalog Management", "Delete catalog items, brands, and categories"), ("inventory:manage", "Inventory & Purchases", "Manage stock movements, warehouses, and purchase orders"), ("marketing:manage", "Marketing & Storefront", "Manage homepage sections, banners, and layout controls"), ("settings:manage", "System Infrastructure", "Configure global system settings and security policies"), ] for code, group, desc in seed_data: p_id = str(ulid.ULID()) parts = code.split(":") module = parts[0] action = parts[1] resource = "general" permission_code = f"{module}.{resource}.{action}" p_obj = Permission( permission_id=p_id, module=module, resource=resource, action=action, permission_code=permission_code, description=desc, is_active=True ) db.add(p_obj) db.commit() permissions = db.execute(stmt).scalars().all() return permissions @router.get("/roles/{role_id}/permissions", response_model=List[str]) def get_role_permissions( role_id: str, db: Session = Depends(get_db), current_user: User = Depends(RoleChecker(["Super Admin", "Admin"])) ): role = db.execute(select(Role).where(Role.role_id == role_id)).scalar_one_or_none() if not role: raise HTTPException(status_code=404, detail="Role not found") stmt = select(RolePermission.permission_id).where(RolePermission.role_id == role_id) perm_ids = db.execute(stmt).scalars().all() return perm_ids @router.post("/roles/{role_id}/permissions", status_code=status.HTTP_200_OK) def update_role_permissions( role_id: str, data: RolePermissionUpdate, db: Session = Depends(get_db), current_user: User = Depends(RoleChecker(["Super Admin", "Admin"])) ): role = db.execute(select(Role).where(Role.role_id == role_id)).scalar_one_or_none() if not role: raise HTTPException(status_code=404, detail="Role not found") if role.role_name.lower() == "super admin": raise HTTPException(status_code=400, detail="Permissions for Super Admin role cannot be modified") if data.permission_ids: valid_perms = db.execute(select(Permission.permission_id).where(Permission.permission_id.in_(data.permission_ids))).scalars().all() if len(valid_perms) != len(data.permission_ids): raise HTTPException(status_code=400, detail="One or more permission IDs are invalid") db.query(RolePermission).filter(RolePermission.role_id == role_id).delete() for perm_id in data.permission_ids: link_id = str(ulid.ULID()) link = RolePermission( id=link_id, role_id=role_id, permission_id=perm_id ) db.add(link) db.commit() return {"detail": "Role permissions updated successfully"}