Fix plugin list bug and switch watcher to PollingObserver

- Fix: list_plugins imported _REGISTRY as a direct reference to the
  empty list that existed at import time; register_services() replaces
  _REGISTRY with a new list so the imported reference was always [].
  Added get_registry() helper so callers access the live list via the
  module namespace. GET /api/plugins now correctly returns accessible
  plugins for the current user.

- Fix: switch watchdog from InotifyObserver to PollingObserver. Inotify
  events from the macOS host are not forwarded through the Docker bind
  mount, so new files were only detected via the startup scan. PollingObserver
  (1s default interval) works reliably on all platforms including
  macOS+Docker bind mounts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-04-18 02:25:16 +02:00
parent 00466a9801
commit 18a638bc3a
3 changed files with 9 additions and 4 deletions
+2 -2
View File
@@ -20,7 +20,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.deps import check_plugin_access, get_current_user
from app.models.user import User
from app.services.service_health import _REGISTRY, get_cached_manifest, get_service_url
from app.services.service_health import get_cached_manifest, get_registry, get_service_url
router = APIRouter()
@@ -72,7 +72,7 @@ async def list_plugins(
) -> list[dict]:
"""Return the list of plugins the current user may access."""
accessible = []
for svc in _REGISTRY:
for svc in get_registry():
manifest = get_cached_manifest(svc.id)
if manifest is None:
continue
+5
View File
@@ -162,3 +162,8 @@ def get_service_url(service_id: str) -> str | None:
if svc.id == service_id:
return svc.internal_url
return None
def get_registry() -> list[ServiceDefinition]:
"""Return the current service registry (always up-to-date after register_services)."""
return _REGISTRY
@@ -25,7 +25,7 @@ import uuid
from pathlib import Path
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from watchdog.observers.polling import PollingObserver
from app.database import AsyncSessionLocal
from app.models.category import DocumentCategory
@@ -226,7 +226,7 @@ class FileWatcherService:
return
handler = _PdfEventHandler(self._watch_root, self._loop, config)
self._observer = Observer()
self._observer = PollingObserver()
self._observer.schedule(handler, watch_path, recursive=True)
self._observer.start()
logger.info("[watcher] started, watching %s", watch_path)