(self, request, key)
| 562 | |
| 563 | class PluginDeleteAPIView(PluginAuthMixin, APIView): |
| 564 | def delete(self, request, key): |
| 565 | pm = PluginManager.get() |
| 566 | try: |
| 567 | pm.stop_plugin(key, reason="delete") |
| 568 | except Exception: |
| 569 | logger.exception("Failed to stop plugin '%s' before delete", key) |
| 570 | plugins_dir = pm.plugins_dir |
| 571 | target_dir = os.path.join(plugins_dir, key) |
| 572 | # Safety: ensure path inside plugins_dir |
| 573 | abs_plugins = os.path.abspath(plugins_dir) + os.sep |
| 574 | abs_target = os.path.abspath(target_dir) |
| 575 | if not abs_target.startswith(abs_plugins): |
| 576 | return Response({"success": False, "error": "Invalid plugin path"}, status=status.HTTP_400_BAD_REQUEST) |
| 577 | |
| 578 | # Remove files |
| 579 | if os.path.isdir(target_dir): |
| 580 | try: |
| 581 | shutil.rmtree(target_dir) |
| 582 | except Exception as e: |
| 583 | return Response({"success": False, "error": f"Failed to delete plugin files: {e}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) |
| 584 | |
| 585 | # Remove DB record |
| 586 | try: |
| 587 | PluginConfig.objects.filter(key=key).delete() |
| 588 | except Exception: |
| 589 | pass |
| 590 | |
| 591 | # Reload registry |
| 592 | pm.discover_plugins(force_reload=True) |
| 593 | return Response({"success": True}) |
| 594 | |
| 595 | |
| 596 | # --------------------------------------------------------------------------- |
no test coverage detected