Skip to content

runtime settings, DIMSE split, legacy API compatibility, and Docker integration - #46

Merged
luis-godinho merged 12 commits into
backend/implementationfrom
backend/runtime-settings
Jun 30, 2026
Merged

luis-godinho merged 12 commits into
backend/implementationfrom
backend/runtime-settings

Conversation

@luis-godinho

Copy link
Copy Markdown
Contributor

Summary

Adds a complete runtime settings management system, splits DIMSE storage from query-retrieve into independent servers, implements legacy Dicoogle API compatibility endpoints, and provides a Docker Compose setup for the full stack.
Changes

Runtime Settings

  • YAML-based persistent settings store (RuntimeSettingsService, YamlSettingsStore)
  • Management endpoints for AE title, move destinations, and storage configuration
  • Settings survive restarts via ./data/runtime-settings.yml

DIMSE Server Split

  • DimseCStoreServer (C-STORE + Echo only, port 6666)
  • DimseQueryRetrieveServer (C-FIND + C-MOVE, port 1045) — new class
  • Both servers always created, start/stop controlled at runtime via settings
  • Shared AE title across both servers

Transfer Syntax Management

  • TransferOptionsController loads all SOP classes and transfer syntaxes from dcm4che5 UIDNames.properties instead of hardcoded maps
  • YamlDimseTransferCapabilityStore persists accepted transfer capabilities to ./data/transfer-capabilities.yml with atomic writes
  • POST/PUT/DELETE endpoints for runtime transfer syntax configuration

Legacy API Compatibility

  • GET /dic2png — DICOM to PNG conversion (public endpoint)
  • GET/POST /index/task — legacy-compatible task management with live watcher
  • POST /management/tasks/index — async index/unindex by URI
  • GET /management/settings/dimse/transfer — legacy-compatible transfer options
  • GET /management/settings/storage/dicom — move destinations

DICOMweb

  • DicoogleApiController — QIDO-RS and WADO-RS endpoints
  • SearchController — search via legacy proxy
    Filesystem Listing
  • FilesystemController — GET /indexer?action=pathcontents for secure directory browsing
  • Path traversal protection, symlink rejection, max entries cap
  • Public endpoint for webapp file browser

Docker

  • Root docker-compose.yml — backend + frontend (multi-stage build)
  • docker-test/docker-compose.yml — backend + legacy Dicoogle for integration testing
  • Webapp Dockerfile now builds frontend with Vite (multi-stage)

Bugfixes

  • Wire /management/dicom/query to actual DIMSE server
  • Fix DICOM to PNG conversion in /dic2png
  • Update OpenAPI security scheme from basicAuth to bearerAuth
  • Fix storage server field names in settings responses

- YAML-backed RuntimeSettings store at ./data/runtime-settings.yml
- RuntimeSettingsService to persist and live-apply DIMSE C-STORE config
- /management/dicom/storage GET/POST - start/stop C-STORE, change port/hostname
- /management/dicom/query GET/POST - mirror legacy shape (same server)
- /management/settings/dicom GET/PUT - AE title
- /management/settings/storage/dicom GET/POST - move destinations
- /management/settings/transfer GET/POST - transfer capability toggle
- CMoveService reads move destination from live properties (not snapshot)
- Jackson YAML dependency added
Add QueryIndexSettings interface to the SDK so index settings (path,
watcher) are plugin-agnostic. Implement it in LuceneQueryIndexPlugin
and wire it through IndexSettingsController at /management/settings/index.

Remove unused stub endpoints (zip, effort, thumbnail, thumbnail/size).
…API and live watcher toggle

Phase 3b: Runtime task management (/index/task)
- IndexTaskController: GET returns {results, count} with legacy field
  names (taskUid, taskName, taskProgress float, taskTimeCreated,
  complete/canceled, elapsedTime, nIndexed, nErrors)
- POST uses query params action=delete&type=close|stop&uid=X
  matching RunningTasksServlet contract
- IndexTaskService: UUID task IDs, CachedThreadPool execution,
  float progress tracking, elapsedTime/nIndexed/nErrors reporting
- QueryIndexController: reinit/index/unindex now submit async tasks
  and return {taskUid} instead of blocking synchronously
- IndexTaskControllerTest: 11 tests covering auth, response shapes,
  param validation, and task lifecycle

Runtime LuceneStorageWatcher toggle:
- Removed permanent enabled flag from LuceneStorageWatcher
- setWatchEnabled() now calls watcher.start()/stop() at runtime
- PUT /management/settings/index/watcher now works live

