Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
Browse files Browse the repository at this point in the history
…/bump-to-0.35
  • Loading branch information
igoroctaviano committed Nov 21, 2024
2 parents bd430c9 + b328b94 commit 2c8efb3
Show file tree
Hide file tree
Showing 28 changed files with 1,816 additions and 273 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# testing
/coverage
.env

# production
/build
Expand Down
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,87 @@ Users can authenticate and authorize the application to access data via [OpenID

## Configuration

### Server Configuration

The app can be configured via a `public/config/{name}.js` JavaScript configuration file (see for example the default `public/config/local.js`).
Please refer to the [AppConfig.d.ts](src/AppConfig.d.ts) file for configuration options.

The configuration can be changed at build-time using the `REACT_APP_CONFIG` environment variable.

### Handling Mixed Content and HTTPS

When deploying SLIM with HTTPS, you may encounter mixed content scenarios where your PACS/VNA server returns HTTP URLs in its responses. This commonly occurs when:

- The PACS server populates bulkdataURI fields with internal HTTP URLs
- Your viewer is running on HTTPS but needs to communicate with services that respond with HTTP URLs
- You're using a reverse proxy that terminates SSL

To handle these scenarios, SLIM provides the `upgradeInsecureRequests` option in the server configuration:

```js
window.config = {
servers: [{
id: "local",
url: "https://your-server.com/dcm4chee-arc/aets/MYAET/rs",
upgradeInsecureRequests: true // Enable automatic HTTP -> HTTPS upgrade
}]
}
```

When `upgradeInsecureRequests` is set to `true` and at least one of your URLs (service URL, QIDO, WADO, or STOW prefixes) uses HTTPS, the viewer will automatically:

1. Add the `Content-Security-Policy: upgrade-insecure-requests` header to requests
2. Attempt to upgrade any HTTP responses to HTTPS

This feature was implemented in response to [issue #159](https://github.com/ImagingDataCommons/slim/issues/159) where PACS servers would return HTTP bulkdata URIs even when accessed via HTTPS.

### Messages/Popups Configuration

Configure message popup notifications that appear at the top of the screen. By default, all message popups are enabled.

```javascript
window.config = {
// ... other config options ...
messages: {
disabled: ['warning', 'info'], // Disable specific message types
duration: 5, // Show messages for 5 seconds
top: 100 // Show 100px from top of screen
}
}
```

Options:
- `disabled`: Disable specific message types or all messages
- `duration`: How long messages are shown (in seconds)
- `top`: Distance from top of screen (in pixels)

Available message types:
- `success` - Green popups
- `error` - Red popups
- `warning` - Yellow popups
- `info` - Blue popups

Examples:
```javascript
// Disable specific types with custom duration and position
messages: {
disabled: ['warning', 'info'],
duration: 5, // Show for 5 seconds
top: 50 // Show 50px from top
}
```

```javascript
// Disable all popups
messages: {
disabled: true
}
```

Default values if not specified:
- `duration`: 5 seconds
- `top`: 100 pixels

## Deployment

Download the latest release from [github.com/imagingdatacommons/slim/releases](https://github.com/imagingdatacommons/slim/releases) and then run the following commands to install build dependencies and build the app:
Expand Down Expand Up @@ -223,7 +299,6 @@ Create an [OIDC client ID for web application](https://developers.google.com/ide
Note that Google's OIDC implementation does currently not yet support the authorization code grant type with PKCE challenge for private clients.
For the time being, the legacy implicit grand type has to be used.


## Development

To install requirements and run the app for local development, run the following commands:
Expand Down
28 changes: 28 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# [0.35.0](https://github.com/ImagingDataCommons/slim/compare/v0.34.0...v0.35.0) (2024-11-21)


### Features

* bump dmv version ([#254](https://github.com/ImagingDataCommons/slim/issues/254)) ([fe9bb49](https://github.com/ImagingDataCommons/slim/commit/fe9bb496aa503c54ee65ffba86a0cf14b7e7c20f))

# [0.34.0](https://github.com/ImagingDataCommons/slim/compare/v0.33.0...v0.34.0) (2024-11-20)


### Features

* improve dicom tag browser ([#251](https://github.com/ImagingDataCommons/slim/issues/251)) ([7e57859](https://github.com/ImagingDataCommons/slim/commit/7e57859287ea3b758f9d2cf4e53729063c589569))

# [0.33.0](https://github.com/ImagingDataCommons/slim/compare/v0.32.0...v0.33.0) (2024-10-31)


### Features

* dicom tag browser ([#248](https://github.com/ImagingDataCommons/slim/issues/248)) ([177ed8f](https://github.com/ImagingDataCommons/slim/commit/177ed8f6b6e82614f9563eb22584c2c6b8bc8de4))

# [0.32.0](https://github.com/ImagingDataCommons/slim/compare/v0.31.4...v0.32.0) (2024-10-29)


### Features

* remove unused white space ([#247](https://github.com/ImagingDataCommons/slim/issues/247)) ([155e001](https://github.com/ImagingDataCommons/slim/commit/155e0018c2fd8b87eee70c207051de9df55f0ada))

## [0.31.4](https://github.com/ImagingDataCommons/slim/compare/v0.31.3...v0.31.4) (2024-07-29)


Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "slim",
"version": "0.31.4",
"version": "0.35.0",
"homepage": "https://github.com/imagingdatacommons/slim",
"private": true,
"author": "ImagingDataCommons",
Expand Down Expand Up @@ -56,9 +56,9 @@
"classnames": "^2.2.6",
"copy-webpack-plugin": "^10.2.4",
"craco-less": "^2.0.0",
"dcmjs": "^0.29.8",
"dcmjs": "^0.35.0",
"detect-browser": "^5.2.1",
"dicom-microscopy-viewer": "^0.47.0",
"dicom-microscopy-viewer": "^0.47.2",
"dicomweb-client": "^0.10.3",
"gh-pages": "^5.0.0",
"oidc-client": "^1.11.5",
Expand Down
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ class App extends React.Component<AppProps, AppState> {
onServerSelection={this.handleServerSelection}
showServerSelectionButton={false}
appConfig={this.props.config}
clients={this.state.clients}
/>
<Layout.Content style={layoutContentStyle}>
<FaSpinner />
Expand Down
6 changes: 6 additions & 0 deletions src/AppConfig.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export interface ServerSettings {
retry?: RetryRequestSettings
errorMessages?: ErrorMessageSettings[]
storageClasses?: string[]
upgradeInsecureRequests?: boolean
}

export interface OidcSettings {
Expand Down Expand Up @@ -103,4 +104,9 @@ export default interface AppConfig {
mode?: string
preload?: boolean
downloadStudyDialog?: DownloadStudyDialogSettings
messages?: {
disabled?: boolean | string[]
top?: number
duration?: number
}
}
42 changes: 39 additions & 3 deletions src/DicomWebManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as dwc from 'dicomweb-client'
import * as dcmjs from 'dcmjs'
import * as dmv from 'dicom-microscopy-viewer'

import { ServerSettings, DicomWebManagerErrorHandler } from './AppConfig'
import { joinUrl } from './utils/url'
Expand All @@ -7,6 +9,9 @@ import { CustomError, errorTypes } from './utils/CustomError'
import NotificationMiddleware, {
NotificationMiddlewareContext
} from './services/NotificationMiddleware'
import DicomMetadataStore, { Instance } from './services/DICOMMetadataStore'

const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary

interface Store {
id: string
Expand Down Expand Up @@ -58,9 +63,20 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
)
)
}

const hasHttpsUrl = (url?: string): boolean => url?.startsWith('https') ?? false

const clientSettings: dwc.api.DICOMwebClientOptions = {
url: serviceUrl
}

const shouldUpgradeInsecure = serverSettings.upgradeInsecureRequests === true && [
serviceUrl,
serverSettings.qidoPathPrefix,
serverSettings.wadoPathPrefix,
serverSettings.stowPathPrefix
].some(hasHttpsUrl)

if (serverSettings.qidoPathPrefix !== undefined) {
clientSettings.qidoURLPrefix = serverSettings.qidoPathPrefix
}
Expand All @@ -70,6 +86,14 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
if (serverSettings.stowPathPrefix !== undefined) {
clientSettings.stowURLPrefix = serverSettings.stowPathPrefix
}

if (shouldUpgradeInsecure) {
clientSettings.headers = {
...clientSettings.headers,
'Content-Security-Policy': 'upgrade-insecure-requests'
}
}

if (serverSettings.retry !== undefined) {
clientSettings.requestHooks = [getXHRRetryHook(serverSettings.retry)]
}
Expand Down Expand Up @@ -144,13 +168,21 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
retrieveStudyMetadata = async (
options: dwc.api.RetrieveStudyMetadataOptions
): Promise<dwc.api.Metadata[]> => {
return await this.stores[0].client.retrieveStudyMetadata(options)
const studySummaryMetadata = await this.stores[0].client.retrieveStudyMetadata(options)
const naturalized = naturalizeDataset(studySummaryMetadata)
DicomMetadataStore.addStudy(naturalized)
return studySummaryMetadata
}

retrieveSeriesMetadata = async (
options: dwc.api.RetrieveSeriesMetadataOptions
): Promise<dwc.api.Metadata[]> => {
return await this.stores[0].client.retrieveSeriesMetadata(options)
const seriesSummaryMetadata = await this.stores[0].client.retrieveSeriesMetadata(options)
console.debug('seriesSummaryMetadata:', seriesSummaryMetadata)
const naturalized = seriesSummaryMetadata.map(naturalizeDataset)
console.debug('naturalized:', naturalized)
DicomMetadataStore.addSeriesMetadata(naturalized, true)
return seriesSummaryMetadata
}

retrieveInstanceMetadata = async (
Expand All @@ -162,7 +194,11 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
retrieveInstance = async (
options: dwc.api.RetrieveInstanceOptions
): Promise<dwc.api.Dataset> => {
return await this.stores[0].client.retrieveInstance(options)
const instance = await this.stores[0].client.retrieveInstance(options)
const data = dcmjs.data.DicomMessage.readFile(instance)
const { dataset } = dmv.metadata.formatMetadata(data.dict)
DicomMetadataStore.addInstances([dataset as Instance])
return instance
}

retrieveInstanceFrames = async (
Expand Down
2 changes: 1 addition & 1 deletion src/components/AnnotationGroupItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ class AnnotationGroupItem extends React.Component<AnnotationGroupItemProps, Anno
size='small'
disabled={!this.props.isVisible}
>
{}
<></>
</Select.Option>
)

Expand Down
Loading

0 comments on commit 2c8efb3

Please sign in to comment.