Skip to content

Commit afa5d92

Browse files
Merge pull request #114 from ImagingDataCommons/feat/bump-to-0.35
Bump to slim 0.35
2 parents bd430c9 + 2c8efb3 commit afa5d92

28 files changed

+1816
-273
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# testing
1212
/coverage
13+
.env
1314

1415
# production
1516
/build

README.md

+76-1
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,87 @@ Users can authenticate and authorize the application to access data via [OpenID
8181

8282
## Configuration
8383

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

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

91+
### Handling Mixed Content and HTTPS
92+
93+
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:
94+
95+
- The PACS server populates bulkdataURI fields with internal HTTP URLs
96+
- Your viewer is running on HTTPS but needs to communicate with services that respond with HTTP URLs
97+
- You're using a reverse proxy that terminates SSL
98+
99+
To handle these scenarios, SLIM provides the `upgradeInsecureRequests` option in the server configuration:
100+
101+
```js
102+
window.config = {
103+
servers: [{
104+
id: "local",
105+
url: "https://your-server.com/dcm4chee-arc/aets/MYAET/rs",
106+
upgradeInsecureRequests: true // Enable automatic HTTP -> HTTPS upgrade
107+
}]
108+
}
109+
```
110+
111+
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:
112+
113+
1. Add the `Content-Security-Policy: upgrade-insecure-requests` header to requests
114+
2. Attempt to upgrade any HTTP responses to HTTPS
115+
116+
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.
117+
118+
### Messages/Popups Configuration
119+
120+
Configure message popup notifications that appear at the top of the screen. By default, all message popups are enabled.
121+
122+
```javascript
123+
window.config = {
124+
// ... other config options ...
125+
messages: {
126+
disabled: ['warning', 'info'], // Disable specific message types
127+
duration: 5, // Show messages for 5 seconds
128+
top: 100 // Show 100px from top of screen
129+
}
130+
}
131+
```
132+
133+
Options:
134+
- `disabled`: Disable specific message types or all messages
135+
- `duration`: How long messages are shown (in seconds)
136+
- `top`: Distance from top of screen (in pixels)
137+
138+
Available message types:
139+
- `success` - Green popups
140+
- `error` - Red popups
141+
- `warning` - Yellow popups
142+
- `info` - Blue popups
143+
144+
Examples:
145+
```javascript
146+
// Disable specific types with custom duration and position
147+
messages: {
148+
disabled: ['warning', 'info'],
149+
duration: 5, // Show for 5 seconds
150+
top: 50 // Show 50px from top
151+
}
152+
```
153+
154+
```javascript
155+
// Disable all popups
156+
messages: {
157+
disabled: true
158+
}
159+
```
160+
161+
Default values if not specified:
162+
- `duration`: 5 seconds
163+
- `top`: 100 pixels
164+
89165
## Deployment
90166

91167
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:
@@ -223,7 +299,6 @@ Create an [OIDC client ID for web application](https://developers.google.com/ide
223299
Note that Google's OIDC implementation does currently not yet support the authorization code grant type with PKCE challenge for private clients.
224300
For the time being, the legacy implicit grand type has to be used.
225301

226-
227302
## Development
228303

229304
To install requirements and run the app for local development, run the following commands:

docs/CHANGELOG.md

+28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
# [0.35.0](https://github.com/ImagingDataCommons/slim/compare/v0.34.0...v0.35.0) (2024-11-21)
2+
3+
4+
### Features
5+
6+
* bump dmv version ([#254](https://github.com/ImagingDataCommons/slim/issues/254)) ([fe9bb49](https://github.com/ImagingDataCommons/slim/commit/fe9bb496aa503c54ee65ffba86a0cf14b7e7c20f))
7+
8+
# [0.34.0](https://github.com/ImagingDataCommons/slim/compare/v0.33.0...v0.34.0) (2024-11-20)
9+
10+
11+
### Features
12+
13+
* improve dicom tag browser ([#251](https://github.com/ImagingDataCommons/slim/issues/251)) ([7e57859](https://github.com/ImagingDataCommons/slim/commit/7e57859287ea3b758f9d2cf4e53729063c589569))
14+
15+
# [0.33.0](https://github.com/ImagingDataCommons/slim/compare/v0.32.0...v0.33.0) (2024-10-31)
16+
17+
18+
### Features
19+
20+
* dicom tag browser ([#248](https://github.com/ImagingDataCommons/slim/issues/248)) ([177ed8f](https://github.com/ImagingDataCommons/slim/commit/177ed8f6b6e82614f9563eb22584c2c6b8bc8de4))
21+
22+
# [0.32.0](https://github.com/ImagingDataCommons/slim/compare/v0.31.4...v0.32.0) (2024-10-29)
23+
24+
25+
### Features
26+
27+
* remove unused white space ([#247](https://github.com/ImagingDataCommons/slim/issues/247)) ([155e001](https://github.com/ImagingDataCommons/slim/commit/155e0018c2fd8b87eee70c207051de9df55f0ada))
28+
129
## [0.31.4](https://github.com/ImagingDataCommons/slim/compare/v0.31.3...v0.31.4) (2024-07-29)
230

331

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "slim",
3-
"version": "0.31.4",
3+
"version": "0.35.0",
44
"homepage": "https://github.com/imagingdatacommons/slim",
55
"private": true,
66
"author": "ImagingDataCommons",
@@ -56,9 +56,9 @@
5656
"classnames": "^2.2.6",
5757
"copy-webpack-plugin": "^10.2.4",
5858
"craco-less": "^2.0.0",
59-
"dcmjs": "^0.29.8",
59+
"dcmjs": "^0.35.0",
6060
"detect-browser": "^5.2.1",
61-
"dicom-microscopy-viewer": "^0.47.0",
61+
"dicom-microscopy-viewer": "^0.47.2",
6262
"dicomweb-client": "^0.10.3",
6363
"gh-pages": "^5.0.0",
6464
"oidc-client": "^1.11.5",

src/App.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ class App extends React.Component<AppProps, AppState> {
453453
onServerSelection={this.handleServerSelection}
454454
showServerSelectionButton={false}
455455
appConfig={this.props.config}
456+
clients={this.state.clients}
456457
/>
457458
<Layout.Content style={layoutContentStyle}>
458459
<FaSpinner />

src/AppConfig.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export interface ServerSettings {
6464
retry?: RetryRequestSettings
6565
errorMessages?: ErrorMessageSettings[]
6666
storageClasses?: string[]
67+
upgradeInsecureRequests?: boolean
6768
}
6869

6970
export interface OidcSettings {
@@ -103,4 +104,9 @@ export default interface AppConfig {
103104
mode?: string
104105
preload?: boolean
105106
downloadStudyDialog?: DownloadStudyDialogSettings
107+
messages?: {
108+
disabled?: boolean | string[]
109+
top?: number
110+
duration?: number
111+
}
106112
}

src/DicomWebManager.ts

+39-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import * as dwc from 'dicomweb-client'
2+
import * as dcmjs from 'dcmjs'
3+
import * as dmv from 'dicom-microscopy-viewer'
24

35
import { ServerSettings, DicomWebManagerErrorHandler } from './AppConfig'
46
import { joinUrl } from './utils/url'
@@ -7,6 +9,9 @@ import { CustomError, errorTypes } from './utils/CustomError'
79
import NotificationMiddleware, {
810
NotificationMiddlewareContext
911
} from './services/NotificationMiddleware'
12+
import DicomMetadataStore, { Instance } from './services/DICOMMetadataStore'
13+
14+
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary
1015

1116
interface Store {
1217
id: string
@@ -58,9 +63,20 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
5863
)
5964
)
6065
}
66+
67+
const hasHttpsUrl = (url?: string): boolean => url?.startsWith('https') ?? false
68+
6169
const clientSettings: dwc.api.DICOMwebClientOptions = {
6270
url: serviceUrl
6371
}
72+
73+
const shouldUpgradeInsecure = serverSettings.upgradeInsecureRequests === true && [
74+
serviceUrl,
75+
serverSettings.qidoPathPrefix,
76+
serverSettings.wadoPathPrefix,
77+
serverSettings.stowPathPrefix
78+
].some(hasHttpsUrl)
79+
6480
if (serverSettings.qidoPathPrefix !== undefined) {
6581
clientSettings.qidoURLPrefix = serverSettings.qidoPathPrefix
6682
}
@@ -70,6 +86,14 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
7086
if (serverSettings.stowPathPrefix !== undefined) {
7187
clientSettings.stowURLPrefix = serverSettings.stowPathPrefix
7288
}
89+
90+
if (shouldUpgradeInsecure) {
91+
clientSettings.headers = {
92+
...clientSettings.headers,
93+
'Content-Security-Policy': 'upgrade-insecure-requests'
94+
}
95+
}
96+
7397
if (serverSettings.retry !== undefined) {
7498
clientSettings.requestHooks = [getXHRRetryHook(serverSettings.retry)]
7599
}
@@ -144,13 +168,21 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
144168
retrieveStudyMetadata = async (
145169
options: dwc.api.RetrieveStudyMetadataOptions
146170
): Promise<dwc.api.Metadata[]> => {
147-
return await this.stores[0].client.retrieveStudyMetadata(options)
171+
const studySummaryMetadata = await this.stores[0].client.retrieveStudyMetadata(options)
172+
const naturalized = naturalizeDataset(studySummaryMetadata)
173+
DicomMetadataStore.addStudy(naturalized)
174+
return studySummaryMetadata
148175
}
149176

150177
retrieveSeriesMetadata = async (
151178
options: dwc.api.RetrieveSeriesMetadataOptions
152179
): Promise<dwc.api.Metadata[]> => {
153-
return await this.stores[0].client.retrieveSeriesMetadata(options)
180+
const seriesSummaryMetadata = await this.stores[0].client.retrieveSeriesMetadata(options)
181+
console.debug('seriesSummaryMetadata:', seriesSummaryMetadata)
182+
const naturalized = seriesSummaryMetadata.map(naturalizeDataset)
183+
console.debug('naturalized:', naturalized)
184+
DicomMetadataStore.addSeriesMetadata(naturalized, true)
185+
return seriesSummaryMetadata
154186
}
155187

156188
retrieveInstanceMetadata = async (
@@ -162,7 +194,11 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
162194
retrieveInstance = async (
163195
options: dwc.api.RetrieveInstanceOptions
164196
): Promise<dwc.api.Dataset> => {
165-
return await this.stores[0].client.retrieveInstance(options)
197+
const instance = await this.stores[0].client.retrieveInstance(options)
198+
const data = dcmjs.data.DicomMessage.readFile(instance)
199+
const { dataset } = dmv.metadata.formatMetadata(data.dict)
200+
DicomMetadataStore.addInstances([dataset as Instance])
201+
return instance
166202
}
167203

168204
retrieveInstanceFrames = async (

src/components/AnnotationGroupItem.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ class AnnotationGroupItem extends React.Component<AnnotationGroupItemProps, Anno
360360
size='small'
361361
disabled={!this.props.isVisible}
362362
>
363-
{}
363+
<></>
364364
</Select.Option>
365365
)
366366

0 commit comments

Comments
 (0)