diff --git a/.circleci/config.yml b/.circleci/config.yml index 45b62779039..3ba060184f0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,11 +13,23 @@ version: 2.1 ## orbs: codecov: codecov/codecov@1.0.5 - cypress: cypress-io/cypress@1.13.0 + cypress: cypress-io/cypress@1.26.0 +executors: + # Custom executor to override Cypress config + deploy-to-prod-executor: + docker: + - image: 'cypress/browsers:node14.15.0-chrome86-ff82' + environment: + CYPRESS_BASE_URL: https://ohif-staging.netlify.com/ + chrome-and-pacs: + docker: + # Primary container image where all steps run. + - image: 'cypress/browsers:node14.15.0-chrome86-ff82' + - image: 'ohif/viewer-testdata:0.1-test' defaults: &defaults docker: - - image: circleci/node:12.9.1 + - image: circleci/node:14.15.0 environment: TERM: xterm # Enable colors in term QUICK_BUILD: true @@ -170,7 +182,7 @@ jobs: DEPLOY_TO_DEV: docker: - - image: circleci/node:12.9.1 + - image: circleci/node:14.15.0 environment: TERM: xterm NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77 @@ -185,7 +197,7 @@ jobs: DEPLOY_TO_STAGING: docker: - - image: circleci/node:12.9.1 + - image: circleci/node:14.15.0 environment: TERM: xterm NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969 @@ -200,7 +212,7 @@ jobs: DEPLOY_TO_PRODUCTION: docker: - - image: circleci/node:12.9.1 + - image: circleci/node:14.15.0 environment: TERM: xterm NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0 @@ -307,8 +319,73 @@ jobs: workflows: version: 2 - # WORKFLOWS Disabled until this feature branch is further along - # See Master's CircleCI Config + PR_CHECKS: + jobs: + - UNIT_TESTS: + filters: + branches: + ignore: + - master + - feature/* + - hotfix/* + # E2E: PWA + - cypress/run: + name: 'E2E: PWA' + executor: chrome-and-pacs + browser: chrome + pre-steps: + - run: | + # Clear yarn cache; update to latest + rm -rf ~/.yarn + npm i -g yarn + yarn -v + yarn: true + record: true + store_artifacts: true + working_directory: platform/viewer + build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build + start: yarn run test:e2e:serve + spec: 'cypress/integration/**/*' + wait-on: 'http://localhost:3000' + cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}' + no-workspace: true # Don't persist workspace + post-steps: + - store_artifacts: + path: platform/viewer/cypress/screenshots + - store_artifacts: + path: platform/viewer/cypress/videos + - store_test_results: + path: platform/viewer/cypress/results + requires: + - UNIT_TESTS + + PR_OPTIONAL_VISUAL_TESTS: + jobs: + - AWAIT_APPROVAL: + type: approval + # Update hub.docker.org + - cypress/run: + name: 'Generate Percy Snapshots' + executor: cypress/browsers-chrome76 + browser: chrome + pre-steps: + - run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global + add wait-on' # Use yarn latest + yarn: true + store_artifacts: false + working_directory: platform/viewer + build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build + # start server --> verify running --> percy + chrome + cypress + command: yarn run test:e2e:dist + cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}' + no-workspace: true # Don't persist workspace + post-steps: + - store_artifacts: + path: platform/viewer/cypress/screenshots + - store_artifacts: + path: platform/viewer/cypress/videos + requires: + - AWAIT_APPROVAL DEPLOY: jobs: @@ -319,3 +396,61 @@ workflows: - DEPLOY_TO_DEV: requires: - BUILD + - PROMOTE_TO_STAGING: + type: approval + requires: + - DEPLOY_TO_DEV + - DEPLOY_TO_STAGING: + requires: + - PROMOTE_TO_STAGING + - PROMOTE_TO_PRODUCTION: + type: approval + requires: + - DEPLOY_TO_STAGING + - DEPLOY_TO_PRODUCTION: + requires: + - PROMOTE_TO_PRODUCTION + ### + # Unit and E2E tests have already run for PR_CHECKS + # Re-running should not gain us any confidence here + ### + RELEASE: + jobs: + - NPM_PUBLISH: + filters: + branches: + only: master + - DOCS_PUBLISH: + filters: + branches: + only: master + # Update base branch snapshots + # and record a Cypress dashboard test run + - cypress/run: + name: 'Generate Percy Snapshots' + executor: cypress/browsers-chrome76 + browser: chrome + pre-steps: + - run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global + add wait-on' # Use yarn latest + yarn: true + store_artifacts: false + working_directory: platform/viewer + build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build + # start server --> verify running --> percy + chrome + cypress + command: yarn run test:e2e:dist + cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}' + no-workspace: true # Don't persist workspace + post-steps: + - store_artifacts: + path: platform/viewer/cypress/screenshots + - store_artifacts: + path: platform/viewer/cypress/videos + - store_test_results: + path: platform/viewer/cypress/results + filters: + branches: + only: master + - DOCKER_MASTER_PUBLISH: + requires: + - NPM_PUBLISH diff --git a/.eslintrc.json b/.eslintrc.json index c8ec9056f60..c543ada9f1c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,7 +16,8 @@ }, "rules": { "react/jsx-props-no-spreading": "error", - "react-hooks/exhaustive-deps": "false" + "react-hooks/exhaustive-deps": "false", + "import/no-unused-modules": [1, {"unusedExports": true}] }, "globals": { "cy": true, diff --git a/.vscode/extensions.json b/.vscode/extensions.json index eca7ac2a9d4..7189097a714 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,6 @@ { "recommendations": [ "esbenp.prettier-vscode", - "sysoev.language-stylus", "dbaeumer.vscode-eslint", "mikestead.dotenv", "bungcip.better-toml", diff --git a/.webpack/rules/stylusToJavaScript.js b/.webpack/rules/stylusToJavaScript.js deleted file mode 100644 index 1d83f95281e..00000000000 --- a/.webpack/rules/stylusToJavaScript.js +++ /dev/null @@ -1,10 +0,0 @@ -const stylusToJavaScript = { - test: /\.styl$/, - use: [ - { loader: 'style-loader' }, // 3. Style nodes from JS Strings - { loader: 'css-loader' }, // 2. CSS to CommonJS - { loader: 'stylus-loader' }, // 1. Stylus to CSS - ], -}; - -module.exports = stylusToJavaScript; diff --git a/.webpack/webpack.base.js b/.webpack/webpack.base.js index 63917dd4013..1ee0228a909 100644 --- a/.webpack/webpack.base.js +++ b/.webpack/webpack.base.js @@ -14,6 +14,7 @@ const TerserJSPlugin = require('terser-webpack-plugin'); const NODE_ENV = process.env.NODE_ENV; const QUICK_BUILD = process.env.QUICK_BUILD; const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0'; +const { UnusedFilesWebpackPlugin } = require("unused-files-webpack-plugin"); // dotenv.config(); @@ -99,6 +100,18 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => { process.env.LOCIZE_API_KEY || '' ), }), + new UnusedFilesWebpackPlugin({ + failOnUnused: true, + globOptions: { + ignore: [ + "node_modules/**/*", + "index.umd.js", + "sanity.test.js", + "__mocks__/fileMock.js", + "__tests__/globalSetup.js" + ] + } + }), ], // Fix: https://github.com/webpack-contrib/css-loader/issues/447#issuecomment-285598881 // For issue in cornerstone-wado-image-loader diff --git a/.webpack/webpack.commonjs.js b/.webpack/webpack.commonjs.js deleted file mode 100644 index 2649da48c4c..00000000000 --- a/.webpack/webpack.commonjs.js +++ /dev/null @@ -1,19 +0,0 @@ -const merge = require('webpack-merge'); -const webpackBase = require('./webpack.base.js'); -const cssToJavaScriptRule = require('./rules/cssToJavaScript.js'); -const stylusToJavaScriptRule = require('./rules/stylusToJavaScript.js'); - -/** - * WebPack configuration for CommonJS Bundles. Extends rules of BaseConfig by making - * sure we're bundling styles and other files that would normally be split in a - * PWA. - */ -module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => { - const baseConfig = webpackBase(env, argv, { SRC_DIR, DIST_DIR }); - - return merge(baseConfig, { - module: { - rules: [cssToJavaScriptRule, stylusToJavaScriptRule], - }, - }); -}; diff --git a/README.md b/README.md index c077a47cd0d..d04be0ccb7e 100644 --- a/README.md +++ b/README.md @@ -173,9 +173,7 @@ also supports a number of commands that can be found in their respective | `dev:project ` | Replace with `core`, `ui`, `i18n`, `cornerstone`, `vtk`, etc. | | `test:unit` | Jest multi-project test runner; overall coverage | | **Deploy** | | -| `build`\* | Builds production output for our PWA Viewer | -| `build:package`\* | Builds production `commonjs` output for our Viewer | -| `build:package-all`\* | Builds commonjs bundles for all projects | +| `build`\* | Builds production output for our PWA Viewer | | \* - For more information on our different builds, check out our [Deploy Docs][deployment-docs] @@ -189,24 +187,27 @@ you'll see the following: ```bash . -├── extensions # -│ ├── _example # Skeleton of example extension -│ ├── cornerstone # 2D images w/ Cornerstone.js -│ ├── dicom-html # Structured Reports as HTML in viewport -│ ├── dicom-microscopy # Whole slide microscopy viewing -│ ├── dicom-pdf # View DICOM wrapped PDFs in viewport -│ └── vtk # MPR and Volume support w/ VTK.js +├── extensions # +│ ├── _example # Skeleton of example extension +│ ├── default # +│ ├── cornerstone # 2D images w/ Cornerstone.js +│ ├── dicom-sr # +│ └── measurement-tracking # │ -├── platform # -│ ├── core # Business Logic -│ ├── i18n # Internationalization Support -│ ├── ui # React component library -│ └── viewer # Connects platform and extension projects +├── modes # +│ ├── _example # Skeleton of example mode +│ └── longitudinal # │ -├── ... # misc. shared configuration -├── lerna.json # MonoRepo (Lerna) settings -├── package.json # Shared devDependencies and commands -└── README.md # This file +├── platform # +│ ├── core # Business Logic +│ ├── i18n # Internationalization Support +│ ├── ui # React component library +│ └── viewer # Connects platform and extension projects +│ +├── ... # misc. shared configuration +├── lerna.json # MonoRepo (Lerna) settings +├── package.json # Shared devDependencies and commands +└── README.md # This file ``` Want to better understand why and how we've structured this repository? Read @@ -231,11 +232,7 @@ can [read more about extensions here][ohif-extensions]. | Name | Description | Links | | -------------------------------------------------------------- | ------------------------------------------------------- | ---------------------- | -| [@ohif/extension-cornestone][extension-cornerstone] | 2D image viewing, annotation, and segementation tools | [NPM][cornerstone-npm] | -| [@ohif/extension-dicom-html][extension-dicom-html] | Support for viewing DICOM SR as rendered HTML | [NPM][html-npm] | -| [@ohif/extension-dicom-microscopy][extension-dicom-microscopy] | Whole slide microscopy viewing | [NPM][microscopy-npm] | -| [@ohif/extension-dicom-pdf][extension-dicom-pdf] | View DICOM wrapped PDFs in a viewport | [NPM][pdf-npm] | -| [@ohif/extension-vtk][extension-vtk] | Volume rendering, reconstruction, and 3D visualizations | [NPM][vtk-npm] | +| [@ohif/extension-cornerstone][extension-cornerstone] | 2D image viewing, annotation, and segementation tools | [NPM][cornerstone-npm] | ## Acknowledgments diff --git a/babel.config.js b/babel.config.js index 6e4439438f6..0adf64bed7c 100644 --- a/babel.config.js +++ b/babel.config.js @@ -44,20 +44,3 @@ module.exports = { }, }, }; - -// TODO: Plugins; Aliases -// We don't currently use aliases, but this is a nice snippet that would help -// [ -// 'module-resolver', -// { -// // https://github.com/tleunen/babel-plugin-module-resolver/issues/338 -// // There seem to be a bug with module-resolver with a mono-repo setup: -// // It doesn't resolve paths correctly when using root/alias combo, so we -// // use this function instead. -// resolvePath(sourcePath, currentFile, opts) { -// // This will return undefined if aliases has no key for the sourcePath, -// // in which case module-resolver will fallback on its default behaviour. -// return aliases[sourcePath]; -// }, -// }, -// ], diff --git a/docs/latest/book.json b/docs/latest/book.json index e133fb02b9c..893811d592b 100644 --- a/docs/latest/book.json +++ b/docs/latest/book.json @@ -34,8 +34,12 @@ "text": "Version 1.0.0 (Meteor)" }, { - "value": "https://docs.ohif.org/", + "value": "https://docs.ohif.org/history/v2/", "text": "Version 2.0.0", + }, + { + "value": "https://docs.ohif.org/", + "text": "Version 3.0.0", "selected": true } ] diff --git a/docs/v1/book.json b/docs/v1/book.json index e12679248e6..3040cc4b12d 100644 --- a/docs/v1/book.json +++ b/docs/v1/book.json @@ -35,8 +35,12 @@ "selected": true }, { - "value": "https://docs.ohif.org/", + "value": "https://docs.ohif.org/history/v2/", "text": "Version 2.0.0" + }, + { + "value": "https://docs.ohif.org/", + "text": "Version 3.0.0" } ] } diff --git a/docs/v2/.nojekyll b/docs/v2/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/v2/README.md b/docs/v2/README.md new file mode 100644 index 00000000000..a47453d61dc --- /dev/null +++ b/docs/v2/README.md @@ -0,0 +1,54 @@ +
+
+ Looking for a Live Demo? + Preview The OHIF Viewer +
+
+ + + +
+
+ +> 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. + +# Introduction + +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). + +![OHIF Viewer Screenshot](../assets/img/viewer.png) + +
The OHIF Viewer: A general purpose DICOM Viewer (Live Demo)
+ +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](./development/getting-started.md). +- We're an active, vibrant community. + [Learn how you can be more involved.](./development/contributing.md) +- Feeling lost? Read our [help page](./help.md). + + + + +[ohif-org]: http://www.ohif.org +[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb +[gh-issues]: https://github.com/OHIF/Viewers/issues + diff --git a/docs/v2/SUMMARY.md b/docs/v2/SUMMARY.md new file mode 100644 index 00000000000..b1a4e593ea2 --- /dev/null +++ b/docs/v2/SUMMARY.md @@ -0,0 +1,61 @@ +# OHIF Viewers + +- [Our Process](our-process.md) +- Development + - [Getting Started](development/getting-started.md) + - [Contributing](development/contributing.md) + - [Continuous Integration](development/continous-integration.md) + - [Testing](development/testing.md) +- [Configuring](configuring/index.md) + - [Data Source](configuring/data-source.md) + +--- + +- [Architecture](architecture/index.md) +- [Viewer](viewer/index.md) + - [Configuration](viewer/configuration.md) + - [Themeing](viewer/themeing.md) + - [Internationalization](viewer/internationalization.md) +- [Extensions](extensions/index.md) + - [Registering](extensions/index.md#registering-an-extension) + - [Lifecycle Hooks](extensions/index.md#lifecycle-hooks) + - [preRegistration](extensions/lifecycle/pre-registration.md) + - [Modules](extensions/index.md#modules) + - [Commands](extensions/modules/commands.md) + - [Panel](extensions/modules/panel.md) + - [SOP Class Handler](extensions/modules/sop-class-handler.md) + - [Toolbar](extensions/modules/toolbar.md) + - [Viewport](extensions/modules/viewport.md) + - [Contexts](extensions/index.md#contexts) + - [ExtensionManager](extensions/index.md#extensionmanager) + - [OHIF Maintained](extensions/index.md#maintained-extensions) +- [Services](services/index.md) + - [Default](services/default/index.md) + - [UI](services/ui/index.md) + - [Dialog Service](services/ui/ui-dialog-service.md) + - [Modal Service](services/ui/ui-modal-service.md) + - [Notification Service](services/ui/ui-notification-service.md) + +--- + +- [Deployment](deployment/index.md) + - [Embedded](deployment/index.md#embedded-viewer) + - [Stand-alone](deployment/index.md#stand-alone-viewer) + - [Data](deployment/index.md#data) +- Recipes + - Script Include + - [Embedding the Viewer](deployment/recipes/embedded-viewer.md) + - Stand-Alone + - [Build for Production](deployment/recipes/build-for-production.md) + - [Static](deployment/recipes/static-assets.md) + - [Nginx + Image Archive](deployment/recipes/nginx--image-archive.md) + - [User Account Control](deployment/recipes/user-account-control.md) + - [Google Cloud Healthcare](connecting-to-image-archives/google-cloud-healthcare.md) + +--- + +- [FAQ](faq/index.md) + - [Scope of Project](faq/scope-of-project.md) + - [Browser Support](faq/browser-support.md) + - [PWA vs Packaged](faq/pwa-vs-packaged.md) +- [Help](help.md) diff --git a/docs/v2/architecture/index.md b/docs/v2/architecture/index.md new file mode 100644 index 00000000000..1491faea591 --- /dev/null +++ b/docs/v2/architecture/index.md @@ -0,0 +1,153 @@ +# Architecture + +Looking to extend your instance of the OHIF Viewer? Want learn how to reuse _a +portion_ of the Viewer in your own application? Or maybe you want to get +involved and draft or suggest a new feature? Regardless, you're in the right +place! + +The OHIF Viewer aims to be decoupled, configurable, and extensible; while this +allows our code to be used in more ways, it also increases complexity. Below, we +aim to demistify that complexity by providing insight into how our Viewer is +architected, and the role each of it's dependent libraries plays. + +- [Overview](#overview) +- [Business Logic](#business-logic) +- [Component Library](#react-component-library) +- [Extensions & Configuration](#extensions--configuration) +- [Common Questions](#common-questions) + +## Overview + +The [OHIF Medical Image Viewing Platform][viewers-project] is maintained as a +[`monorepo`][monorepo]. This means that this repository, instead of containing a +single project, contains many projects. If you explore our project structure, +you'll see the following: + +```bash +. +├── extensions +│ ├── _example # Skeleton of example extension +│ ├── cornerstone # 2D images w/ Cornerstone.js +│ ├── dicom-html # Structured Reports as HTML in viewport +│ ├── dicom-microscopy # Whole slide microscopy viewing +│ ├── dicom-pdf # View DICOM wrapped PDFs in viewport +│ └── vtk # MPR and Volume support w/ VTK.js +│ +├── platform +│ ├── core # Business Logic +│ ├── i18n # Internationalization Support +│ ├── ui # React component library +│ └── viewer # Connects platform and extension projects +│ +├── ... # misc. shared configuration +├── lerna.json # MonoRepo (Lerna) settings +├── package.json # Shared devDependencies and commands +└── README.md +``` + +The `platform` directory contains the business logic library, component library, +and the application library that combines them to create a powerful medical +imaging viewer. + +The `extensions` directory contains many packages that can be registered with +`@ohif/core`'s `ExtensionManager` to expand an application's supported features +and functionality. + +![Architecture Diagram](../assets/img/architecture-diagram.png) + +
architecture diagram
+ +This diagram is a conceptual illustration of how the Viewer is architected. + +1. (optional) `extensions` can be registered with `@ohif/core`'s + `ExtensionManager` +2. `@ohif/core` provides bussiness logic and a way for `@ohif/viewer` to access + registered extensions +3. The `@ohif/viewer` composes and provides data to components from our + component library (`@ohif/ui`) +4. The `@ohif/viewer` can be built and served as a stand-alone PWA, or as an + embeddable package ([`@ohif/viewer`][viewer-npm]) + +## Business Logic + +The [`@ohif/core`][core-github] project offers pre-packaged solutions for +features common to Web-based medical imaging viewers. For example: + +- Hotkeys +- DICOM Web requests +- Hanging Protocols +- Managing a study's measurements +- Managing a study's DICOM metadata +- [A flexible pattern for extensions](../extensions/index.md) +- And many others + +It does this while remaining decoupled from any particular view library or +rendering logic. While we use it to power our React Viewer, it can be used with +Vue, React, Vanilla JS, or any number of other frameworks. + +## React Component Library + +[`@ohif/ui`][ui-github] is a React Component library that contains the reusable +components that power the OHIF Viewer. It allows us to build, compose, and test +components in isolation; easing the development process by reducing the need to +stand-up a local PACS with test case data. + +Extension authors can also use these same components when building their +extension's UI; allowing for a consistent look and feel with the rest of the +application. + +[Check out our component library!](https://react.ohif.org/) + +## Extensions & Configuration + +While OHIF maintains several high value and commonly requested features in its +own extensions, there are many instances where one may wish to further extend +the viewer. Some common use cases include: + +- Adding AI/ML tools and insights +- Custom workflows for guided diagnosis +- Collecting specific annotations for training data or reports +- Authentication and granular permissions +- Teleconsultation workflow, image comments, and tracking +- Adding surgical templating tools and reports +- and many others + +We expose common integration points via [extensions](../extensions/index.md) to +make this possible. The viewer and many of our own extensions also offer +[configuration][configuration]. For a list of extensions maintained by OHIF, +[check out this helpful table](../extensions/index.md#maintained-extensions). + +If you find yourself thinking "I wish the Viewer could do X", and you can't +accomplish it with an extension today, create a GitHub issue! We're actively +looking for ways to improve our extensibility ^\_^ + +[Click here to read more about extensions!](../extensions/index.md) + +## Common Questions + +> When should I use the packaged source `@ohif/viewer` versus building a PWA +> from the source? + +... + +> Can I create my own Viewer using Vue.js or Angular.js? + +You can, but you will not be able to leverage as much of the existing code and +components. `@ohif/core` could still be used for business logic, and to provide +a model for extensions. `@ohif/ui` would then become a guide for the components +you would need to recreate. + + + + +[monorepo]: https://github.com/OHIF/Viewers/issues/768 +[viewers-project]: https://github.com/OHIF/Viewers +[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer +[pwa]: https://developers.google.com/web/progressive-web-apps/ +[configuration]: ../configuring/index.md +[extensions]: ../extensions/index.md +[core-github]: https://github.com/OHIF/viewers/platform/core +[ui-github]: https://github.com/OHIF/Viewers/tree/master/platform/ui + diff --git a/docs/v2/assets/designs/architecture-diagram b/docs/v2/assets/designs/architecture-diagram new file mode 100644 index 00000000000..bbf6cf58b7a Binary files /dev/null and b/docs/v2/assets/designs/architecture-diagram differ diff --git a/docs/v2/assets/designs/canny-full.fig b/docs/v2/assets/designs/canny-full.fig new file mode 100644 index 00000000000..8756e9f7951 Binary files /dev/null and b/docs/v2/assets/designs/canny-full.fig differ diff --git a/docs/v2/assets/designs/cloud.svg b/docs/v2/assets/designs/cloud.svg new file mode 100644 index 00000000000..ad04389c61a --- /dev/null +++ b/docs/v2/assets/designs/cloud.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/docs/v2/assets/designs/embedded-viewer-diagram b/docs/v2/assets/designs/embedded-viewer-diagram new file mode 100644 index 00000000000..182ad232399 Binary files /dev/null and b/docs/v2/assets/designs/embedded-viewer-diagram differ diff --git a/docs/v2/assets/designs/nginx-image-archive.fig b/docs/v2/assets/designs/nginx-image-archive.fig new file mode 100644 index 00000000000..460ae95dd35 Binary files /dev/null and b/docs/v2/assets/designs/nginx-image-archive.fig differ diff --git a/docs/v2/assets/designs/npm-logo-red.svg b/docs/v2/assets/designs/npm-logo-red.svg new file mode 100644 index 00000000000..8e4aac5d237 --- /dev/null +++ b/docs/v2/assets/designs/npm-logo-red.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/docs/v2/assets/designs/scope-of-project.fig b/docs/v2/assets/designs/scope-of-project.fig new file mode 100644 index 00000000000..5eb82e561df Binary files /dev/null and b/docs/v2/assets/designs/scope-of-project.fig differ diff --git a/docs/v2/assets/designs/user-access-control-request-flow.fig b/docs/v2/assets/designs/user-access-control-request-flow.fig new file mode 100644 index 00000000000..8982a8fedd5 Binary files /dev/null and b/docs/v2/assets/designs/user-access-control-request-flow.fig differ diff --git a/docs/v2/assets/img/WORKFLOW_DEPLOY.png b/docs/v2/assets/img/WORKFLOW_DEPLOY.png new file mode 100644 index 00000000000..3e562a7971d Binary files /dev/null and b/docs/v2/assets/img/WORKFLOW_DEPLOY.png differ diff --git a/docs/v2/assets/img/WORKFLOW_PR_CHECKS.png b/docs/v2/assets/img/WORKFLOW_PR_CHECKS.png new file mode 100644 index 00000000000..f9c4a568b6c Binary files /dev/null and b/docs/v2/assets/img/WORKFLOW_PR_CHECKS.png differ diff --git a/docs/v2/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png b/docs/v2/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png new file mode 100644 index 00000000000..54b0aa39fbb Binary files /dev/null and b/docs/v2/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png differ diff --git a/docs/v2/assets/img/WORKFLOW_RELEASE.png b/docs/v2/assets/img/WORKFLOW_RELEASE.png new file mode 100644 index 00000000000..f3c2a806973 Binary files /dev/null and b/docs/v2/assets/img/WORKFLOW_RELEASE.png differ diff --git a/docs/v2/assets/img/architecture-diagram.png b/docs/v2/assets/img/architecture-diagram.png new file mode 100644 index 00000000000..1c43d010826 Binary files /dev/null and b/docs/v2/assets/img/architecture-diagram.png differ diff --git a/docs/v2/assets/img/cornerstone-tools-link.gif b/docs/v2/assets/img/cornerstone-tools-link.gif new file mode 100644 index 00000000000..22fde7a7f10 Binary files /dev/null and b/docs/v2/assets/img/cornerstone-tools-link.gif differ diff --git a/docs/v2/assets/img/dialog-example.gif b/docs/v2/assets/img/dialog-example.gif new file mode 100644 index 00000000000..b6f9754c477 Binary files /dev/null and b/docs/v2/assets/img/dialog-example.gif differ diff --git a/docs/v2/assets/img/embedded-viewer-diagram.png b/docs/v2/assets/img/embedded-viewer-diagram.png new file mode 100644 index 00000000000..426cb7ab855 Binary files /dev/null and b/docs/v2/assets/img/embedded-viewer-diagram.png differ diff --git a/docs/v2/assets/img/extensions-diagram.png b/docs/v2/assets/img/extensions-diagram.png new file mode 100644 index 00000000000..c71be869263 Binary files /dev/null and b/docs/v2/assets/img/extensions-diagram.png differ diff --git a/docs/v2/assets/img/extensions-panel.gif b/docs/v2/assets/img/extensions-panel.gif new file mode 100644 index 00000000000..fd173bd70a4 Binary files /dev/null and b/docs/v2/assets/img/extensions-panel.gif differ diff --git a/docs/v2/assets/img/extensions-toolbar-nested.gif b/docs/v2/assets/img/extensions-toolbar-nested.gif new file mode 100644 index 00000000000..d89a75ab3ed Binary files /dev/null and b/docs/v2/assets/img/extensions-toolbar-nested.gif differ diff --git a/docs/v2/assets/img/extensions-toolbar.gif b/docs/v2/assets/img/extensions-toolbar.gif new file mode 100644 index 00000000000..88c313f3d93 Binary files /dev/null and b/docs/v2/assets/img/extensions-toolbar.gif differ diff --git a/docs/v2/assets/img/extensions-viewport.png b/docs/v2/assets/img/extensions-viewport.png new file mode 100644 index 00000000000..0ecffda0693 Binary files /dev/null and b/docs/v2/assets/img/extensions-viewport.png differ diff --git a/docs/v2/assets/img/homePage.png b/docs/v2/assets/img/homePage.png new file mode 100644 index 00000000000..9ae0624cc5c Binary files /dev/null and b/docs/v2/assets/img/homePage.png differ diff --git a/docs/v2/assets/img/jwt-explained.png b/docs/v2/assets/img/jwt-explained.png new file mode 100644 index 00000000000..f26509a16fa Binary files /dev/null and b/docs/v2/assets/img/jwt-explained.png differ diff --git a/docs/v2/assets/img/keycloak-default-theme.png b/docs/v2/assets/img/keycloak-default-theme.png new file mode 100644 index 00000000000..0ea77f9655f Binary files /dev/null and b/docs/v2/assets/img/keycloak-default-theme.png differ diff --git a/docs/v2/assets/img/keycloak-ohif-theme.png b/docs/v2/assets/img/keycloak-ohif-theme.png new file mode 100644 index 00000000000..ad060f262a6 Binary files /dev/null and b/docs/v2/assets/img/keycloak-ohif-theme.png differ diff --git a/docs/v2/assets/img/lesionTracker.png b/docs/v2/assets/img/lesionTracker.png new file mode 100644 index 00000000000..effc7da9064 Binary files /dev/null and b/docs/v2/assets/img/lesionTracker.png differ diff --git a/docs/v2/assets/img/loading-study.gif b/docs/v2/assets/img/loading-study.gif new file mode 100644 index 00000000000..c010830ee31 Binary files /dev/null and b/docs/v2/assets/img/loading-study.gif differ diff --git a/docs/v2/assets/img/locizeSponsor.svg b/docs/v2/assets/img/locizeSponsor.svg new file mode 100644 index 00000000000..1139aa2c760 --- /dev/null +++ b/docs/v2/assets/img/locizeSponsor.svg @@ -0,0 +1,187 @@ + + + + Custom Preset 2 Copy + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/v2/assets/img/modal-example.gif b/docs/v2/assets/img/modal-example.gif new file mode 100644 index 00000000000..834705de178 Binary files /dev/null and b/docs/v2/assets/img/modal-example.gif differ diff --git a/docs/v2/assets/img/netlify-drop.gif b/docs/v2/assets/img/netlify-drop.gif new file mode 100644 index 00000000000..98634e088e5 Binary files /dev/null and b/docs/v2/assets/img/netlify-drop.gif differ diff --git a/docs/v2/assets/img/nginx-image-archive.png b/docs/v2/assets/img/nginx-image-archive.png new file mode 100644 index 00000000000..bd75479652a Binary files /dev/null and b/docs/v2/assets/img/nginx-image-archive.png differ diff --git a/docs/v2/assets/img/notification-example.gif b/docs/v2/assets/img/notification-example.gif new file mode 100644 index 00000000000..34d564cb481 Binary files /dev/null and b/docs/v2/assets/img/notification-example.gif differ diff --git a/docs/v2/assets/img/open-graph.png b/docs/v2/assets/img/open-graph.png new file mode 100644 index 00000000000..5b881abdfd8 Binary files /dev/null and b/docs/v2/assets/img/open-graph.png differ diff --git a/docs/v2/assets/img/scope-of-project.png b/docs/v2/assets/img/scope-of-project.png new file mode 100644 index 00000000000..6daac8bee38 Binary files /dev/null and b/docs/v2/assets/img/scope-of-project.png differ diff --git a/docs/v2/assets/img/services.png b/docs/v2/assets/img/services.png new file mode 100644 index 00000000000..569c046c0df Binary files /dev/null and b/docs/v2/assets/img/services.png differ diff --git a/docs/v2/assets/img/surge-deploy.gif b/docs/v2/assets/img/surge-deploy.gif new file mode 100644 index 00000000000..545f0686358 Binary files /dev/null and b/docs/v2/assets/img/surge-deploy.gif differ diff --git a/docs/v2/assets/img/ui-services.png b/docs/v2/assets/img/ui-services.png new file mode 100644 index 00000000000..dd530637775 Binary files /dev/null and b/docs/v2/assets/img/ui-services.png differ diff --git a/docs/v2/assets/img/user-access-control-request-flow.png b/docs/v2/assets/img/user-access-control-request-flow.png new file mode 100644 index 00000000000..573c835038e Binary files /dev/null and b/docs/v2/assets/img/user-access-control-request-flow.png differ diff --git a/docs/v2/assets/img/viewer.png b/docs/v2/assets/img/viewer.png new file mode 100644 index 00000000000..21eacd59a51 Binary files /dev/null and b/docs/v2/assets/img/viewer.png differ diff --git a/docs/v2/assets/img/worklist.png b/docs/v2/assets/img/worklist.png new file mode 100644 index 00000000000..3f126c784e1 Binary files /dev/null and b/docs/v2/assets/img/worklist.png differ diff --git a/docs/v2/book.json b/docs/v2/book.json new file mode 100644 index 00000000000..ab5882b85cf --- /dev/null +++ b/docs/v2/book.json @@ -0,0 +1,54 @@ +{ + "title": "OHIF", + "description": "Documentation for the OHIF framework", + "gitbook": ">3.0.0", + "plugins": [ + "edit-link", + "theme-cornerstone", + "-fontsettings", + "github", + "ga", + "sitemap", + "anchors", + "versions" + ], + "pluginsConfig": { + "edit-link": { + "base": "https://github.com/OHIF/Viewers/edit/master/docs", + "label": "Edit This Page" + }, + "github": { + "url": "https://github.com/OHIF/Viewers" + }, + "ga": { + "token": "UA-110573590-2" + }, + "sitemap": { + "hostname": "https://docs.ohif.org" + }, + "versions": { + "gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json", + "options": [ + { + "value": "https://docs.ohif.org/history/v1/", + "text": "Version 1.0.0 (Meteor)" + }, + { + "value": "https://docs.ohif.org/history/v2/", + "text": "Version 2.0.0", + "selected": true + }, + { + "value": "https://docs.ohif.org/", + "text": "Version 3.0.0" + } + ] + } + }, + "links": { + "sharing": { + "facebook": false, + "twitter": false + } + } +} diff --git a/docs/v2/configuring/data-source.md b/docs/v2/configuring/data-source.md new file mode 100644 index 00000000000..1f9cbbb5b51 --- /dev/null +++ b/docs/v2/configuring/data-source.md @@ -0,0 +1,170 @@ +# Data Source + +After following the steps outlined in +[Getting Started](./../development/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 + +- Docker + - [Docker for Mac](https://docs.docker.com/docker-for-mac/) + - [Docker for Windows (recommended)](https://docs.docker.com/docker-for-windows/) + - [Docker Toolbox for Windows](https://docs.docker.com/toolbox/toolbox_install_windows/) + +_Not sure if you have `docker` installed already? Try running `docker --version` +in command prompt or terminal_ + +> If you are using `Docker Toolbox` you need to change the _PROXY_DOMAIN_ +> parameter in _platform/viewer/package.json_ to http://192.168.99.100:8042 or +> the ip docker-machine ip throws. This is the value [`WebPack`][webpack-proxy] +> uses to proxy requests + +### 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:8042/app/explorer.html) at + `http://localhost:8042/app/explorer.html` 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 +[`/.docker/Nginx-Orthanc/`][orthanc-docker-compose], 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 repository's root directory, and run: + +```bash +# If you haven't already, enable yarn workspaces +yarn config set workspaces-experimental true + +# 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](./index.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 NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js -w +``` + +- `cross-env` sets three environment variables + - PROXY_TARGET: `/dicom-web` + - PROXY_DOMAIN: `http://localhost:8042` + - APP_CONFIG: `config/docker_nginx-orthanc.js` +- `webpack-dev-server` runs using the `.webpack/webpack.pwa.js` configuration + file. It will watch for changes and update as we develop. + +`PROXY_TARGET` and `PROXY_DOMAIN` tell our development server to proxy requests +to `Orthanc`. This allows us to bypass CORS issues that normally occur when +requesting resources that live at a different domain. + +The `APP_CONFIG` value tells our app which file to load on to `window.config`. +By default, our app uses the file at +`/platform/viewer/public/config/default.js`. Here is what that +configuration looks like: + +```js +window.config = { + routerBasename: '/', + servers: { + dicomWeb: [ + { + name: 'Orthanc', + wadoUriRoot: 'http://localhost:8899/wado', + qidoRoot: 'http://localhost:8899/dicom-web', + wadoRoot: 'http://localhost:8899/dicom-web', + qidoSupportsIncludeField: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + }, + ], + }, +}; +``` + +To learn more about how you can configure the OHIF Viewer, check out our +[Configuration Guide](./index.md). + +## Open Source DICOM Image Archives + +Our example uses `Orthanc`, but there are a lot of options available to you. +Here are some of the more popular ones: + +| 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._ + + + + +[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 +[webpack-proxy]: https://webpack.js.org/configuration/dev-server/#devserverproxy +[orthanc-docker-compose]: https://github.com/OHIF/Viewers/tree/master/.docker/Nginx-Orthanc + +[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/ + diff --git a/docs/v2/configuring/index.md b/docs/v2/configuring/index.md new file mode 100644 index 00000000000..73b65e4a8a3 --- /dev/null +++ b/docs/v2/configuring/index.md @@ -0,0 +1,126 @@ +# Configuration + +> This step assumes you have an imaging archive. If you need assistance setting +> one up, check out the [`Data Source` Guide](./data-source.md) or a deployment +> recipe that contains an open Image Archive + +- [Overview](#overview) + - [Configuration Files](#configuration-files) + - [Environment Variables](#environment-variables) +- [How do I configure my project?](#how-do-i-configure-my-project) + +## Overview + +### Configuration Files + +The configuration for our viewer is in the `platform/viewer/public/config` +directory. Our build process knows which configuration file to use based on the +`APP_CONFIG` environment variable. By default, its value is +[`config/default.js`][default-config]. The majority of the viewer's features, +and registered extension's features, are configured using this file. + +**Embedded Use Note:** + +Alternatively, when using the `umd` bundle for embedded use cases, these same +values are what you'll pass to `installViewer` method: + +`OHIFStandaloneViewer.installViewer(window.config)` + +### Environment Variables + +We use environment variables at build and dev time to change the Viewer's +behavior. We can update the `HTML_TEMPLATE` to easily change which extensions +are registered, and specify a different `APP_CONFIG` to connect to an +alternative data source (or even specify different default hotkeys). + +| Environment Variable | Description | Default | +| -------------------- | -------------------------------------------------------------------------------------------------- | ------------------- | +| `HTML_TEMPLATE` | Which [HTML template][html-templates] to use as our web app's entry point. Specific to PWA builds. | `index.html` | +| `PUBLIC_URL` | The route relative to the host that the app will be served from. Specific to PWA builds. | `/` | +| `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` | +| `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` | +| `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` | + +## How do I configure my project? + +The simplest way is to update the existing default config: + +_/platform/viewer/public/config/default.js_ + +```js +window.config = { + routerBasename: '/', + servers: { + dicomWeb: [ + { + name: 'DCM4CHEE', + wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', + qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + qidoSupportsIncludeField: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + }, + ], + }, +}; +``` + +The configuration can also be written as a JS Function in case you need to inject dependencies like external services: + +```js +window.config = ({ servicesManager } = {}) => { + const { UIDialogService } = servicesManager.services; + return { + cornerstoneExtensionConfig: { + tools: { + ArrowAnnotate: { + configuration: { + getTextCallback: (callback, eventDetails) => UIDialogService.create({... + } + } + }, + }, + routerBasename: '/', + servers: { + dicomWeb: [ + { + name: 'DCM4CHEE', + wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', + qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + qidoSupportsIncludeField: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + }, + ], + }, + }; +}; +``` + +You can also create a new config file and specify its path relative to the build +output's root by setting the `APP_CONFIG` environment variable. You can set the +value of this environment variable a few different ways: + +- ~[Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)~ + - Previous `react-scripts` functionality that we need to duplicate with + `dotenv-webpack` +- ~[Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)~ + - Previous `react-scripts` functionality that we need to duplicate with + `dotenv-webpack` +- Using the `cross-env` package in an npm script: + - `"build": "cross-env APP_CONFIG=config/my-config.js react-scripts build"` + +After updating the configuration, `yarn run build` to generate updated build +output. + + + + +[default-config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js +[html-templates]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/html-templates +[config-files]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config + diff --git a/docs/v2/connecting-to-image-archives/google-cloud-healthcare.md b/docs/v2/connecting-to-image-archives/google-cloud-healthcare.md new file mode 100644 index 00000000000..6fce2746b85 --- /dev/null +++ b/docs/v2/connecting-to-image-archives/google-cloud-healthcare.md @@ -0,0 +1,71 @@ +# Google Cloud Healthcare + +> The [Google Cloud Healthcare API](https://cloud.google.com/healthcare/) is a +> powerful option for storing medical imaging data in the cloud. + +An alternative to deploying your own PACS is to use a software-as-a-service +provider such as Google Cloud. The Cloud Healthcare API promises to be a +scalable, secure, cost effective image storage solution for those willing to +store their data in the cloud. It offers an +[almost-entirely complete DICOMWeb API](https://cloud.google.com/healthcare/docs/dicom) +which requires tokens generated via the +[OAuth 2.0 Sign In flow](https://developers.google.com/identity/sign-in/web/sign-in). +Images can even be transcoded on the fly if this is desired. + +## Setup a Google Cloud Healthcare Project + +- Create a Google Cloud account +- Create a project in Google Cloud +- Enable the [Cloud Healthcare API](https://cloud.google.com/healthcare/) for + your project. + - (Optional): Create a Dataset and Data Store for storing your DICOM data +- Enable the + [Cloud Resource Manager API](https://cloud.google.com/resource-manager/) for + your project. + - _Note:_ If you are having trouble finding the APIs, use the search box at + the top of the Cloud console. +- Go to APIs & Services > Credentials to create an OAuth Consent screen and fill + in your application details. + - Under Scopes for Google APIs, click "manually paste scopes". + - Add the following scopes: + - `https://www.googleapis.com/auth/cloudplatformprojects.readonly` + - `https://www.googleapis.com/auth/cloud-healthcare` +- Go to APIs & Services > Credentials to create a new set of credentials: + + - Choose the "Web Application" type + - Set up an + [OAuth 2.0 Client ID](https://support.google.com/cloud/answer/6158849?hl=en) + - Add your domain (e.g. `http://localhost:3000`) to Authorized JavaScript + origins. + - Add your domain, plus `callback` (e.g. `http://localhost:3000/callback`) to + Authorized Redirect URIs. + - Save your Client ID for later. + +- (Optional): Enable Public Datasets that are being hosted by Google: + https://cloud.google.com/healthcare/docs/resources/public-datasets/ + +## Run the viewer with your OAuth Client ID + +1. Open the `config/google.js` file and change `YOURCLIENTID` to your Client ID + value. +1. Run the OHIF Viewer using the config/google.js configuration file + +```bash +cd OHIFViewer +yarn install +APP_CONFIG=config/google.js yarn run dev +``` + +## Running via Docker + +The OHIF Viewer Docker container can be connected to Google Cloud Healthcare by +providing a Client ID at runtime. This is a very simple method to get up and +running. + +1. Install Docker (https://www.docker.com/) +1. Run the Docker container, providing a Client ID as an environment variable. + Client IDs look like `xyz.apps.googleusercontent.com`. + +```bash +docker run --env CLIENT_ID=$CLIENT_ID --publish 5000:80 ohif/viewer:latest +``` diff --git a/docs/v2/deployment/_embedded-viewer-diagram.md b/docs/v2/deployment/_embedded-viewer-diagram.md new file mode 100644 index 00000000000..6af9e0a3043 --- /dev/null +++ b/docs/v2/deployment/_embedded-viewer-diagram.md @@ -0,0 +1,4 @@ +
+ Embedded Viewer Diagram +
embedded viewer diagram
+
diff --git a/docs/v2/deployment/_nginx-image-archive-diagram.md b/docs/v2/deployment/_nginx-image-archive-diagram.md new file mode 100644 index 00000000000..780e4f766be --- /dev/null +++ b/docs/v2/deployment/_nginx-image-archive-diagram.md @@ -0,0 +1,4 @@ +
+ request flow example +
simplified request flow diagram
+
diff --git a/docs/v2/deployment/_user-account-control-flow-diagram.md b/docs/v2/deployment/_user-account-control-flow-diagram.md new file mode 100644 index 00000000000..b6937cd6b11 --- /dev/null +++ b/docs/v2/deployment/_user-account-control-flow-diagram.md @@ -0,0 +1,4 @@ +
+ request flow example +
simplified request flow diagram
+
diff --git a/docs/v2/deployment/index.md b/docs/v2/deployment/index.md new file mode 100644 index 00000000000..8959fac7d66 --- /dev/null +++ b/docs/v2/deployment/index.md @@ -0,0 +1,305 @@ +# Deployment + +The OHIF Viewer can be embedded in other web applications via it's [packaged +script source][viewer-npm], or served up as a stand-alone PWA ([progressive web +application][pwa-url]) by building and hosting a collection of static assets. In +either case, you will need to configure your instance of the Viewer so that it +can connect to your data source (the database or PACS that provides the data +your Viewer will display). + +## Overview + +Our goal is to make deployment as simple and painless as possible; however, +there is an inherent amount of complexity in configuring and deploying web +applications. If you find yourself a little lost, please don't hesitate to +[reach out for help](/help.md) + +## Deployment Scenarios + +### Embedded Viewer + +The quickest and easiest way to get the OHIF Viewer up and running is to embed +it into an existing web application. It allows us to forego a "build step", and +add a powerful medical imaging viewer to an existing web page using only a few +include tags. + +- Read more about it here: [Embedded Viewer](./recipes/embedded-viewer.md) +- And check out our [live demo on CodeSandbox][code-sandbox] + +{% include "./_embedded-viewer-diagram.md" %} + +### Stand-alone Viewer + +Deploying the OHIF Viewer as a stand-alone web application provides many +benefits, but comes at the cost of time and complexity. Some benefits include: + +_Today:_ + +- Leverage [extensions](/extensions/index.md) to drop-in powerful new features +- Add routes and customize the viewer's workflow +- Finer control over styling and whitelabeling + +_In the future:_ + +- The ability to package the viewer for [App Store distribution][app-store] +- Leverage `service-workers` for offline support and speed benefits from caching + +#### Hosted Static Assets + +At the end of the day, a production OHIF Viewer instance is a collection of +HTML, CSS, JS, Font Files, and Images. We "build" those files from our +`source code` with configuration specific to our project. We then make those +files publicly accessible by hosting them on a Web Server. + +If you have not deployed a web application before, this may be a good time to +[reach out for help](/help.md), as these steps assume prior web development and +deployment experience. + +##### Part 1 - Build Production Assets + +"Building", or creating, the files you will need is the same regardless of the +web host you choose. You can find detailed instructions on how to configure and +build the OHIF Viewer in our +["Build for Production" guide](./recipes/build-for-production.md). + +##### Part 2 - Host Your App + +There are a lot of [benefits to hosting static assets][host-static-assets] over +dynamic content. You can find instructions on how to host your build's output +via one of these guides: + +_Drag-n-drop_ + +- [Netlify: Drop](/deployment/recipes/static-assets.md#netlify-drop) + +_Easy_ + +- [Surge.sh](/deployment/recipes/static-assets.md#surgesh) +- [GitHub Pages](/deployment/recipes/static-assets.md#github-pages) + +_Advanced_ + +- [AWS S3 + Cloudfront](/deployment/recipes/static-assets.md#aws-s3--cloudfront) +- [GCP + Cloudflare](/deployment/recipes/static-assets.md#gcp--cloudflare) +- [Azure](/deployment/recipes/static-assets.md#azure) + +## Data + +The OHIF Viewer is able to connect to any data source that implements the [DICOM +Web Standard][dicom-web-standard]. [DICOM Web][dicom-web] refers to RESTful +DICOM Services -- a recently standardized set of guidelines for exchanging +medical images and imaging metadata over the internet. Not all archives fully +support it yet, but it is gaining wider adoption. + +### Configure Connection + +If you have an existing archive and intend to host the OHIF Viewer at the same +domain name as your archive, then connecting the two is as simple as following +the steps layed out in our +[Configuration Essentials Guide](./../configuring/index.md). + +#### What if I don't have an imaging archive? + +We provide some guidance on configuring a local image archive in our +[Data Source Essentials](./../configuring/data-source.md) guide. Hosting an +archive remotely is a little trickier. You can check out some of our +[advanced recipes](#recipes) for modeled setups that may work for you. + +#### What if I intend to host the OHIF Viewer at a different domain? + +There are two important steps to making sure this setup works: + +1. Your Image Archive needs to be exposed, in some way, to the open web. This + can be directly, or through a `reverse proxy`, but the Viewer needs _some + way_ to request it's data. +2. \* Your Image Archive needs to have appropriate CORS (Cross-Origin Resource + Sharing) Headers + +> \* Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional +> HTTP headers to tell a browser to let a web application running at one origin +> (domain) have permission to access selected resources from a server at a +> different origin. - [MDN Web Docs: Web - Http - CORS][cors] + +Most image archives do not provide either of these features "out of the box". +It's common to use IIS, Nginx, or Apache to route incoming requests and append +appropriate headers. You can find an example of this setup in our +[Nginx + Image Archive Deployment Recipe](./recipes/nginx--image-archive.md). + +#### What if my archive doesn't support DicomWeb? + +It's possible to supply all Study data via JSON format, in the event you do not have a DicomWeb endpoint. +You can host all of the relevant files on any web accessible server (Amazon S3, Azure Blob Storage, Local file server etc.) + +This JSON is supplied via the '?url=' query parameter. +It should reference an endpoint that returns **application/json** formatted text. + +If you do not have an API, you can simply return a text file containing the JSON from any web server. + + +You tell the OHIF viewer to use JSON by appending the `'?url='` query to the `/Viewer` route: + +eg. `https://my-test-ohif-server/viewer?url=https://my-json-server/study-uid.json` + +The returned JSON object must contain a single root object with a 'studies' array. + + +*Sample JSON format:* +```JSON +{ + "studies": [ + { + "StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78", + "StudyDescription": "BRAIN SELLA", + "StudyDate": "20010108", + "StudyTime": "120022", + "PatientName": "MISTER^MR", + "PatientId": "832040", + "series": [ + { + "SeriesDescription": "SAG T-1", + "SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121", + "SeriesNumber": 2, + "SeriesDate": "20010108", + "SeriesTime": "120318", + "Modality": "MR", + "instances": [ + { + "metadata": { + "Columns": 512, + "Rows": 512, + "InstanceNumber": 3, + "AcquisitionNumber": 0, + "PhotometricInterpretation": "MONOCHROME2", + "BitsAllocated": 16, + "BitsStored": 16, + "PixelRepresentation": 1, + "SamplesPerPixel": 1, + "PixelSpacing": [0.390625, 0.390625], + "HighBit": 15, + "ImageOrientationPatient": [0,1,0,0,0,-1], + "ImagePositionPatient": [11.600000,-92.500000, 98.099998], + "FrameOfReferenceUID": "1.2.840.113619.2.5.1762583153.223134.978956938.470", + "ImageType": ["ORIGINAL","PRIMARY","OTHER"], + "Modality": "MR", + "SOPInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.124", + "SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121", + "StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78" + }, + "url": "dicomweb://s3.amazonaws.com/lury/MRStudy/1.2.840.113619.2.5.1762583153.215519.978957063.124.dcm" + } + ] + } + ] + } + ] +} +``` +More info on this JSON format can be found here [Issue #1500](https://github.com/OHIF/Viewers/issues/1500) + + +**Implementation Notes:** + +1. When hosting the viewer, you will also need to host a /viewer route on the server - or the browser may not be able to find the route. +2. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomweb:` in order for the cornerstone image loader to retrieve it correctly. + eg. `https://image-server/my-image.dcm` ---> `dicomweb:https://image-server/my-image.dcm` +3. The JSON format above is compatible with >= v3.7.8 of the application. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet. +4. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500). + +*CORS Issues (Cross-Origin Resource Sharing)* + +If you host a JSON API or Images on a different domain from the the app itself, you will likely have CORS issues. This will also happen when testing from Localhost and reaching out to remote servers. +Even if the domain is the same, different ports, subdomains or protocols (https vs http) will also cause CORS errors. +You will to need add a configuration on each server hosting these assets to allow your App server origin. + +For example: + +Lets assume your application is hosted on `https://my-ohif-server.com`. + +Your JSON API is hosted on `https://my-json-api.aws.com` + +And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com` + +When you first start your application, browsing to `https://my-ohif-server.com/viewer?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`. + +Adding a setting on the JSON server to allow the CORS origin = `https://my-ohif-server.com` should solve this. + +Next, you will likely get a similar CORS error, as the browser tries to go to `https://my-s3-bucket.aws.com`. +You will need to go to the S3 bucket configuration, and add a CORS setting to allow origin = `https://my-ohif-server.com`. + +Essentially, whenever the application connects to a remote resource, you will need to add the applications url to the allowed CORS Origins on that resource. Adding an origin similar to https://localhost:3000 will also allow for local testing. + + +### Securing Your Data + +> Feeling lost? Securing your data is important, and it can be hard to tell if +> you've gotten it right. Don't hesitate to work with professional auditors, or +> [enlist help from experts](./../help.md). + +The OHIF Viewer can be configured to work with authorization servers that +support one or more of the OpenID-Connect authorization flows. The Viewer finds +it's OpenID-Connect settings on the `oidc` configuration key. You can set these +values following the instructions laid out in the +[Configuration Essentials Guide](./../configuring/index.md). + +_Example OpenID-Connect Settings:_ + +```js +window.config = { + ... + oidc: [ + { + // ~ REQUIRED + // Authorization Server URL + authority: 'http://127.0.0.1/auth/realms/ohif', + client_id: 'ohif-viewer', + redirect_uri: 'http://127.0.0.1/callback', // `OHIFStandaloneViewer.js` + response_type: 'code', // "Authorization Code Flow" + scope: 'openid', // email profile openid + // ~ OPTIONAL + post_logout_redirect_uri: '/logout-redirect.html', + }, + ], +} +``` + +You can find an example of this setup in our +[User Account Control Deployment Recipe](./recipes/user-account-control.md). + +#### Choosing a Flow for the Viewer + +In general, we recommend using the "Authorization Code Flow" ( [see +`response_type=code` here][code-flows]); however, the "Implicit Flow" ( [see +`response_type=token` here][code-flows]) can work if additonal precautions are +taken. If the flow you've chosen produces a JWT Token, it's validity can be used +to secure access to your Image Archive as well. + +### Recipes + +We've included a few recipes for common deployment scenarios. There are many, +many possible configurations, so please don't feel limited to these setups. +Please feel free to suggest or contribute your own recipes. + +- Script Include + - [Embedding the Viewer](./recipes/embedded-viewer.md) +- Stand-Alone + - [Build for Production](./recipes/build-for-production.md) + - [Static](./recipes/static-assets.md) + - [Nginx + Image Archive](./recipes/nginx--image-archive.md) + - [User Account Control](./recipes/user-account-control.md) + + + + +[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer +[pwa-url]: https://developers.google.com/web/progressive-web-apps/ +[static-assets-url]: https://www.maxcdn.com/one/visual-glossary/static-content/ +[app-store]: https://medium.freecodecamp.org/i-built-a-pwa-and-published-it-in-3-app-stores-heres-what-i-learned-7cb3f56daf9b +[dicom-web-standard]: https://www.dicomstandard.org/dicomweb/ +[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb +[host-static-assets]: https://www.netlify.com/blog/2016/05/18/9-reasons-your-site-should-be-static/ +[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +[code-flows]: https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660 +[code-sandbox]: https://codesandbox.io/s/viewer-script-tag-tprch + diff --git a/docs/v2/deployment/recipes/build-for-production.md b/docs/v2/deployment/recipes/build-for-production.md new file mode 100644 index 00000000000..5328a79635b --- /dev/null +++ b/docs/v2/deployment/recipes/build-for-production.md @@ -0,0 +1,135 @@ +# Build for Production + +> If you've already followed the +> ["Getting Started" Guide](/development/getting-started.md), you can skip ahead +> to [Configuration](#configuration) + +## Overview + +### Build Machine Requirements + +- [Node.js & NPM](https://nodejs.org/en/download/) +- [Yarn](https://yarnpkg.com/lang/en/docs/install/) +- [Git](https://www.atlassian.com/git/tutorials/install-git) + +### Getting the Code + +_With Git:_ + +```bash +# Clone the remote repository to your local machine +git clone https://github.com/OHIF/Viewers.git +``` + +More on: _[`git clone`](https://git-scm.com/docs/git-clone), +[`git checkout`](https://git-scm.com/docs/git-checkout)_ + +_From .zip:_ + +[OHIF/Viewers: react.zip](https://github.com/OHIF/Viewers/archive/master.zip) + +### Restore Dependencies & Build + +Open your terminal, and navigate to the directory containing the source files. +Next run these commands: + +```js +// If you haven't already, enable yarn workspaces +yarn config set workspaces-experimental true + +// Restore dependencies +yarn install + +// Build source code for production +yarn run build +``` + +If everything worked as expected, you should have a new `dist/` directory in the +project's folder. It should roughly resemble the following: + +```bash +platform/viewer/dist/ +├── app-config.js +├── app.bundle.js +├── app.css +├── index.html +├── manifest.json +├── service-worker.js +└── ... +``` + +By default, the build output will connect to OHIF's publicly accessible PACS. If +this is your first time setting up the OHIF Viewer, it is recommended that you +test with these default settings. After testing, you can find instructions on +how to configure the project for your own imaging archive below. + +### Configuration + +The configuration for our viewer is in the `platform/viewer/public/config` +directory. Our build process knows which configuration file to use based on the +`APP_CONFIG` environment variable. By default, its value is +[`config/default.js`][default-config]. The majority of the viewer's features, +and registered extension's features, are configured using this file. + +The easiest way to apply your own configuration is to modify the `default.js` +file. For more advanced cofiguration options, check out our +[configuration essentials guide](/configuring/index.md). + +## Next Steps + +### Deploying Build Output + +_Drag-n-drop_ + +- [Netlify: Drop](/deployment/recipes/static-assets.md#netlify-drop) + +_Easy_ + +- [Surge.sh](/deployment/recipes/static-assets.md#surgesh) +- [GitHub Pages](/deployment/recipes/static-assets.md#github-pages) + +_Advanced_ + +- [AWS S3 + Cloudfront](/deployment/recipes/static-assets.md#aws-s3--cloudfront) +- [GCP + Cloudflare](/deployment/recipes/static-assets.md#gcp--cloudflare) +- [Azure](/deployment/recipes/static-assets.md#azure) + +### Testing Build Output Locally + +A quick way to test your build output locally is to spin up a small webserver. +You can do this by running the following commands in the `dist/` output +directory: + +```js +// Install http-server as a globally available package +yarn global add http-server + +// Serve the files in our current directory +// Accessible at: `http://localhost:8080` +http-server +``` + +### Automating Builds and Deployments + +If you found setting up your environmnent and running all of these steps to be a +bit tedious, then you are in good company. Thankfully, there are a large number +of tools available to assist with automating tasks like building and deploying +web application. For a starting point, check out this repository's own use of: + +- [CircleCI][circleci]: [config.yaml][circleci-config] +- [Netlify][netlify]: [netlify.toml][netlify.toml] | + [build-deploy-preview.sh][build-deploy-preview.sh] + +## Troubleshooting + +> Issues and resolutions for common GitHub issues will be summarized here + +... + + +[circleci]: https://circleci.com/gh/OHIF/Viewers +[circleci-config]: https://github.com/OHIF/Viewers/blob/master/.circleci/config.yml +[netlify]: https://app.netlify.com/sites/ohif/deploys +[netlify.toml]: https://github.com/OHIF/Viewers/blob/master/netlify.toml +[build-deploy-preview.sh]: https://github.com/OHIF/Viewers/blob/master/.netlify/build-deploy-preview.sh + diff --git a/docs/v2/deployment/recipes/embedded-viewer.md b/docs/v2/deployment/recipes/embedded-viewer.md new file mode 100644 index 00000000000..86aea531b7c --- /dev/null +++ b/docs/v2/deployment/recipes/embedded-viewer.md @@ -0,0 +1,171 @@ +# Embedded Viewer + +The quickest and easiest way to get the OHIF Viewer up and running is to embed +it into an existing web application. It allows us to forego a "build step", and +add a powerful medical imaging viewer to an existing web page using only a few +include tags. Here's how it works: + +{% include "./../_embedded-viewer-diagram.md" %} + +1. Create a new web page or template that includes the following external + dependencies: + + + +
    +
  1. Create a JS Object or Function to hold the OHIF Viewer's configuration. Here are some + example values that would allow the viewer to hit our public PACS:
  2. +
+ +```js +// Set before importing `ohif-viewer` (JS Object) +window.config = { + // default: '/' + routerBasename: '/', + servers: { + dicomWeb: [ + { + name: 'DCM4CHEE', + wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', + qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + qidoSupportsIncludeField: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + }, + ], + }, +}; +``` + +To learn more about how you can configure the OHIF Viewer, check out our +[Configuration Guide](../../configuring/index.md). + +
  1. + Render the viewer in the web page's target div +
+ +```js +// Made available by the `@ohif/viewer` script included in step 1 +var containerId = 'id-of-div-to-render-component-to'; +var componentRenderedOrUpdatedCallback = function() { + console.log('OHIF Viewer rendered/updated'); +}; +window.OHIFViewer.installViewer( + window.config, + containerId, + componentRenderedOrUpdatedCallback +); +``` + +You can see a live example of this recipe in [this CodeSandbox][code-sandbox]. + +## Add Extensions + +The UMD build of the OHIF Viewer is a "light weight" build that only contains +the core extensions required for basic 2D image viewing. It's possible to add +other extensions at runtime. + +This only requires us to include a single script tag, and add it using the +`extensions` key to our config. In this practical example, we register our +popular whole slide microscopy extension: + +```html + + + + +``` + +You can see an example of a slide microscopy study in the viewer [with the +extension enabled here][whole-slide-ext-demo] ([source code][ext-code-sandbox]) +and [without it here][whole-slide-base-demo] ([source code][code-sandbox]). + +You can read more about extensions and how to create your own in our +[extensions guide](/extensions/index.md). + +#### FAQ + +> I'm having trouble getting this to work. Where can I go for help? + +First, check out this fully functional [CodeSandbox][code-sandbox] example. If +you're still having trouble, feel free to search or GitHub issues. Can't find +anything related your problem? Create a new one. + +> My application's styles are impacting the OHIF Viewer's look and feel. What +> can I do? + +When you include stylesheets and scripts, they are added globally. This has the +potential of causing conflicts with other scripts and styles on the page. To +prevent this, `embed` the viewer in a new/empty web page. Have that working? +Good. Now `embed` that new page using an +[`