Samba is installed and can be configured in the Controller.

This commit is contained in:
Roland 2026-03-28 16:28:47 -06:00 • committed by lrnselfreliance
parent 0ceac364ec
commit ce0cdd3a2e
15 changed files with 837 additions and 5 deletions

View file

@ -10,6 +10,10 @@ from controller.api.schemas import (
HotspotActionResponse,
HotspotStatusResponse,
RestartServicesResponse,
SambaShareAddRequest,
SambaShareRemoveResponse,
SambaShareResponse,
SambaStatusResponse,
ServiceRestartResult,
SystemActionResponse,
ThrottleActionResponse,
@ -35,6 +39,11 @@ from controller.lib.admin import (
shutdown_system,
)
from controller.lib.config import is_docker_mode
from controller.lib.samba import (
add_share,
get_samba_status_dict,
remove_share,
)
router = APIRouter(tags=["admin"])
@ -91,6 +100,32 @@ async def bluetooth_disable() -> BluetoothActionResponse:
return BluetoothActionResponse(**result)
# --- Samba ---
@router.get("/api/samba/status", response_model=SambaStatusResponse)
async def samba_status() -> SambaStatusResponse:
"""Get Samba sharing status."""
return SambaStatusResponse(**get_samba_status_dict())
@router.post("/api/samba/shares", response_model=SambaShareResponse)
async def samba_add_share(request: SambaShareAddRequest) -> SambaShareResponse:
"""Add a Samba share."""
result = add_share(request.name, request.path, request.read_only, request.comment or "")
if not result.get("success"):
raise HTTPException(status_code=400, detail=result.get("error", "Failed"))
return SambaShareResponse(**result)
@router.delete("/api/samba/shares/{share_name}", response_model=SambaShareRemoveResponse)
async def samba_remove_share(share_name: str) -> SambaShareRemoveResponse:
"""Remove a Samba share."""
result = remove_share(share_name)
if not result.get("success"):
raise HTTPException(status_code=404, detail=result.get("error", "Failed"))
return SambaShareRemoveResponse(**result)
# --- Throttle ---
@router.get("/api/throttle/status", response_model=ThrottleStatusResponse)

View file

@ -185,6 +185,52 @@ class BluetoothActionResponse(BaseModel):
error: Optional[str] = Field(default=None, description="Error message if failed")
# ============================================================================
# Admin - Samba
# ============================================================================
class SambaShareInfo(BaseModel):
"""Information about a single Samba share."""
name: str = Field(description="Share name")
path: str = Field(description="Filesystem path being shared")
read_only: bool = Field(description="Whether share is read-only")
comment: Optional[str] = Field(default="", description="Share description")
class SambaStatusResponse(BaseModel):
"""Response model for /api/samba/status endpoint."""
enabled: bool = Field(description="Whether Samba is currently running")
available: bool = Field(description="Whether Samba functionality is available")
reason: Optional[str] = Field(default=None, description="Reason if unavailable")
shares: list[SambaShareInfo] = Field(default_factory=list, description="Configured shares")
class SambaShareAddRequest(BaseModel):
"""Request model for adding a Samba share."""
name: str = Field(description="Share name (alphanumeric, hyphens, underscores)")
path: str = Field(description="Filesystem path to share")
read_only: bool = Field(default=True, description="Whether share is read-only")
comment: Optional[str] = Field(default="", description="Share description")
class SambaShareResponse(BaseModel):
"""Response model for share add/update actions."""
success: bool = Field(description="Whether the action succeeded")
share: Optional[SambaShareInfo] = Field(default=None, description="The share")
error: Optional[str] = Field(default=None, description="Error message if failed")
class SambaShareRemoveResponse(BaseModel):
"""Response model for removing a Samba share."""
success: bool = Field(description="Whether the share was removed")
error: Optional[str] = Field(default=None, description="Error message if failed")
# ============================================================================
# Admin - Throttle
# ============================================================================