diff --git a/ui/app.py b/ui/app.py index bdda21c..cf17f13 100644 --- a/ui/app.py +++ b/ui/app.py @@ -25,6 +25,7 @@ try: RunWorkflowModel, SchemaModel, ServerModel, + WorkflowBatchDeleteModel, TransferExportModel, ToggleModel, TransferImportModel, @@ -41,6 +42,7 @@ except ImportError: RunWorkflowModel, SchemaModel, ServerModel, + WorkflowBatchDeleteModel, TransferExportModel, ToggleModel, TransferImportModel, @@ -227,6 +229,14 @@ def create_app() -> FastAPI: service.delete_workflow(server_id, workflow_id) return {"status": "success"} + @app.post("/api/servers/{server_id}/workflows/batch-delete") + async def batch_delete_workflows(server_id: str, data: WorkflowBatchDeleteModel) -> dict: + try: + result = service.delete_workflows(server_id, data.workflow_ids) + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) from e + return {"status": "success", **result} + @app.post("/api/servers/{server_id}/workflow/{workflow_id}/run") async def run_workflow(server_id: str, workflow_id: str, data: RunWorkflowModel) -> dict: try: diff --git a/ui/models.py b/ui/models.py index b65e11d..ec4f004 100644 --- a/ui/models.py +++ b/ui/models.py @@ -138,7 +138,7 @@ class ToggleModel(BaseModel): enabled: bool -class WorkflowOrderModel(BaseModel): +class WorkflowIdsModel(BaseModel): workflow_ids: list[str] = Field(min_length=1) @field_validator("workflow_ids", mode="before") @@ -173,6 +173,14 @@ class WorkflowOrderModel(BaseModel): return normalized +class WorkflowOrderModel(WorkflowIdsModel): + pass + + +class WorkflowBatchDeleteModel(WorkflowIdsModel): + pass + + class TransferPreviewModel(BaseModel): bundle: dict[str, Any] apply_environment: bool = False diff --git a/ui/services.py b/ui/services.py index ad09779..545a5f9 100644 --- a/ui/services.py +++ b/ui/services.py @@ -73,7 +73,9 @@ class WorkflowSummary: "origin": self.origin, "source_label": self.source_label, "tags": self.tags, - "has_history": self.has_history, + # Keep the key for backward compatibility with older bundles, + # but always disable the list-level history entry point. + "has_history": False, } @@ -237,7 +239,7 @@ class UIStorageService: origin=origin, source_label=source_label, tags=tags, - has_history=self._workflow_has_history(sid, wf_id), + has_history=False, )) server_workflows.sort( @@ -392,6 +394,28 @@ class UIStorageService: pass self._remove_workflow_from_order(server_id, workflow_id) + def delete_workflows(self, server_id: str, workflow_ids: list[str]) -> dict[str, list[str]]: + config = self.get_config() + if self._get_server_entry(config, server_id) is None: + raise FileNotFoundError(f"Server '{server_id}' not found") + + deleted: list[str] = [] + missing: list[str] = [] + + for workflow_id in workflow_ids: + workflow_path = self._workflow_path(server_id, workflow_id) + schema_path = self._schema_path(server_id, workflow_id) + history_dir = self._history_dir(server_id, workflow_id) + + if not workflow_path.exists() and not schema_path.exists() and not history_dir.exists(): + missing.append(workflow_id) + continue + + self.delete_workflow(server_id, workflow_id) + deleted.append(workflow_id) + + return {"deleted": deleted, "missing": missing} + def reorder_workflows(self, server_id: str, workflow_ids: list[str]) -> list[str]: config = self.get_config() server = self._get_server_entry(config, server_id)