Merge pull request #420 from dannyrb/docs/orthanc-proxy
Docs/orthanc proxy
@ -1,10 +1,16 @@
|
||||
# Output
|
||||
dist/
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.git
|
||||
|
||||
# Root
|
||||
README.md
|
||||
Dockerfile
|
||||
|
||||
# Misc. Config
|
||||
.git
|
||||
.DS_Store
|
||||
.gitignore
|
||||
README.md
|
||||
.vscode
|
||||
.circleci
|
||||
example/
|
||||
|
||||
48
docker/Nginx-Orthanc/config/nginx.conf
Normal file
@ -0,0 +1,48 @@
|
||||
worker_processes 1;
|
||||
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
|
||||
upstream orthanc-server {
|
||||
server orthanc:8042;
|
||||
}
|
||||
|
||||
server {
|
||||
listen [::]:80 default_server;
|
||||
listen 80;
|
||||
|
||||
# CORS Magic
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
|
||||
location / {
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
|
||||
proxy_pass http://orthanc:8042;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
|
||||
# CORS Magic
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
}
|
||||
}
|
||||
}
|
||||
89
docker/Nginx-Orthanc/config/orthanc.json
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"Name": "Orthanc inside Docker",
|
||||
"StorageDirectory": "/var/lib/orthanc/db",
|
||||
"IndexDirectory": "/var/lib/orthanc/db",
|
||||
"StorageCompression": false,
|
||||
"MaximumStorageSize": 0,
|
||||
"MaximumPatientCount": 0,
|
||||
"LuaScripts": [],
|
||||
"Plugins": ["/usr/share/orthanc/plugins", "/usr/local/share/orthanc/plugins"],
|
||||
"ConcurrentJobs": 2,
|
||||
"HttpServerEnabled": true,
|
||||
"HttpPort": 8042,
|
||||
"HttpDescribeErrors": true,
|
||||
"HttpCompressionEnabled": true,
|
||||
"DicomServerEnabled": true,
|
||||
"DicomAet": "ORTHANC",
|
||||
"DicomCheckCalledAet": false,
|
||||
"DicomPort": 4242,
|
||||
"DefaultEncoding": "Latin1",
|
||||
"DeflatedTransferSyntaxAccepted": true,
|
||||
"JpegTransferSyntaxAccepted": true,
|
||||
"Jpeg2000TransferSyntaxAccepted": true,
|
||||
"JpegLosslessTransferSyntaxAccepted": true,
|
||||
"JpipTransferSyntaxAccepted": true,
|
||||
"Mpeg2TransferSyntaxAccepted": true,
|
||||
"RleTransferSyntaxAccepted": true,
|
||||
"UnknownSopClassAccepted": false,
|
||||
"DicomScpTimeout": 30,
|
||||
|
||||
"RemoteAccessAllowed": true,
|
||||
"SslEnabled": false,
|
||||
"SslCertificate": "certificate.pem",
|
||||
"AuthenticationEnabled": false,
|
||||
"RegisteredUsers": {
|
||||
"test": "test"
|
||||
},
|
||||
"DicomModalities": {},
|
||||
"DicomModalitiesInDatabase": false,
|
||||
"DicomAlwaysAllowEcho": true,
|
||||
"DicomAlwaysAllowStore": true,
|
||||
"DicomCheckModalityHost": false,
|
||||
"DicomScuTimeout": 10,
|
||||
"OrthancPeers": {},
|
||||
"OrthancPeersInDatabase": false,
|
||||
"HttpProxy": "",
|
||||
|
||||
"HttpVerbose": true,
|
||||
|
||||
"HttpTimeout": 10,
|
||||
"HttpsVerifyPeers": true,
|
||||
"HttpsCACertificates": "",
|
||||
"UserMetadata": {},
|
||||
"UserContentType": {},
|
||||
"StableAge": 60,
|
||||
"StrictAetComparison": false,
|
||||
"StoreMD5ForAttachments": true,
|
||||
"LimitFindResults": 0,
|
||||
"LimitFindInstances": 0,
|
||||
"LimitJobs": 10,
|
||||
"LogExportedResources": false,
|
||||
"KeepAlive": true,
|
||||
"TcpNoDelay": true,
|
||||
"HttpThreadsCount": 50,
|
||||
"StoreDicom": true,
|
||||
"DicomAssociationCloseDelay": 5,
|
||||
"QueryRetrieveSize": 10,
|
||||
"CaseSensitivePN": false,
|
||||
"LoadPrivateDictionary": true,
|
||||
"Dictionary": {},
|
||||
"SynchronousCMove": true,
|
||||
"JobsHistorySize": 10,
|
||||
"SaveJobs": true,
|
||||
"OverwriteInstances": false,
|
||||
"MediaArchiveSize": 1,
|
||||
"StorageAccessOnFind": "Always",
|
||||
"MetricsEnabled": true,
|
||||
|
||||
"DicomWeb": {
|
||||
"Enable": true,
|
||||
"Root": "/dicom-web/",
|
||||
"EnableWado": true,
|
||||
"WadoRoot": "/wado",
|
||||
"Host": "127.0.0.1",
|
||||
"Ssl": false,
|
||||
"StowMaxInstances": 10,
|
||||
"StowMaxSize": 10,
|
||||
"QidoCaseSensitive": false
|
||||
}
|
||||
}
|
||||
26
docker/Nginx-Orthanc/docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
proxy:
|
||||
image: nginx:1.15-alpine
|
||||
ports:
|
||||
- 8899:80
|
||||
volumes:
|
||||
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
restart: unless-stopped
|
||||
|
||||
# LINK: https://hub.docker.com/r/jodogne/orthanc-plugins/
|
||||
# TODO: Update to use Postgres
|
||||
# https://github.com/mrts/docker-postgresql-multiple-databases
|
||||
orthanc:
|
||||
image: jodogne/orthanc-plugins:1.5.6
|
||||
hostname: orthanc
|
||||
volumes:
|
||||
# Config
|
||||
- ./config/orthanc.json:/etc/orthanc/orthanc.json:ro
|
||||
# Persist data
|
||||
- ./volumes/orthanc-db/:/var/lib/orthanc/db/
|
||||
# ports:
|
||||
# - '4242:4242' # DICOM
|
||||
# - '8042:8042' # Web
|
||||
restart: unless-stopped
|
||||
0
docker/Nginx-Orthanc/volumes/orthanc-db/.githold
Normal file
2
docker/Nginx-Orthanc/volumes/orthanc-db/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
16
docker/OpenResty-Orthanc-Keycloak/.dockerignore
Normal file
@ -0,0 +1,16 @@
|
||||
# Output
|
||||
dist/
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Root
|
||||
README.md
|
||||
Dockerfile
|
||||
|
||||
# Misc. Config
|
||||
.git
|
||||
.DS_Store
|
||||
.gitignore
|
||||
.vscode
|
||||
.circleci
|
||||
5
docker/OpenResty-Orthanc-Keycloak/.env
Normal file
@ -0,0 +1,5 @@
|
||||
# Docker ENV and ARG Variables
|
||||
# ----------------------------
|
||||
# https://vsupalov.com/docker-arg-env-variable-guide/
|
||||
#
|
||||
#
|
||||
31
docker/OpenResty-Orthanc-Keycloak/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Guide
|
||||
|
||||
Build docker container using:
|
||||
|
||||
- Build: `docker build -t authproxy .`
|
||||
- Tag: `docker tag authproxy:latest authproxy:staging`
|
||||
|
||||
> Start your docker container, and volume mount the directory containing the
|
||||
> `nginx-keycloak.conf` configuration file. We also mount the current directory
|
||||
> under `/usr/share/nginx/html` so any html files in the current directory will
|
||||
> be hosted behind the authenticating proxy. Finally, we mapped port 80 on the
|
||||
> host to port 80 in the container.
|
||||
|
||||
`docker run -d -it -p 80:80 -v $PWD/:/config -v /:/usr/share/nginx/html authproxy -c /config/nginx.conf`
|
||||
docker run -d -it -p 80:80 -v /:/config -v /:/usr/share/nginx/html authproxy -c
|
||||
/config/nginx-keycloak.conf
|
||||
|
||||
## Orthanc
|
||||
|
||||
- Configuration
|
||||
- http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
|
||||
## Keycloak
|
||||
|
||||
### Setup
|
||||
|
||||
Library we use to manage OpenID-Connect `implicit` flow:
|
||||
https://github.com/maxmantz/redux-oidc
|
||||
|
||||
- Set admin user/pass in `docker-compose`
|
||||
- What are realms?
|
||||
259
docker/OpenResty-Orthanc-Keycloak/config/nginx.conf
Normal file
@ -0,0 +1,259 @@
|
||||
worker_processes 2;
|
||||
error_log /var/logs/nginx/mydomain.error.log;
|
||||
pid /var/run/nginx.pid;
|
||||
include /usr/share/nginx/modules/*.conf; # See /usr/share/doc/nginx/README.dynamic.
|
||||
|
||||
events {
|
||||
worker_connections 1024; ## Default: 1024
|
||||
use epoll; # http://nginx.org/en/docs/events.html
|
||||
multi_accept on; # http://nginx.org/en/docs/ngx_core_module.html#multi_accept
|
||||
}
|
||||
|
||||
# Core Modules Docs:
|
||||
# http://nginx.org/en/docs/http/ngx_http_core_module.html
|
||||
http {
|
||||
include '/usr/local/openresty/nginx/conf/mime.types';
|
||||
default_type application/octet-stream;
|
||||
|
||||
keepalive_timeout 65;
|
||||
keepalive_requests 100000;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
|
||||
# lua_ settings
|
||||
#
|
||||
lua_package_path '/usr/local/openresty/lualib/?.lua;;';
|
||||
lua_shared_dict discovery 1m; # cache for discovery metadata documents
|
||||
lua_shared_dict jwks 1m; # cache for JWKs
|
||||
# lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
|
||||
|
||||
variables_hash_max_size 2048;
|
||||
server_names_hash_bucket_size 128;
|
||||
server_tokens off;
|
||||
|
||||
resolver 8.8.8.8 valid=30s ipv6=off;
|
||||
resolver_timeout 11s;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
# No idea what this is doing
|
||||
# https://stackoverflow.com/a/5877989/1867984
|
||||
# upstream upstream_server {
|
||||
# # server 10.100.4.200:1010 max_fails=3 fail_timeout=30s;
|
||||
# server 127.0.0.1:
|
||||
# }
|
||||
|
||||
# Nginx `listener` block
|
||||
server {
|
||||
listen [::]:80 default_server;
|
||||
listen 80;
|
||||
# listen 443 ssl;
|
||||
access_log /var/logs/nginx/mydomain.access.log;
|
||||
|
||||
# Domain to protect
|
||||
server_name 127.0.0.1 localhost; # mydomain.com;
|
||||
proxy_intercept_errors off;
|
||||
# ssl_certificate /etc/letsencrypt/live/mydomain.co.uk/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/mydomain.co.uk/privkey.pem;
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||
gzip_comp_level 9;
|
||||
etag on;
|
||||
|
||||
# https://github.com/bungle/lua-resty-session/issues/15
|
||||
set $session_check_ssi off;
|
||||
lua_code_cache off;
|
||||
set $session_secret Eeko7aeb6iu5Wohch9Loo1aitha0ahd1;
|
||||
set $session_storage cookie;
|
||||
|
||||
server_tokens off; # Hides server version num
|
||||
|
||||
# [PROTECTED] Reverse Proxy for `orthanc` admin
|
||||
#
|
||||
location /pacs-admin/ {
|
||||
access_by_lua_block {
|
||||
local opts = {
|
||||
redirect_uri = "http://127.0.0.1/pacs-admin/admin",
|
||||
discovery = "http://127.0.0.1/auth/realms/ohif/.well-known/openid-configuration",
|
||||
token_endpoint_auth_method = "client_secret_basic",
|
||||
client_id = "pacs",
|
||||
client_secret = "66279641-eba6-47f5-9fdb-70c4ac74d548",
|
||||
client_jwt_assertion_expires_in = 60 * 60,
|
||||
ssl_verify = "no",
|
||||
scope = "openid email profile",
|
||||
refresh_session_interval = 900,
|
||||
redirect_uri_scheme = "http",
|
||||
redirect_after_logout_uri = "/",
|
||||
session_contents = {id_token=true}
|
||||
}
|
||||
|
||||
-- call authenticate for OpenID Connect user authentication
|
||||
local res, err = require("resty.openidc").authenticate(opts)
|
||||
|
||||
if err or not res then
|
||||
ngx.print(err)
|
||||
ngx.status = 200
|
||||
ngx.say(err and err or "no access_token provided")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
|
||||
-- Or set cookie?
|
||||
-- ngx.req.set_header("Authorization", "Bearer " .. res.access_token)
|
||||
ngx.req.set_header("X-USER", res.id_token.sub)
|
||||
}
|
||||
|
||||
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
expires 0;
|
||||
add_header Cache-Control private;
|
||||
|
||||
proxy_pass http://orthanc:8042/;
|
||||
}
|
||||
|
||||
# [PROTECTED] Reverse Proxy for `orthanc` APIs (including DICOMWeb)
|
||||
#
|
||||
location /pacs/ {
|
||||
access_by_lua_block {
|
||||
local opts = {
|
||||
discovery = "http://127.0.0.1/auth/realms/ohif/.well-known/openid-configuration",
|
||||
}
|
||||
|
||||
-- call bearer_jwt_verify for OAuth 2.0 JWT validation
|
||||
local res, err = require("resty.openidc").bearer_jwt_verify(opts)
|
||||
|
||||
if err or not res then
|
||||
ngx.status = 403
|
||||
ngx.say(err and err or "no access_token provided")
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
}
|
||||
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
|
||||
expires 0;
|
||||
add_header Cache-Control private;
|
||||
|
||||
proxy_pass http://orthanc:8042/;
|
||||
|
||||
# By default, this endpoint is protected by CORS (cross-origin-resource-sharing)
|
||||
# You can add headers to allow other domains to request this resource.
|
||||
# See the "Updating CORS Settings" example below
|
||||
}
|
||||
|
||||
# Keycloak
|
||||
#
|
||||
location /auth/ {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_pass http://keycloak:8080/auth/;
|
||||
}
|
||||
|
||||
# Do not cache sw.js, required for offline-first updates.
|
||||
location /sw.js {
|
||||
add_header Cache-Control "no-cache";
|
||||
proxy_cache_bypass $http_pragma;
|
||||
proxy_cache_revalidate on;
|
||||
expires off;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Single Page App
|
||||
# Try files, fallback to index.html
|
||||
#
|
||||
location / {
|
||||
alias /var/www/html/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
}
|
||||
|
||||
# EXAMPLE: Reverse Proxy, no auth
|
||||
# [UNPROTECTED] reverse proxy for `orthanc`
|
||||
#
|
||||
# location /pacs/ {
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $remote_addr;
|
||||
# proxy_set_header Host $host;
|
||||
#
|
||||
# proxy_pass http://orthanc:8042/;
|
||||
#
|
||||
# # OR
|
||||
# # rewrite ^/pacs(.*) /$1 break;
|
||||
# # proxy_pass http://orthanc:8042;
|
||||
# }
|
||||
|
||||
# EXAMPLE: Modifying headers to allow requests from other domains
|
||||
# IE. Updating CORS settings
|
||||
#
|
||||
# location / {
|
||||
# if ($request_method = 'OPTIONS') {
|
||||
# add_header 'Access-Control-Allow-Origin' '*';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
# #
|
||||
# # Custom headers and headers various browsers *should* be OK with but aren't
|
||||
# #
|
||||
# add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
# #
|
||||
# # Tell client that this pre-flight info is valid for 20 days
|
||||
# #
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization';
|
||||
# add_header 'Access-Control-Allow-Credentials' true;
|
||||
# add_header 'Access-Control-Max-Age' 1728000;
|
||||
# add_header 'Content-Length' 0;
|
||||
# return 204;
|
||||
# }
|
||||
# if ($request_method = 'POST') {
|
||||
# add_header 'Access-Control-Allow-Origin' '*';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
# add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
# add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
# }
|
||||
# if ($request_method = 'GET') {
|
||||
# add_header 'Access-Control-Allow-Origin' '*';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
# add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
# add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization';
|
||||
# add_header 'Access-Control-Allow-Credentials' true;
|
||||
# }
|
||||
#
|
||||
# # proxy_http_version 1.1;
|
||||
#
|
||||
# # proxy_set_header Host $host;
|
||||
# # proxy_set_header X-Real-IP $remote_addr;
|
||||
# # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# # proxy_set_header X-Forwarded-Proto $scheme;
|
||||
#
|
||||
# proxy_pass http://orthanc:8042;
|
||||
# }
|
||||
|
||||
# EXAMPLE: Redirect server error pages to the static page /40x.html
|
||||
#
|
||||
# error_page 404 /404.html;
|
||||
# location = /40x.html {
|
||||
# }
|
||||
|
||||
# EXAMPLE: Redirect server error pages to the static page /50x.html
|
||||
#
|
||||
# error_page 500 502 503 504 /50x.html;
|
||||
# location = /50x.html {
|
||||
# }
|
||||
}
|
||||
}
|
||||
1876
docker/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json
Normal file
89
docker/OpenResty-Orthanc-Keycloak/config/orthanc.json
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"Name": "Orthanc inside Docker",
|
||||
"StorageDirectory": "/var/lib/orthanc/db",
|
||||
"IndexDirectory": "/var/lib/orthanc/db",
|
||||
"StorageCompression": false,
|
||||
"MaximumStorageSize": 0,
|
||||
"MaximumPatientCount": 0,
|
||||
"LuaScripts": [],
|
||||
"Plugins": ["/usr/share/orthanc/plugins", "/usr/local/share/orthanc/plugins"],
|
||||
"ConcurrentJobs": 2,
|
||||
"HttpServerEnabled": true,
|
||||
"HttpPort": 8042,
|
||||
"HttpDescribeErrors": true,
|
||||
"HttpCompressionEnabled": true,
|
||||
"DicomServerEnabled": true,
|
||||
"DicomAet": "ORTHANC",
|
||||
"DicomCheckCalledAet": false,
|
||||
"DicomPort": 4242,
|
||||
"DefaultEncoding": "Latin1",
|
||||
"DeflatedTransferSyntaxAccepted": true,
|
||||
"JpegTransferSyntaxAccepted": true,
|
||||
"Jpeg2000TransferSyntaxAccepted": true,
|
||||
"JpegLosslessTransferSyntaxAccepted": true,
|
||||
"JpipTransferSyntaxAccepted": true,
|
||||
"Mpeg2TransferSyntaxAccepted": true,
|
||||
"RleTransferSyntaxAccepted": true,
|
||||
"UnknownSopClassAccepted": false,
|
||||
"DicomScpTimeout": 30,
|
||||
|
||||
"RemoteAccessAllowed": true,
|
||||
"SslEnabled": false,
|
||||
"SslCertificate": "certificate.pem",
|
||||
"AuthenticationEnabled": false,
|
||||
"RegisteredUsers": {
|
||||
"test": "test"
|
||||
},
|
||||
"DicomModalities": {},
|
||||
"DicomModalitiesInDatabase": false,
|
||||
"DicomAlwaysAllowEcho": true,
|
||||
"DicomAlwaysAllowStore": true,
|
||||
"DicomCheckModalityHost": false,
|
||||
"DicomScuTimeout": 10,
|
||||
"OrthancPeers": {},
|
||||
"OrthancPeersInDatabase": false,
|
||||
"HttpProxy": "",
|
||||
|
||||
"HttpVerbose": true,
|
||||
|
||||
"HttpTimeout": 10,
|
||||
"HttpsVerifyPeers": true,
|
||||
"HttpsCACertificates": "",
|
||||
"UserMetadata": {},
|
||||
"UserContentType": {},
|
||||
"StableAge": 60,
|
||||
"StrictAetComparison": false,
|
||||
"StoreMD5ForAttachments": true,
|
||||
"LimitFindResults": 0,
|
||||
"LimitFindInstances": 0,
|
||||
"LimitJobs": 10,
|
||||
"LogExportedResources": false,
|
||||
"KeepAlive": true,
|
||||
"TcpNoDelay": true,
|
||||
"HttpThreadsCount": 50,
|
||||
"StoreDicom": true,
|
||||
"DicomAssociationCloseDelay": 5,
|
||||
"QueryRetrieveSize": 10,
|
||||
"CaseSensitivePN": false,
|
||||
"LoadPrivateDictionary": true,
|
||||
"Dictionary": {},
|
||||
"SynchronousCMove": true,
|
||||
"JobsHistorySize": 10,
|
||||
"SaveJobs": true,
|
||||
"OverwriteInstances": false,
|
||||
"MediaArchiveSize": 1,
|
||||
"StorageAccessOnFind": "Always",
|
||||
"MetricsEnabled": true,
|
||||
|
||||
"DicomWeb": {
|
||||
"Enable": true,
|
||||
"Root": "/dicom-web/",
|
||||
"EnableWado": true,
|
||||
"WadoRoot": "/wado",
|
||||
"Host": "127.0.0.1",
|
||||
"Ssl": false,
|
||||
"StowMaxInstances": 10,
|
||||
"StowMaxSize": 10,
|
||||
"QidoCaseSensitive": false
|
||||
}
|
||||
}
|
||||
95
docker/OpenResty-Orthanc-Keycloak/docker-compose.yml
Normal file
@ -0,0 +1,95 @@
|
||||
# Reference:
|
||||
# - https://docs.docker.com/compose/compose-file
|
||||
# - https://eclipsesource.com/blogs/2018/01/11/authenticating-reverse-proxy-with-keycloak/
|
||||
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
# Exposed server that's handling incoming web requests
|
||||
# Underlying image: openresty/openresty:alpine-fat
|
||||
ohif_viewer:
|
||||
build:
|
||||
# Project root
|
||||
context: ./../../
|
||||
# Relative to context
|
||||
dockerfile: ./docker/OpenResty-Orthanc-Keycloak/dockerfile
|
||||
image: webapp:latest
|
||||
container_name: webapp
|
||||
volumes:
|
||||
# Nginx config
|
||||
- ./config/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
|
||||
# Logs
|
||||
- ./logs/nginx:/var/logs/nginx
|
||||
# Let's Encrypt
|
||||
# - letsencrypt_certificates:/etc/letsencrypt
|
||||
# - letsencrypt_challenges:/var/www/letsencrypt
|
||||
ports:
|
||||
- '443:443' # SSL
|
||||
- '80:80' # Web
|
||||
depends_on:
|
||||
- keycloak
|
||||
- orthanc
|
||||
restart: on-failure
|
||||
|
||||
# LINK: https://hub.docker.com/r/jodogne/orthanc-plugins/
|
||||
# TODO: Update to use Postgres
|
||||
# https://github.com/mrts/docker-postgresql-multiple-databases
|
||||
orthanc:
|
||||
image: jodogne/orthanc-plugins:1.5.6
|
||||
hostname: orthanc
|
||||
container_name: orthanc
|
||||
volumes:
|
||||
# Config
|
||||
- ./config/orthanc.json:/etc/orthanc/orthanc.json:ro
|
||||
# Persist data
|
||||
- ./volumes/orthanc-db/:/var/lib/orthanc/db/
|
||||
restart: unless-stopped
|
||||
|
||||
# LINK: https://hub.docker.com/r/jboss/keycloak
|
||||
keycloak:
|
||||
image: jboss/keycloak:6.0.1
|
||||
hostname: keycloak
|
||||
container_name: keycloak
|
||||
volumes:
|
||||
# Theme: https://www.keycloak.org/docs/latest/server_development/index.html#_themes
|
||||
- ./volumes/keycloak-themes/ohif:/opt/jboss/keycloak/themes/ohif
|
||||
# Previous Realm Config
|
||||
- ./config/ohif-keycloak-realm.json:/tmp/ohif-keycloak-realm.json
|
||||
environment:
|
||||
# Database
|
||||
DB_VENDOR: postgres
|
||||
DB_ADDR: postgres
|
||||
DB_PORT: 5432
|
||||
DB_DATABASE: keycloak
|
||||
DB_SCHEMA: public
|
||||
DB_USER: keycloak
|
||||
DB_PASSWORD: password
|
||||
# Keycloak
|
||||
KEYCLOAK_USER: admin
|
||||
KEYCLOAK_PASSWORD: password
|
||||
KEYCLOAK_IMPORT: /tmp/ohif-keycloak-realm.json
|
||||
# KEYCLOAK_WELCOME_THEME: <theme-name>
|
||||
# KEYCLOAK_DEFAULT_THEME: <theme-name>
|
||||
# KEYCLOAK_HOSTNAME: (recommended in prod)
|
||||
# KEYCLOAK_LOGLEVEL: DEBUG
|
||||
PROXY_ADDRESS_FORWARDING: 'true'
|
||||
depends_on:
|
||||
- postgres
|
||||
restart: unless-stopped
|
||||
|
||||
# LINK: https://hub.docker.com/_/postgres/
|
||||
postgres:
|
||||
image: postgres:11.2
|
||||
hostname: postgres
|
||||
container_name: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: password
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
65
docker/OpenResty-Orthanc-Keycloak/dockerfile
Normal file
@ -0,0 +1,65 @@
|
||||
# docker-compose
|
||||
# --------------
|
||||
# This dockerfile is used by the `docker-compose.yml` adjacent file. When
|
||||
# running `docker-compose build`, this dockerfile helps build the "webapp" image.
|
||||
# All paths are relative to the `context`, which is the project root directory.
|
||||
#
|
||||
# docker build
|
||||
# --------------
|
||||
# If you would like to use this dockerfile to build and tag an image, make sure
|
||||
# you set the context to the project's root directory:
|
||||
# https://docs.docker.com/engine/reference/commandline/build/
|
||||
#
|
||||
#
|
||||
# SUMMARY
|
||||
# --------------
|
||||
# This dockerfile has two stages:
|
||||
#
|
||||
# 1. Building the React application for production
|
||||
# 2. Setting up our Nginx (OpenResty*) image w/ step one's output
|
||||
#
|
||||
# * OpenResty is functionally identical to Nginx with the addition of Lua out of
|
||||
# the box.
|
||||
|
||||
|
||||
# Stage 1: Build the application
|
||||
FROM node:11.2.0-slim as builder
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
ENV REACT_APP_CONFIG=config/docker_openresty-orthanc-keycloak.js
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
|
||||
COPY package.json /usr/src/app/package.json
|
||||
COPY yarn.lock /usr/src/app/yarn.lock
|
||||
|
||||
ADD . /usr/src/app/
|
||||
RUN yarn install
|
||||
RUN yarn run build:web
|
||||
|
||||
# Stage 2: Bundle the built application into a Docker container
|
||||
# which runs openresty (nginx) using Alpine Linux
|
||||
# LINK: https://hub.docker.com/r/openresty/openresty
|
||||
FROM openresty/openresty:1.15.8.1rc1-0-alpine-fat
|
||||
|
||||
RUN mkdir /var/log/nginx
|
||||
RUN apk add --no-cache openssl
|
||||
RUN apk add --no-cache openssl-dev
|
||||
RUN apk add --no-cache git
|
||||
RUN apk add --no-cache gcc
|
||||
# !!!
|
||||
RUN luarocks install lua-resty-openidc
|
||||
|
||||
#
|
||||
RUN luarocks install lua-resty-jwt
|
||||
RUN luarocks install lua-resty-session
|
||||
RUN luarocks install lua-resty-http
|
||||
# !!!
|
||||
RUN luarocks install lua-resty-openidc
|
||||
RUN luarocks install luacrypto
|
||||
|
||||
# Copy build output to image
|
||||
COPY --from=builder /usr/src/app/build /var/www/html
|
||||
|
||||
ENTRYPOINT ["/usr/local/openresty/nginx/sbin/nginx", "-g", "daemon off;"]
|
||||
@ -0,0 +1,212 @@
|
||||
body {
|
||||
background-color: #040507;
|
||||
background-image: url('../img/background.jpg');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
color: #fff;
|
||||
font-family: sans-serif;
|
||||
text-shadow: 0px 0px 10px #000;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
div#kc-content {
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
left: 50%;
|
||||
width: 550px;
|
||||
margin-left: -180px;
|
||||
}
|
||||
|
||||
div#kc-form {
|
||||
float: left;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
div#kc-form label {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
div#info-area {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin-top: 40px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div#info-area p {
|
||||
margin-right: 30px;
|
||||
display: inline;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
input[type='password'] {
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
background-color: rgba(256, 256, 256, 0.7);
|
||||
border: 0px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: inset 0 0 2px 2px rgba(0, 0, 0, 0.15);
|
||||
padding: 10px;
|
||||
width: 296px;
|
||||
}
|
||||
|
||||
input[type='text']:hover,
|
||||
input[type='password']:hover {
|
||||
background-color: rgba(256, 256, 256, 0.9);
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
border: none;
|
||||
|
||||
background: -webkit-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
);
|
||||
background: -moz-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
);
|
||||
background: -ms-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
);
|
||||
background: -o-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
);
|
||||
|
||||
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.5);
|
||||
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
margin-right: 10px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
input[type='submit']:hover {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
div#kc-form-options div {
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
div#kc-form-options div label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
div#kc-feedback {
|
||||
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div#kc-feedback-wrapper {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
div.feedback-success {
|
||||
background-color: rgba(155, 155, 255, 0.1);
|
||||
}
|
||||
|
||||
div.feedback-warning {
|
||||
background-color: rgba(255, 175, 0, 0.1);
|
||||
}
|
||||
|
||||
div.feedback-error {
|
||||
background-color: rgba(255, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
div#kc-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div#kc-registration {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
div#social-login {
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.2);
|
||||
float: right;
|
||||
width: 150px;
|
||||
padding: 20px 0 200px 40px;
|
||||
}
|
||||
|
||||
div.social-login span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div#kc-social-providers ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div#kc-social-providers ul li {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
div#kc-social-providers ul li span {
|
||||
display: inline;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
a.zocial {
|
||||
border: none;
|
||||
background: -webkit-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
) !important;
|
||||
background: -moz-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
) !important;
|
||||
background: -ms-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
) !important;
|
||||
background: -o-linear-gradient(
|
||||
top,
|
||||
rgba(255, 255, 255, 0.8),
|
||||
rgba(255, 255, 255, 0.1)
|
||||
) !important;
|
||||
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.5);
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
width: 130px;
|
||||
text-shadow: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.0 MiB |
@ -0,0 +1,3 @@
|
||||
parent=base
|
||||
import=common/keycloak
|
||||
styles=lib/zocial/zocial.css css/styles.css
|
||||
2
docker/OpenResty-Orthanc-Keycloak/volumes/orthanc-db/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@ -1,39 +0,0 @@
|
||||
# docker-compose:
|
||||
# https://docs.docker.com/compose/overview/
|
||||
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
orthanc:
|
||||
container_name: orthanc
|
||||
image: jodogne/orthanc-plugins
|
||||
volumes:
|
||||
- ../sampledata:/sampledata
|
||||
ports:
|
||||
- '4242:4242'
|
||||
- '8042:8042'
|
||||
restart: always
|
||||
viewer:
|
||||
container_name: ohif-viewer
|
||||
build:
|
||||
# Up to the root directory
|
||||
context: ../
|
||||
# Use our dockerfile to build the viewer
|
||||
dockerfile: dockerfile
|
||||
ports:
|
||||
- '80:80'
|
||||
depends_on:
|
||||
- orthanc
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- REACT_APP_CONFIG=config/local_orthanc.js
|
||||
restart: always
|
||||
volumes:
|
||||
- ../config/local_orthanc.js:/usr/share/nginx/html/servers.js
|
||||
proxy:
|
||||
image: nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 8899:80
|
||||
volumes:
|
||||
- ./nginx-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
@ -1,41 +0,0 @@
|
||||
{
|
||||
"apps" : [{
|
||||
"name" : "ohif-viewer",
|
||||
"script" : "main.js",
|
||||
"watch" : true,
|
||||
"merge_logs" : true,
|
||||
"cwd" : "/app/bundle/",
|
||||
"env": {
|
||||
"METEOR_SETTINGS": {
|
||||
"servers": {
|
||||
"dicomWeb": [
|
||||
{
|
||||
"name": "Orthanc",
|
||||
"wadoUriRoot": "http://pacsIP:8042/wado",
|
||||
"qidoRoot": "http://pacsIP:8042/dicom-web",
|
||||
"wadoRoot": "http://pacsIP:8042/dicom-web",
|
||||
"qidoSupportsIncludeField": false,
|
||||
"imageRendering": "wadouri",
|
||||
"thumbnailRendering": "wadouri",
|
||||
"requestOptions": {
|
||||
"auth": "orthanc:orthanc",
|
||||
"logRequests": true,
|
||||
"logResponses": false,
|
||||
"logTiming": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"defaultServiceType": "dicomWeb",
|
||||
"public": {
|
||||
"ui": {
|
||||
"studyListDateFilterNumDays": 1
|
||||
}
|
||||
},
|
||||
"proxy": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
{
|
||||
"servers": {
|
||||
"dicomWeb": [
|
||||
{
|
||||
"name": "Orthanc",
|
||||
"wadoUriRoot": "http://pacsIP:8042/wado",
|
||||
"qidoRoot": "http://pacsIP:8042/dicom-web",
|
||||
"wadoRoot": "http://pacsIP:8042/dicom-web",
|
||||
"qidoSupportsIncludeField": false,
|
||||
"imageRendering": "wadouri",
|
||||
"thumbnailRendering": "wadouri",
|
||||
"requestOptions": {
|
||||
"auth": "orthanc:orthanc",
|
||||
"logRequests": true,
|
||||
"logResponses": false,
|
||||
"logTiming": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"defaultServiceType": "dicomWeb",
|
||||
"public": {
|
||||
"ui": {
|
||||
"studyListDateFilterNumDays": 1
|
||||
}
|
||||
},
|
||||
"proxy": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,44 @@
|
||||
##### Looking for your Deploy Preview? - <a onclick="function redirect() { window.location.href='/demo/'; } redirect();">Deploy Preview for Viewer</a>
|
||||
##### Looking for a Deploy Preview? - <a onclick="function redirect() { window.location.href='/demo/'; } redirect();">Deploy Preview for Viewer</a>
|
||||
|
||||
# Introduction - test
|
||||
> ATTENTION! You are looking at the docs for the `React` version of the OHIF
|
||||
> Viewer. If you're looking for the `Meteor` version's documentation (now
|
||||
> deprecated), select it's version from the dropdown box in the top left corner
|
||||
> of this page.
|
||||
|
||||
The [Open Health Imaging Foundation](https://www.ohif.org) is developing an open source framework for constructing web-based medical imaging applications.
|
||||
# Introduction
|
||||
|
||||
## The **OHIF Viewer**: A general purpose DICOM Viewer ([demo](http://viewer.ohif.org/))
|
||||
The [Open Health Imaging Foundation][ohif-org] (OHIF) Viewer is an open source,
|
||||
web-based, medical imaging viewer. It can be configured to connect to Image
|
||||
Archives that support [DicomWeb][dicom-web], and offers support for mapping to
|
||||
proprietary API formats. OHIF maintained extensions add support for viewing,
|
||||
annotating, and reporting on DICOM images in 2D (slices) and 3D (volumes).
|
||||
|
||||

|
||||
|
||||
The Open Health Imaging Foundation intends to provide a simple general purpose DICOM Viewer which can be easily extended for specific uses.
|
||||
<center><i>The <strong>OHIF Viewer</strong>: A general purpose DICOM Viewer (<a href="http://viewer.ohif.org/">Live Demo</a>)</center>
|
||||
|
||||
The Open Health Imaging Foundation intends to provide a simple general purpose
|
||||
DICOM Viewer which can be easily extended for specific uses. If you find
|
||||
yourself unable to extend the viewer for your purposes, please reach out via our
|
||||
[GitHub issues][gh-issues]. We are actively seeking feedback on ways to improve
|
||||
our integration and extension points.
|
||||
|
||||
## Where to Next?
|
||||
|
||||
Check out these helpful links:
|
||||
|
||||
- Ready to dive into some code? Check out our
|
||||
[Getting Started Guide](./essentials/getting-started.md).
|
||||
- We're an active, vibrant community.
|
||||
[Learn how you can be more involved.](./contributing.md)
|
||||
- Feeling lost? Read our [help page](./help.md).
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[ohif-org]: https://www.ohif.org
|
||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||
[gh-issues]: https://github.com/OHIF/Viewers/issues
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
- Essentials
|
||||
- [Getting Started](essentials/getting-started.md)
|
||||
- [Data Source](essentials/data-source.md)
|
||||
- [Configuration](essentials/configuration.md)
|
||||
- [Themeing](essentials/themeing.md)
|
||||
- [Troubleshooting](essentials/troubleshooting.md)
|
||||
@ -41,11 +42,8 @@
|
||||
- Stand-Alone
|
||||
- [Build for Production](deployment/recipes/build-for-production.md)
|
||||
- [Static](deployment/recipes/static-assets.md)
|
||||
- [Docker]()
|
||||
- [Nginx + Orthanc]()
|
||||
- [Nginx + dcm4chee]()
|
||||
- [Nginx + DICOMCloud]()
|
||||
- [User Access Control]()
|
||||
- [Nginx + Image Archive](deployment/recipes/nginx--image-archive.md)
|
||||
- [User Account Control](deployment/recipes/user-account-control.md)
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 818 B After Width: | Height: | Size: 818 B |
|
Before Width: | Height: | Size: 473 B After Width: | Height: | Size: 473 B |
BIN
docs/latest/assets/designs/user-access-control-request-flow.fig
Normal file
BIN
docs/latest/assets/img/jwt-explained.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
docs/latest/assets/img/keycloak-default-theme.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
docs/latest/assets/img/keycloak-ohif-theme.png
Normal file
|
After Width: | Height: | Size: 884 KiB |
BIN
docs/latest/assets/img/user-access-control-request-flow.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,4 @@
|
||||
<div style="text-align: center;">
|
||||
<img src="/assets/img/user-access-control-request-flow.png" alt="request flow example" style="margin: 0 auto;" />
|
||||
<div><i>simplified request flow diagram</i></div>
|
||||
</div>
|
||||
1
docs/latest/deployment/recipes/nginx--image-archive.md
Normal file
@ -0,0 +1 @@
|
||||
# Nginx + Image Archive
|
||||
289
docs/latest/deployment/recipes/user-account-control.md
Normal file
@ -0,0 +1,289 @@
|
||||
# User Account Control
|
||||
|
||||
> DISCLAIMER! We make no claims or guarantees of this approach's security. If in
|
||||
> doubt, enlist the help of an expert and conduct proper audits.
|
||||
|
||||
Making a viewer and its medical imaging data accessible on the open web can
|
||||
provide a lot of benefits, but requires additional security to make sure
|
||||
sensitive information can only be viewed by authorized individuals. Most image
|
||||
archives are equipped with basic security measures, but they are not
|
||||
robust/secure enough for the open web.
|
||||
|
||||
This guide covers one of many potential production setups that secure our
|
||||
sensitive data.
|
||||
|
||||
## Overview
|
||||
|
||||
This guide builds on top of our
|
||||
[Nginx + Image Archive guide](/deployment/recipes/nginx--image-archive.md),
|
||||
wherein we used a [`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy)
|
||||
to retrieve resources from our image archive (Orthanc).
|
||||
|
||||
To add support for "User Account Control" we introduce
|
||||
[Keycloak](https://www.keycloak.org/about.html). Keycloak is an open source
|
||||
Identity and Access Management solution that makes it easy to secure
|
||||
applications and services with little to no code. We improve upon our
|
||||
`reverse proxy` setup by integrating Keycloak and Nginx to create an
|
||||
`authenticating reverse proxy`.
|
||||
|
||||
> An authenticating reverse proxy is a reverse proxy that only retrieves the
|
||||
> resources on behalf of a client if the client has been authenticated. If a
|
||||
> client is not authenticated they can be redirected to a login page.
|
||||
|
||||
This setup allows us to create a setup similar to the one pictured below:
|
||||
|
||||
{% include "./../_user-account-control-flow-diagram.md" %}
|
||||
|
||||
- All web requests are routed through `nginx` on our `OpenResty` image
|
||||
- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints
|
||||
- Requires valid `Authorization: Bearer <token>` header
|
||||
- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin
|
||||
- `/auth` is a reverse proxy for `keycloak`
|
||||
- All static resources for OHIF Viewer are unprotected and accessible. We have
|
||||
application logic that will redirect unauthenticated users to the appropriate
|
||||
`keycloak` login screen.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Requirements
|
||||
|
||||
- Docker
|
||||
- [Docker for Mac](https://docs.docker.com/docker-for-mac/)
|
||||
- [Docker for Windows](https://docs.docker.com/docker-for-windows/)
|
||||
|
||||
_Not sure if you have `docker` installed already? Try running `docker --version`
|
||||
in command prompt or terminal_
|
||||
|
||||
### Setup
|
||||
|
||||
_Spin Things Up_
|
||||
|
||||
- Navigate to `<project-root>/docker/OpenResty-Orthanc-Keycloak` in your shell
|
||||
- Run `docker-compose up`
|
||||
|
||||
_Create Your First User_
|
||||
|
||||
- Navigate to: `http://127.0.0.1/auth/admin`
|
||||
- Sign in with: `admin`/`password`
|
||||
- From the top left dropdown, select the `Ohif` realm
|
||||
- From the left sidebar, under `Manage`, select `Users`
|
||||
- Click `Add User`
|
||||
- Username: `test`
|
||||
- Email Verified: `ON`
|
||||
- Click `Save`
|
||||
- Click the `Credentials` Tab
|
||||
- New Pasword: `test`
|
||||
- Password Confirmation: `test`
|
||||
- Temporary: `OFF`
|
||||
- Click: `Reset Password`
|
||||
- From the top right dropdown, select `Admin`, then `Sign Out`
|
||||
|
||||
_Sign In_
|
||||
|
||||
- Navigate to `http://127.0.0.1/`
|
||||
- Username: `test`, Password: `test`
|
||||
- Click `Log In`
|
||||
|
||||
_Upload Your First Study_
|
||||
|
||||
- Navigate to `http://127.0.0.1/pacs-admin`
|
||||
- If you're not already logged in, use `test`/`test`
|
||||
- From the top right, select "Upload"
|
||||
- Click "Select files to upload..." (DICOM)
|
||||
- Click "Start the upload"
|
||||
- Navigate back to `http://127.0.0.1/` to view your studies in the Study List
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
_Exit code 137_
|
||||
|
||||
This means Docker ran out of memory. Open Docker Desktop, go to the `advanced`
|
||||
tab, and increase the amount of Memory available.
|
||||
|
||||
_Cannot create container for service X_
|
||||
|
||||
Use this one with caution: `docker system prune`
|
||||
|
||||
_X is already running_
|
||||
|
||||
Stop running all containers:
|
||||
|
||||
- Win: `docker ps -a -q | ForEach { docker stop $_ }`
|
||||
- Linux: `docker stop $(docker ps -a -q)`
|
||||
|
||||
### Configuration
|
||||
|
||||
After verifying that everything runs with default configuration values, you will
|
||||
likely want to update:
|
||||
|
||||
- The domain: `http://127.0.0.1`
|
||||
- Set secure, non-default passwords
|
||||
- Regenerate Keycloak Client Secrets
|
||||
|
||||
#### OHIF Viewer
|
||||
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||
available globally at `window.config`. The configuration we use is set to a
|
||||
specific file when we build the viewer, and determined by the env variable:
|
||||
`REACT_APP_CONFIG`. You can see where we set its value in the `dockerfile` for
|
||||
this solution:
|
||||
|
||||
`ENV REACT_APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
|
||||
|
||||
You can find the configuration we're using here:
|
||||
`/public/config/docker_openresty-orthanc-keycloak.js`
|
||||
|
||||
To rebuild the `webapp` image created by our `dockerfile` after updating the
|
||||
Viewer's configuration, you can run:
|
||||
|
||||
- `docker-compose build` OR
|
||||
- `docker-compose up --build`
|
||||
|
||||
#### Other
|
||||
|
||||
All other files are found in: `/docker/OpenResty-Orthanc-Keycloak/`
|
||||
|
||||
| Service | Configuration | Docs |
|
||||
| ----------------- | ------------------------------------------------ | ------------------------------------------- |
|
||||
| OHIF Viewer | [dockerfile][dockerfile] / [config.js][config] | You're reading them now! |
|
||||
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
|
||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||
| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | |
|
||||
|
||||
- \* These are the seed values for Keycloak. They can be manually updated at
|
||||
`http://127.0.0.1/auth/admin`
|
||||
|
||||
#### Keycloak Themeing
|
||||
|
||||
The `Login` screen for the `ohif-viewer` client is using a Custom Keycloak
|
||||
theme. You can find the source files for it in
|
||||
`/docker/OpenResty-Orthanc-Keycloak/volumes/keycloak-themes/`. You can see how
|
||||
we add it to Keycloak in the `docker-compose` file, and you can read up on how
|
||||
to leverage custom themes in
|
||||
[Keycloak's own docs](https://www.keycloak.org/docs/latest/server_development/index.html#_themes).
|
||||
|
||||
| Default Theme | OHIF Theme |
|
||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Deploying to Production
|
||||
|
||||
While these configuration and docker-compose files model an environment suitable
|
||||
for production, they are not easy to deploy "as is". You can either:
|
||||
|
||||
- Manually recreate this environment and deploy built application files **OR**
|
||||
- Deploy to a cloud kubernetes provider like
|
||||
[Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR**
|
||||
- [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category)
|
||||
- Find and follow your preferred provider's guide on setting up
|
||||
[swarms and stacks](https://docs.docker.com/get-started/)
|
||||
|
||||
### Adding SSL
|
||||
|
||||
Adding SSL registration and renewal for your domain with Let's Encrypt that
|
||||
terminates at Nginx is an incredibly important step toward securing your data.
|
||||
Here are some resources, specific to this setup, that may be helpful:
|
||||
|
||||
- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl)
|
||||
- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/)
|
||||
|
||||
While we terminate SSL at Nginx, it may be worth using self signed certificates
|
||||
for communication between services.
|
||||
|
||||
- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/)
|
||||
|
||||
### Use PostgresSQL w/ Orthanc
|
||||
|
||||
Orthanc can handle a large amount of data and requests, but if you find that
|
||||
requests start to slow as you add more and more studies, you may want to
|
||||
configure your Orthanc instance to use PostgresSQL. Instructions on how to do
|
||||
that can be found in the
|
||||
[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under
|
||||
"PostgreSQL and Orthanc inside Docker"
|
||||
|
||||
### Improving This Guide
|
||||
|
||||
Here are some improvements this guide would benefit from, and that we would be
|
||||
more than happy to accept Pull Requests for:
|
||||
|
||||
- SSL Support
|
||||
- Complete configuration with `.env` file (or something similar)
|
||||
- Keycloak Theme improvements
|
||||
- Any security issues
|
||||
- One-click deploy to a cloud provider
|
||||
|
||||
## Resources
|
||||
|
||||
### Misc. Helpful Commands
|
||||
|
||||
_Check if `nginx.conf` is valid:_
|
||||
|
||||
```bash
|
||||
docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t
|
||||
```
|
||||
|
||||
_Interact w/ running container:_
|
||||
|
||||
`docker exec -it CONTAINER_NAME bash`
|
||||
|
||||
_List running containers:_
|
||||
|
||||
`docker ps`
|
||||
|
||||
_Clear Keycloak DB so you can re-seed values:_
|
||||
|
||||
- `docker volume prune` OR
|
||||
- `docker volume ls` and `docker volume rm VOLUME_NAME VOLUME_NAME`
|
||||
|
||||
### Referenced Articles
|
||||
|
||||
The inspiration for our setup was driven largely by these articles:
|
||||
|
||||
- [Securing Nginx with Keycloak](https://edhull.co.uk/blog/2018-06-06/keycloak-nginx)
|
||||
- [Authenticating Reverse Proxy with Keycloak](https://eclipsesource.com/blogs/2018/01/11/authenticating-reverse-proxy-with-keycloak/)
|
||||
- [Securing APIs with Kong and Keycloak](https://www.jerney.io/secure-apis-kong-keycloak-1/)
|
||||
|
||||
For more documentation on the software we've chosen to use, you may find the
|
||||
following resources helpful:
|
||||
|
||||
- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html)
|
||||
- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/)
|
||||
- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/)
|
||||
- [Auth0: Picking a Grant Type](https://auth0.com/docs/api-auth/which-oauth-flow-to-use)
|
||||
|
||||
We chose to use a generic OpenID Connect library on the client, but it's worth
|
||||
noting that Keycloak comes packaged with its own:
|
||||
|
||||
- [redux-oidc](https://github.com/maxmantz/redux-oidc) (Which wraps
|
||||
[oidc-client-js](https://github.com/IdentityModel/oidc-client-js/wiki))
|
||||
- [Keycloak JavaScript Adapter](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter)
|
||||
|
||||
If you're not already drowning in links, here are some good security resources
|
||||
for OAuth:
|
||||
|
||||
- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660)
|
||||
- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code)
|
||||
- [Good description on SSO Protocols](https://www.keycloak.org/docs/2.5/server_admin/topics/sso-protocols/oidc.html)
|
||||
|
||||
For a different take on this setup, check out the repository one of our
|
||||
community members put together:
|
||||
|
||||
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- DOCS -->
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
[config]: #
|
||||
[dockerfile]: #
|
||||
[config-nginx]: #
|
||||
[config-orthanc]: #
|
||||
[config-keycloak]: #
|
||||
<!-- prettier-ignore-end -->
|
||||
@ -2,14 +2,25 @@
|
||||
|
||||
The ohif-viewer package provides two different build processes:
|
||||
|
||||
# create-react-app
|
||||
## create-react-app
|
||||
|
||||
> [create-react-app](https://github.com/facebook/create-react-app) provides pre-configured build process for developing front-end applications with [React](https://reactjs.org/).
|
||||
> [create-react-app](https://github.com/facebook/create-react-app) provides
|
||||
> pre-configured build process for developing front-end applications with
|
||||
> [React](https://reactjs.org/).
|
||||
|
||||
The ohif-viewer package can be run as a create-react-app application. This is useful for development, debugging, or evolving ohif-viewer into your own custom imaging application.
|
||||
The ohif-viewer package can be run as a create-react-app application. This is
|
||||
useful for development, debugging, or evolving ohif-viewer into your own custom
|
||||
imaging application.
|
||||
|
||||
# Rollup
|
||||
## Rollup
|
||||
|
||||
> [Rollup](https://rollupjs.org/guide/en) is a module bundler for JavaScript. It uses the new standardized format for code modules included in the ES6 revision of JavaScript.
|
||||
> [Rollup](https://rollupjs.org/guide/en) is a module bundler for JavaScript. It
|
||||
> uses the new standardized format for code modules included in the ES6 revision
|
||||
> of JavaScript.
|
||||
|
||||
The ohif-viewer package can be built with Rollup to provide a set of React components which can be dropped into a larger application. Specifically, the ohif-viewer package provides a React component named `OHIFViewer` which is the entire viewer, configurable via React `props`. This is useful for including the OHIF Viewer in a larger web application, as the entire application can be provided via a `<script>` tag with no build process required.
|
||||
The ohif-viewer package can be built with Rollup to provide a set of React
|
||||
components which can be dropped into a larger application. Specifically, the
|
||||
ohif-viewer package provides a React component named `OHIFViewer` which is the
|
||||
entire viewer, configurable via React `props`. This is useful for including the
|
||||
OHIF Viewer in a larger web application, as the entire application can be
|
||||
provided via a `<script>` tag with no build process required.
|
||||
|
||||
122
docs/latest/essentials/data-source.md
Normal file
@ -0,0 +1,122 @@
|
||||
# Data Source
|
||||
|
||||
After following the steps outlined in [Getting Started](./getting-started.md),
|
||||
you'll notice that the OHIF Viewer has data for several studies and their
|
||||
images. You didn't add this data, so where is it coming from?
|
||||
|
||||
By default, the viewer is configured to connect to a remote server hosted by the
|
||||
nice folks over at [dcmjs.org][dcmjs-org]. While convenient for getting started,
|
||||
the time may come when you want to develop using your own data either locally or
|
||||
remotely.
|
||||
|
||||
## Set up a local DICOM server
|
||||
|
||||
> ATTENTION! Already have a remote or local server? Skip to the
|
||||
> [configuration section](#configuration-learn-more) below.
|
||||
|
||||
While the OHIF Viewer can work with any data source, the easiest to configure
|
||||
are the ones that follow the [DICOMWeb][dicom-web] spec.
|
||||
|
||||
1. Choose and install an Image Archive
|
||||
2. Upload data to your archive (e.g. with DCMTK's [storescu][storescu] or your
|
||||
archive's web interface)
|
||||
3. Keep the server running
|
||||
|
||||
For our purposes, we will be using `Orthanc`, but you can see a list of
|
||||
[other Open Source options](#open-source-dicom-image-archives) below.
|
||||
|
||||
### Requirements
|
||||
|
||||
...
|
||||
|
||||
### Running Orthanc
|
||||
|
||||
_Start Orthanc:_
|
||||
|
||||
```bash
|
||||
# Runs orthanc so long as window remains open
|
||||
yarn run orthanc:up
|
||||
```
|
||||
|
||||
_Upload your first Study:_
|
||||
|
||||
1. Navigate to [Orthanc's web interface](http://localhost:8899) at
|
||||
`http://localhost:8899` in a web browser.
|
||||
2. In the top right corner, click "Upload"
|
||||
3. Click "Select files to upload..." and select one or more DICOM files
|
||||
4. Click "Start the upload"
|
||||
|
||||
#### Orthanc: Learn More
|
||||
|
||||
You can see the `docker-compose.yml` file this command runs at
|
||||
[`<project-root>/docker/Nginx-Docker/`](#), and more on Orthanc for Docker in
|
||||
[Orthanc's documentation][orthanc-docker].
|
||||
|
||||
### Connecting to Orthanc
|
||||
|
||||
Now that we have a local Orthanc instance up and running, we need to configure
|
||||
our web application to connect to it. Open a new terminal window, navigate to
|
||||
this project's root directory, and run:
|
||||
|
||||
```bash
|
||||
# If you haven't already, restore dependencies
|
||||
yarn install
|
||||
|
||||
# Run our dev command, but with the local orthanc config
|
||||
yarn run dev:orthanc
|
||||
```
|
||||
|
||||
#### Configuration: Learn More
|
||||
|
||||
> For more configuration fun, check out the
|
||||
> [Essentials Configuration](./configuration.md) guide.
|
||||
|
||||
Let's take a look at what's going on under the hood here. `yarn run dev:orthanc`
|
||||
is running the `dev:orthanc` script in our project's `package.json`. That script
|
||||
is:
|
||||
|
||||
```js
|
||||
cross-env PORT=5000 REACT_APP_CONFIG=config/docker_nginx-orthanc.js react-scripts start
|
||||
```
|
||||
|
||||
- `cross-env` sets two environment variables
|
||||
- PORT: 5000
|
||||
- REACT_APP_CONFIG: `config/docker_nginx-orthanc.js`
|
||||
- `react-scripts` runs it's `start` script. This is [the de-facto
|
||||
way][cra-start] to run a "Create React App" in development mode.
|
||||
|
||||
The `REACT_APP_CONFIG` value tells our app which file to load on to
|
||||
`window.config`. By default, our app uses the file at
|
||||
`<project-root>/public/config/default.js`.
|
||||
|
||||
## Open Source DICOM Image Archives
|
||||
|
||||
| Archive | Installation |
|
||||
| --------------------------------------------- | ---------------------------------- |
|
||||
| [DCM4CHEE Archive 5.x][dcm4chee] | [W/ Docker][dcm4chee-docker] |
|
||||
| [Orthanc][orthanc] | [W/ Docker][orthanc-docker] |
|
||||
| [DICOMcloud][dicomcloud] (**DICOM Web only**) | [Installation][dicomcloud-install] |
|
||||
| [OsiriX][osirix] (**Mac OSX only**) | Desktop Client |
|
||||
| [Horos][horos] (**Mac OSX only**) | Desktop Client |
|
||||
|
||||
_Feel free to make a Pull Request if you want to add to this list._
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[dcmjs-org]: https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado
|
||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||
[storescu]: http://support.dcmtk.org/docs/storescu.html
|
||||
[cra-start]: https://github.com/facebook/create-react-app#npm-start-or-yarn-start
|
||||
<!-- Archives -->
|
||||
[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light
|
||||
[dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker
|
||||
[orthanc]: https://www.orthanc-server.com/
|
||||
[orthanc-docker]: http://book.orthanc-server.com/users/docker.html
|
||||
[dicomcloud]: https://github.com/DICOMcloud/DICOMcloud
|
||||
[dicomcloud-install]: https://github.com/DICOMcloud/DICOMcloud#running-the-code
|
||||
[osirix]: http://www.osirix-viewer.com/
|
||||
[horos]: https://www.horosproject.org/
|
||||
<!-- prettier-ignore-end -->
|
||||
@ -1,43 +1,59 @@
|
||||
# Getting Started
|
||||
|
||||
## Getting the Code
|
||||
## Setup
|
||||
|
||||
Either clone the repository using Git:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:OHIF/Viewers.git
|
||||
```
|
||||
|
||||
or
|
||||
[Download the latest Master as a ZIP File](https://github.com/OHIF/Viewers/archive/master.zip).
|
||||
|
||||
## Run the OHIF Viewer application against our public DICOM server
|
||||
|
||||
You can either spin up locally or run with Docker.
|
||||
|
||||
### running locally
|
||||
|
||||
1. [Install Node.js](https://nodejs.org/en/)
|
||||
2. Open a new terminal tab, go under `ohif-viewer` directory and install all
|
||||
dependency packages via `yarn`
|
||||
### Fork & Clone
|
||||
|
||||
If you intend to contribute back changes, or if you would like to pull updates
|
||||
we make to the OHIF Viewer, then follow these steps:
|
||||
|
||||
- [Fork][fork-a-repo] the [OHIF/Viewers][ohif-viewers-repo] repository
|
||||
- [Create a local clone][clone-a-repo] of your fork
|
||||
- `git clone https://github.com/YOUR-USERNAME/Viewers`
|
||||
- Add OHIF/Viewers as a [remote repository][add-remote-repo] labled `upstream`
|
||||
- Navigate to the cloned project's directory
|
||||
- `git remote add upstream https://github.com/OHIF/Viewers.git`
|
||||
|
||||
With this setup, you can now [sync your fork][sync-changes] to keep it
|
||||
up-to-date with the upstream (original) repository. This is called a "Triangular
|
||||
Workflow" and is common for Open Source projects. The GitHub blog has a [good
|
||||
graphic that illustrates this setup][triangular-workflow].
|
||||
|
||||
### Private
|
||||
|
||||
Alternatively, if you intend to use the OHIF Viewer as a starting point, and you
|
||||
aren't as concerned with syncing updates, then follow these steps:
|
||||
|
||||
1. Navigate to the [OHIF/Viewers/tree/react][ohif-viewers-react-repo] repository
|
||||
and branch
|
||||
2. Click `Clone or download`, and then `Download ZIP`
|
||||
3. Use the contents of the `.zip` file as a starting point for your viewer
|
||||
|
||||
> NOTE: It is still possible to sync changes using this approach. However,
|
||||
> submitting pull requests for fixes and features are best done with the
|
||||
> separate, forked repository setup described in "Fork & Clone"
|
||||
|
||||
## Developing
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Node.js & NPM](https://nodejs.org/en/)
|
||||
- [Yarn](https://yarnpkg.com/en/)
|
||||
|
||||
### Kick the tires
|
||||
|
||||
Navigate to the root of the project's directory in your terminal and run the
|
||||
following commands:
|
||||
|
||||
```bash
|
||||
# Restore dependencies
|
||||
yarn install
|
||||
```
|
||||
|
||||
3. Run the application
|
||||
|
||||
```bash
|
||||
# Start local development server
|
||||
yarn start
|
||||
```
|
||||
|
||||
Note: This will connect to our public DICOMWeb server so you can verify your
|
||||
installation. Follow the next section to connect to your own local or remote
|
||||
DICOMWeb server.
|
||||
|
||||
4. Launch the OHIF Viewer Study List. By default the address is
|
||||
[http://localhost:3000/](http://localhost:3000/). The port may vary so check
|
||||
the start up output messages such as:
|
||||
You should see the following output:
|
||||
|
||||
```bash
|
||||
Compiled successfully!
|
||||
@ -51,132 +67,49 @@ Note that the development build is not optimized.
|
||||
To create a production build, use yarn build.
|
||||
```
|
||||
|
||||
**If everything is working correctly, you should see the studies from our public
|
||||
archive when you visit the Study List.**
|
||||
### 🎉 Celebrate 🎉
|
||||
|
||||

|
||||
<center>
|
||||
<img alt="development server hosted app" src="/assets/img/loading-study.gif" />
|
||||
<i>Our app, hosted by the development server</i>
|
||||
</center>
|
||||
|
||||
5. Double-click on a Study in the Study List to launch it in the Viewer
|
||||
### Building for Production
|
||||
|
||||
**If everything is working correctly, you should see your study load into the
|
||||
Viewer.**
|
||||
|
||||

|
||||
|
||||
## Set up a local DICOM server
|
||||
|
||||
1. Choose and install an Image Archive
|
||||
2. Upload some data into your archive (e.g. with DCMTK's
|
||||
[storescu](http://support.dcmtk.org/docs/storescu.html) or your archive's web
|
||||
interface)
|
||||
3. Keep the server running
|
||||
|
||||
#### Open Source DICOM Image Archive Options
|
||||
|
||||
| Archive | Installation |
|
||||
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| [DCM4CHEE Archive 5.x](https://github.com/dcm4che/dcm4chee-arc-light) | [Installation with Docker](https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker) |
|
||||
| [Orthanc](https://www.orthanc-server.com/) | [Installation with Docker](http://book.orthanc-server.com/users/docker.html) |
|
||||
| [DICOMcloud](https://github.com/DICOMcloud/DICOMcloud) (**DICOM Web only**) | [Installation](https://github.com/DICOMcloud/DICOMcloud#running-the-code) |
|
||||
| [OsiriX](http://www.osirix-viewer.com/) (**Mac OSX only**) |
|
||||
| [Horos](https://www.horosproject.org/) (**Mac OSX only**) |
|
||||
|
||||
_Feel free to make a Pull Request if you want to add to this list._
|
||||
|
||||
#### Orthanc with Docker
|
||||
|
||||
Depending on whether or not you want uploaded studies to persist in Orthanc
|
||||
after Docker has been closed, there are two different methods for starting the
|
||||
Docker image:
|
||||
|
||||
##### Temporary data storage
|
||||
|
||||
This command will start an instance of the jodogne/orthanc-plugins Docker image.
|
||||
_All data will be removed when the instance is stopped!_
|
||||
|
||||
```
|
||||
docker run --rm -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins
|
||||
```
|
||||
|
||||
##### Persistent data storage
|
||||
|
||||
In order to allow your data to persist after the instance is stopped, you first
|
||||
need to create an image and attached data volume with Docker. The steps are as
|
||||
follows:
|
||||
|
||||
1. Create a persistent data volume for Orthanc to use
|
||||
|
||||
```
|
||||
docker create --name sampledata -v /sampledata jodogne/orthanc-plugins
|
||||
```
|
||||
|
||||
**Note: On Windows, you need to use an absolute path for the data volume,
|
||||
like so:**
|
||||
|
||||
```
|
||||
docker create --name sampledata -v '//C/Users/erik/sampledata' jodogne/orthanc-plugins
|
||||
```
|
||||
|
||||
2. Run Orthanc from Docker with the data volume attached
|
||||
|
||||
```
|
||||
docker run --volumes-from sampledata -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins
|
||||
```
|
||||
|
||||
3. Upload your data and it will be persisted
|
||||
|
||||
### Setting up OHIF Viewer with Orthanc as an example
|
||||
|
||||
Once you have Orthanc running with docker either with temporary data storage or
|
||||
persistent data storage we con move forward with the next steps.
|
||||
|
||||
1. Load orthanc with a dataset you might want to use. To upload data use
|
||||
[http://localhost:8042/app/explorer.html](http://localhost:8042/app/explorer.html).
|
||||
|
||||
**orthanc is the username and password for orthanc docker**
|
||||
|
||||
2. Go under
|
||||
[http://localhost:8042/app/explorer.html#upload](http://localhost:8042/app/explorer.html#upload)
|
||||
and upload your DICOM files there
|
||||
|
||||
3. After you load the data, open a new terminal tab in the `ohif-viewer`
|
||||
directory and install all dependency packages via Yarn
|
||||
> More comprehensive guides for building and publishing can be found in our
|
||||
> [deployment docs](./../deployment/index.md)
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
# Build static assets to host a PWA
|
||||
yarn run build:web
|
||||
|
||||
# Build packaged output
|
||||
yarn run build:package
|
||||
```
|
||||
|
||||
3. Run the application using one of the available configuration files. **the
|
||||
following command assumes you are under the `root` folder**
|
||||
## Next Steps
|
||||
|
||||
```bash
|
||||
export REACT_APP_CONFIG=$(cat ./config/local_orthanc.js)
|
||||
yarn start
|
||||
```
|
||||
...
|
||||
|
||||
This uses the
|
||||
[Custom Environment Variables of Create-React-App](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables)
|
||||
to pass in your configuration. The example above will not work on Windows.
|
||||
Please visit the link to read about how to set environment variables on Windows.
|
||||
|
||||
4. Launch the OHIF Viewer Study List by visiting
|
||||
[http://localhost:3000/](http://localhost:3000/) in a web browser.
|
||||
|
||||
**If everything is working correctly, you should see the Study List from your
|
||||
archive when you visit the Study List.**
|
||||
|
||||
5. Double-click on a Study in the Study List to launch it in the Viewer
|
||||
|
||||
**If everything is working correctly, you should see your study load into the
|
||||
Viewer.**
|
||||
|
||||
#### Troubleshooting
|
||||
## Troubleshooting
|
||||
|
||||
- If you receive a _"No Studies Found"_ message and do not see your studies, try
|
||||
changing the Study Date filters to a wider range.
|
||||
- If you see a 'Loading' message which never resolves, check your browser
|
||||
JavaScript console inside the Developer Tools to identify any errors.
|
||||
- If you receive `exit code 137`, increase the amount of memory available to
|
||||
your docker instances.
|
||||
- If you see any errors in your server console, check the
|
||||
[Troubleshooting](./troubleshooting.md) page for more in depth advice.
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[fork-a-repo]: https://help.github.com/en/articles/fork-a-repo
|
||||
[clone-a-repo]: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork
|
||||
[add-remote-repo]: https://help.github.com/en/articles/fork-a-repo#step-3-configure-git-to-sync-your-fork-with-the-original-spoon-knife-repository
|
||||
[sync-changes]: https://help.github.com/en/articles/syncing-a-fork
|
||||
[triangular-workflow]: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows
|
||||
[ohif-viewers-repo]: https://github.com/OHIF/Viewers
|
||||
[ohif-viewers-react-repo]: https://github.com/OHIF/Viewers/tree/react
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
# Meteor Packages
|
||||
|
||||
### Commands (*ohif-commands*)
|
||||
### Core (*ohif-core*)
|
||||
|
||||
## Cornerstone Package (*ohif-cornerstone*)
|
||||
|
||||
This package contains a number of front-end libraries that help us build web-based medical imaging applications.
|
||||
|
||||
These are:
|
||||
- [dicomParser](https://github.com/cornerstonejs/dicomParser):
|
||||
A lightweight JavaScript library for parsing DICOM P10 byte streams in modern web browsers (IE10+), Node.js, and Meteor.
|
||||
|
||||
- [Cornerstone Core](https://github.com/cornerstonejs/cornerstone):
|
||||
A lightweight JavaScript library for displaying medical images in modern web browsers that support the HTML5 canvas element.
|
||||
|
||||
- [Cornerstone Tools](https://github.com/cornerstonejs/cornerstoneTools):
|
||||
A library built on top of cornerstone that provides a set of common tools needed in medical imaging to work with images and stacks of images
|
||||
|
||||
- [Cornerstone Math](https://github.com/cornerstonejs/cornerstoneMath):
|
||||
Math and computational geometry functionality for Cornerstone
|
||||
|
||||
- [Cornerstone WADO Image Loader](https://github.com/cornerstonejs/cornerstoneWADOImageLoader):
|
||||
A Cornerstone Image Loader for DICOM P10 instances over HTTP. This can be used to integrate cornerstone with WADO-URI servers or any other HTTP based server that returns DICOM P10 instances (e.g. Orthanc or custom servers).
|
||||
|
||||
- [Hammer.js](https://github.com/hammerjs/hammer.js):
|
||||
A JavaScript library for multi-touch gestures
|
||||
|
||||
### Design (*ohif-design*)
|
||||
### DICOM Services (*ohif-dicom-services*)
|
||||
It contains a number of helper functions for retrieving common value types (e.g. JSON, patient name, image frame) from a DICOM image. This package is for server-side usage.
|
||||
|
||||
### Hanging Protocols (*ohif-hanging-protocols*)
|
||||
### Header (*ohif-header*)
|
||||
### Hotkeys (*ohif-hotkeys*)
|
||||
|
||||
### Lesion Tracker (*ohif-lesiontracker*)
|
||||
This package stores all of the oncology-specific tools and functions developed for the Lesion Tracker application. Here we store, for example, the Target measurement and Non-target pointer tools that are used to monitor tumour burden over time.
|
||||
|
||||
This package also stores Meteor components for the interactive lesion table used in the Lesion Tracker, and dialog boxes for the callbacks attached to the Target and Non-target tools.
|
||||
|
||||
### Logging (*ohif-log*)
|
||||
### Logging (*ohif-measurements*)
|
||||
### Metadata (*ohif-metadata*)
|
||||
### Polyfilling Functionality (*ohif-polyfill*)
|
||||
|
||||
### Select Tree UI (*ohif-select-tree*)
|
||||
### Server Settings UI (*ohif-servers*)
|
||||
### Studies (*ohif-studies*)
|
||||
### Study List UI (*ohif-study-list*)
|
||||
### Common Themes (*ohif-themes-common*)
|
||||
### Theming (*ohif-themes*)
|
||||
### User Management (*ohif-user-management*)
|
||||
### User (*ohif-user*)
|
||||
|
||||
### Basic Viewer Components (*ohif-viewerbase*)
|
||||
This is the largest package in the repository. It holds a large number of re-usable Meteor components that are used to build both the OHIF Viewer and Lesion Tracker.
|
||||
|
||||
### WADO Proxy (*ohif-wadoproxy*)
|
||||
Proxy for CORS
|
||||
@ -1,33 +0,0 @@
|
||||
# Orthanc with Docker
|
||||
|
||||
Depending on whether or not you want uploaded studies to persist in Orthanc after Docker has been closed, there are two different methods for starting the Docker image:
|
||||
|
||||
## Temporary data storage
|
||||
This command will start an instance of the jodogne/orthanc-plugins Docker image. *All data will be removed when the instance is stopped!*
|
||||
|
||||
````
|
||||
docker run --rm -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins
|
||||
````
|
||||
|
||||
## Persistent data storage
|
||||
In order to allow your data to persist after the instance is stopped, you first need to create an image and attached data volume with Docker. The steps are as follows:
|
||||
|
||||
1. Create a persistent data volume for Orthanc to use
|
||||
|
||||
````
|
||||
docker create --name sampledata -v /sampledata jodogne/orthanc-plugins
|
||||
````
|
||||
|
||||
**Note: On Windows, you need to use an absolute path for the data volume, like so:**
|
||||
|
||||
````
|
||||
docker create --name sampledata -v '//C/Users/erik/sampledata' jodogne/orthanc-plugins
|
||||
````
|
||||
|
||||
2. Run Orthanc from Docker with the data volume attached
|
||||
|
||||
````
|
||||
docker run --volumes-from sampledata -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins
|
||||
````
|
||||
|
||||
3. Upload your data and it will be persisted
|
||||
@ -1 +1,59 @@
|
||||
# Themeing
|
||||
|
||||
Themeing is currently accomplished with color variables that are defined within
|
||||
the [`:root`](https://css-tricks.com/almanac/selectors/r/root/) selector
|
||||
(allowing them to cascade across all elements). This repository's components,
|
||||
and the ones we consume from our
|
||||
[React Viewerbase component library](https://react.ohif.org/styling-and-theming)
|
||||
utilize them. We are interested in pursuing more robust themeing options, and
|
||||
open to pull requests and discussion issues.
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Interface UI Colors */
|
||||
--default-color: #9ccef9;
|
||||
--hover-color: #ffffff;
|
||||
--active-color: #20a5d6;
|
||||
--ui-border-color: #44626f;
|
||||
--ui-border-color-dark: #3c5d80;
|
||||
--ui-border-color-active: #00a4d9;
|
||||
--primary-background-color: #000000;
|
||||
--box-background-color: #3e5975;
|
||||
|
||||
--text-primary-color: #ffffff;
|
||||
--text-secondary-color: #91b9cd;
|
||||
--input-background-color: #2c363f;
|
||||
--input-placeholder-color: #d3d3d3;
|
||||
|
||||
--table-hover-color: #2c363f;
|
||||
--table-text-primary-color: #ffffff;
|
||||
--table-text-secondary-color: #91b9cd;
|
||||
|
||||
--large-numbers-color: #6fbde2;
|
||||
|
||||
--state-error: #ffcccc;
|
||||
--state-error-border: #ffcccc;
|
||||
--state-error-text: #ffcccc;
|
||||
|
||||
/* Common palette */
|
||||
--ui-yellow: #e29e4a;
|
||||
--ui-sky-blue: #6fbde2;
|
||||
|
||||
/* State palette */
|
||||
--ui-state-error: #ffcccc;
|
||||
--ui-state-error-border: #993333;
|
||||
--ui-state-error-text: #661111;
|
||||
--ui-gray-lighter: #436270;
|
||||
--ui-gray-light: #516873;
|
||||
--ui-gray: #263340;
|
||||
--ui-gray-dark: #16202b;
|
||||
--ui-gray-darker: #151a1f;
|
||||
--ui-gray-darkest: #14202a;
|
||||
|
||||
--calendar-day-color: #d3d3d3;
|
||||
--calendar-day-border-color: #d3d3d3;
|
||||
--calendar-day-active-hover-background-color: #516873;
|
||||
--calendar-main-color: #263340;
|
||||
--viewport-border-thickness: 1px;
|
||||
}
|
||||
```
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
# Troubleshooting
|
||||
|
||||
# Common Problems
|
||||
## Common Problems
|
||||
|
||||
Problem | Most Common Reasons
|
||||
--------|--------------------
|
||||
** Can't retrieve Study List over DICOMWeb** | 1. QIDO root URL is incorrect<br> 2. DICOM Web is not enabled on PACS
|
||||
** Can't retrieve images** | 1. WADO Root URL is incorrect<br> 2. DICOM Web is not enabled on PACS<br> 3. HTTP Basic Authentication username and password are incorrect or not provided.
|
||||
| Problem | Most Common Reasons |
|
||||
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ** Can't retrieve Study List over DICOMWeb** | 1. QIDO root URL is incorrect<br> 2. DICOM Web is not enabled on PACS |
|
||||
| ** Can't retrieve images** | 1. WADO Root URL is incorrect<br> 2. DICOM Web is not enabled on PACS<br> 3. HTTP Basic Authentication username and password are incorrect or not provided. |
|
||||
|
||||
## Debugging Steps
|
||||
|
||||
# Debugging Steps
|
||||
### Can't retrieve Study List over DICOMWeb
|
||||
|
||||
1. Check that you can query your PACS using an alternative DICOM Web client (e.g. cURL, or a Web Browser). If you cannot, then your PACS is configured incorrectly. Refer to the documentation of the image archive.
|
||||
1. Check that you can query your PACS using an alternative DICOM Web client
|
||||
(e.g. cURL, or a Web Browser). If you cannot, then your PACS is configured
|
||||
incorrectly. Refer to the documentation of the image archive.
|
||||
|
||||
|
Before Width: | Height: | Size: 585 KiB |
|
Before Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 311 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 733 KiB |
|
Before Width: | Height: | Size: 517 KiB |
|
Before Width: | Height: | Size: 424 KiB |
|
Before Width: | Height: | Size: 457 KiB |
|
Before Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 318 KiB |
|
Before Width: | Height: | Size: 305 KiB |
|
Before Width: | Height: | Size: 315 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 595 KiB |
|
Before Width: | Height: | Size: 576 KiB |
|
Before Width: | Height: | Size: 411 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 320 KiB |
|
Before Width: | Height: | Size: 521 KiB |
|
Before Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 306 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 40 KiB |