Also: added getPlugin/getAllPlugins to QueryIndexMaintenanceService
- Backend Dockerfile: multi-stage with Maven build and JRE runtime
- Frontend dockerfile: simplified to copy pre-built dist (no npm in Docker)
- docker-compose.yml: backend (spring-boot :8080) + frontend (nginx :3000)
- nginx.conf: proxy /next/api/ → http://backend:8080/api/ (preserves /api prefix)
- Frontend VITE_API_BASE_URL=/next/api, backend SPRING_PROFILES_ACTIVE=dev
- Runtime data volume for persistence across restarts
- .dockerignore files for both backend and webapp
- Replace basicAuth SecurityScheme with bearerAuth (JWT) in OpenApiConfig
- Update @securityrequirement(name = "basicAuth") to "bearerAuth" in all controllers
- No functional change — annotations are Swagger docs only
… Docker integration

- Add POST /management/tasks/index for triggering indexing via URI
- Add GET/POST /management/settings/dicom/query for DICOM query settings
- Add GET /logger returning root log level
- Add GET /ext/version endpoint (permitAll)
- Add POST /plugins/{type}/{id}/disable and enable for plugin toggling
- Fix PluginController: align /plugins response format with legacy
- Fix TokenAuthenticationFilter: accept raw token without Bearer prefix
- Fix LoginController.logout(): accept raw token without Bearer prefix
- Fix SearchController: handle *:* wildcard query in keyword mode
- Add error logging to ApiExceptionHandler catch-all handler
- Add nginx resolver + variable proxy_pass for runtime DNS resolution
- Mount /home/jotalma13/FELIX into backend container for indexing access
…ram auth

- Add GET /legacy/file?uid=... returning file URI for a SOPInstanceUID
- Add GET /dic2png returning raw DICOM bytes (needs rendering library for PNG)
- Add retrieveInstanceBySopUid convenience method to DicomwebRetrieveService
- Support token query parameter in TokenAuthenticationFilter for img-tag auth
…umbnails

- Change /legacy/file to return raw DICOM bytes instead of JSON URI
  (Cornerstone wadouri: loader expects DICOM data, not JSON)
- Make /dic2png permitAll so thumbnail img tags work without auth
- Remove unused Map import
- Add dcm4che-imageio 5.34.0 dependency for DICOM pixel data decoding
- Rewrite dic2png to decode DICOM pixel data via ImageIO/dcm4che plugin
  and encode as PNG (image/png), matching legacy Dicoogle behavior
- Support thumbnail scaling (256px max dimension)
- Support frame parameter for multi-frame DICOM
- Return 404/500 errors on missing image or conversion failure
… server field names

- /management/dicom/query now returns/updates the same DIMSE server state
  as /management/dicom/storage (C-FIND runs on the same server as C-STORE)
- Fix StorageDestinationsController JSON field names to match frontend
  expectations: AETitle, ipAddrs, isPublic (was aeTitle, host, public)
…full-text logger

- DICOM query/retrieve settings (responseTimeout, connectionTimeout, etc.) via AETitleController
- Legacy-compatible task endpoints: /management/tasks/index, /unindex, /remove (with URI params)
- Full-text log file endpoint at /logger, legacy JSON at /logger?action=getadvancedsettings
- Fix missing Map import in CMoveService
- Fix OpenApiConfig conflict markers from rebase
…dd transfer syntax persistence and UIDNames loading

- Split DimseCStoreServer (C-STORE only, port 6666) and DimseQueryRetrieveServer (C-FIND+C-MOVE, port 1045) into independent SmartLifecycle beans
- Add DimseQueryRetrieveProperties and DimseQueryRetrieveServer for QR server config
- Rewrite YamlDimseTransferCapabilityStore to persist to file (./data/transfer-capabilities.yml) with atomic writes
- Rewrite TransferOptionsController to load all SOP classes and transfer syntaxes from dcm4che5 UIDNames.properties instead of hardcoded maps
- Fix TransferOptionsController POST to persist through store instead of modifying in-memory bean
- Add filePath config property to DimseTransferCapabilityConfigProperties
- Update RuntimeSettings/RuntimeSettingsService with QR server settings and shared AE title
- Update DicomServicesController /query to read from QR server instead of C-STORE
- Update test-matrix script and docker-compose for separate C-STORE (11113) and QR (11114) ports
- Remove @ConditionalOnProperty from server beans so they can be started/stopped at runtime
- All 38 unit tests pass, all 5 test-matrix combinations pass
@luis-godinho
luis-godinho merged commit 2cb4c80 into backend/implementation Jun 30, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant