From bc0c68e09d0fc1e6e6594b120808457cf0f1de50 Mon Sep 17 00:00:00 2001 From: mario Date: Fri, 16 May 2025 13:54:45 +0700 Subject: [PATCH] Squashed commit of the following: commit 18d5b6dd9abce9f4104a7e1453fcd7cb64067245 Author: mario Date: Tue May 13 16:15:37 2025 +0700 add: shortlink DoB auth page commit 7cad1c5e05e8c1a9e8426ee6b9267a4c1beba670 Author: mario Date: Tue May 13 08:52:45 2025 +0700 prevent patient to see Worklist commit 7f4548e18c3ad2baf08d22e798a05242a4b4397d Author: mario Date: Tue May 13 08:51:38 2025 +0700 add: Login page and route commit eaa18b83897d13d471d158065196b2f24da4ee8a Author: mario Date: Fri May 9 16:40:02 2025 +0700 edit: patch XHR Request dengan coverage lebih luas dari monkeyPatchXML commit 86ad0b38dd5ee34f345772d81df456b051219227 Author: padmanto Date: Tue Apr 29 09:42:53 2025 +0700 Monkey Patch XMLHttpRequest -- inject bearer token and verify response commit cb380a521d890b557fd2a1eead5bc12d1b2a7677 Author: padmanto Date: Tue Apr 29 08:44:52 2025 +0700 default use cloud pacs commit 5f56d06fcd7711d44d3fcd478bf5cd7f1905837d Author: mario Date: Sat Apr 26 23:38:18 2025 +0700 edit ip pdf dan cloud --- .gitignore | 1 + .../Overlays/studyDataForOverlayItem.ts | 14 +- .../cornerstone/src/initWADOImageLoader.js | 9 ++ .../utils/CornerstoneViewportDownloadForm.tsx | 59 +++++++- .../default/src/DicomWebDataSource/index.js | 8 ++ .../viewports/OHIFCornerstonePdfViewport.tsx | 3 +- platform/app/.webpack/webpack.pwa.js | 2 +- platform/app/public/config/default.js | 8 +- platform/app/public/config/google.js | 12 +- platform/app/src/App.tsx | 46 ++++++ platform/app/src/routes/Login.tsx | 129 +++++++++++++++++ platform/app/src/routes/ShortlinkLogin.tsx | 131 ++++++++++++++++++ platform/app/src/routes/WorkList/WorkList.tsx | 38 ++++- platform/app/src/routes/index.tsx | 11 ++ .../utils/initUserAuthenticationService.js | 72 ++++++++++ 15 files changed, 527 insertions(+), 16 deletions(-) create mode 100644 platform/app/src/routes/Login.tsx create mode 100644 platform/app/src/routes/ShortlinkLogin.tsx create mode 100644 platform/app/src/utils/initUserAuthenticationService.js diff --git a/.gitignore b/.gitignore index 61dcb78..a32ab0c 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,5 @@ tests/playwright-report/ # Dummy /dump +jwt-auth-inject.json platform/app/dist.zip diff --git a/extensions/cornerstone/src/Viewport/Overlays/studyDataForOverlayItem.ts b/extensions/cornerstone/src/Viewport/Overlays/studyDataForOverlayItem.ts index 71f3b87..e6c90f6 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/studyDataForOverlayItem.ts +++ b/extensions/cornerstone/src/Viewport/Overlays/studyDataForOverlayItem.ts @@ -102,8 +102,20 @@ export const studyDataForOverlayItem = (studyInstanceUID: string) => { try { const qidoRootUrl = getQidoRootUrl(); + // Get the authentication token from session storage + const authToken = window.sessionStorage.getItem('ohif-auth-token'); + + // Create request headers with Authorization if token exists + const headers: HeadersInit = {}; + if (authToken) { + headers['Authorization'] = `Bearer ${authToken}`; + } + const response = await fetch( - `${qidoRootUrl}/studies?limit=101&offset=0&fuzzymatching=false&includefield=00080050,00081030,00101010,0010004&StudyInstanceUID=${studyInstanceUID}` + `${qidoRootUrl}/studies?limit=101&offset=0&fuzzymatching=false&includefield=00080050,00081030,00101010,0010004&StudyInstanceUID=${studyInstanceUID}`, + { + headers, + } ); if (!response.ok) { diff --git a/extensions/cornerstone/src/initWADOImageLoader.js b/extensions/cornerstone/src/initWADOImageLoader.js index 9f9922f..8b12a77 100644 --- a/extensions/cornerstone/src/initWADOImageLoader.js +++ b/extensions/cornerstone/src/initWADOImageLoader.js @@ -39,6 +39,15 @@ export default function initWADOImageLoader( Accept: acceptHeader, }; + // // Patch Mario: + const authToken = sessionStorage.getItem('ohif-auth-token'); + if (!authToken) { + window.location.href = '/login'; + return; + } + + xhrRequestHeaders.Authorization = `Bearer ${authToken}`; + if (headers) { Object.assign(xhrRequestHeaders, headers); } diff --git a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx index 5e72d37..514855c 100644 --- a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx +++ b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx @@ -26,6 +26,15 @@ const CornerstoneViewportDownloadForm = ({ const activeViewportElement = enabledElement?.element; const activeViewportEnabledElement = getEnabledElement(activeViewportElement); + // console.log('cornerstoneViewportService', cornerstoneViewportService); + const viewportInfo = cornerstoneViewportService.getViewportInfo("default"); + // console.log('viewportInfo', viewportInfo); + + // Retrieve StudyInstanceUID from viewportInfo + const StudyInstanceUID = viewportInfo.getViewportData().data[0].StudyInstanceUID; + const SetInstanceUID = viewportInfo.getViewportData().data[0].displaySetInstanceUID; + // console.log('StudyInstanceUID', StudyInstanceUID); + // console.log('SetInstanceUID', SetInstanceUID); const { viewportId: activeViewportId, renderingEngineId, @@ -108,6 +117,10 @@ const CornerstoneViewportDownloadForm = ({ const downloadCanvas = getOrCreateCanvas(element); + // Log the canvas content before conversion + const context = downloadCanvas.getContext('2d'); + const imageData = context.getImageData(0, 0, downloadCanvas.width, downloadCanvas.height); + const type = 'image/' + fileType; const dataUrl = downloadCanvas.toDataURL(type, 1); @@ -214,6 +227,43 @@ const CornerstoneViewportDownloadForm = ({ }); }; + // New function to send annotation data + const sendAnnotationData = async (base64Image, activeViewportElement) => { + try { + // Get the SOPInstanceUID from the active viewport + const activeViewportEnabledElement = getEnabledElement(activeViewportElement); + const imageId = activeViewportEnabledElement?.viewport?.getCurrentImageId(); + + if (!base64Image || !StudyInstanceUID) { + throw new Error('Missing required data'); + } + + const payload = { + image: base64Image, + StudyInstanceUID: StudyInstanceUID + }; + + const response = await fetch('http://host:port/one-api/tools/annotation/store', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload) + }); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + const data = await response.json(); + console.log('Annotation data sent successfully:', data); + return data; + } catch (error) { + console.error('Error sending annotation data:', error); + throw error; + } + }; + const downloadBlob = (filename, fileType) => { const file = `${filename}.${fileType}`; const divForDownloadViewport = document.querySelector( @@ -221,9 +271,16 @@ const CornerstoneViewportDownloadForm = ({ ); html2canvas(divForDownloadViewport).then(canvas => { + const dataUrl = canvas.toDataURL(fileType, 1.0); + const base64Image = dataUrl.split(',')[1]; // Remove prefix 'data:image/png;base64,' + + // Send annotation data + sendAnnotationData(base64Image, activeViewportElement) + .catch(error => console.error('Annotation data sending failed:', error)); + const link = document.createElement('a'); link.download = file; - link.href = canvas.toDataURL(fileType, 1.0); + link.href = dataUrl; link.click(); }); }; diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index 8f5f9ae..de3d528 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -85,6 +85,14 @@ function createDicomWebApi(dicomWebConfig, servicesManager) { if (authHeaders && authHeaders.Authorization) { xhrRequestHeaders.Authorization = authHeaders.Authorization; } + + const authToken = sessionStorage.getItem('ohif-auth-token'); + if (!authToken) { + window.location.href = '/login'; + return; + } + xhrRequestHeaders.Authorization = `Bearer ${authToken}`; + return xhrRequestHeaders; }; diff --git a/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx b/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx index a7d9df9..5e4651e 100644 --- a/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx +++ b/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx @@ -6,7 +6,8 @@ function OHIFCornerstonePdfViewport({ displaySets }) { var [url, setUrl] = useState(null); const sopInstanceUid = displaySets[0].SOPInstanceUID; - url = `http://128.199.154.150:8080/rid/IHERetrieveDocument?requestType=DOCUMENT&documentUID=${sopInstanceUid}&preferredContentType=application%2Fpdf`; + url = `http://${window.config.pacs_document_host}:${window.config.pacs_document_port}/rid/IHERetrieveDocument?requestType=DOCUMENT&documentUID=${sopInstanceUid}&preferredContentType=application%2Fpdf`; + console.log("URL PDF", url); useEffect(() => { document.body.addEventListener('drag', makePdfDropTarget); diff --git a/platform/app/.webpack/webpack.pwa.js b/platform/app/.webpack/webpack.pwa.js index cd64875..78c05e0 100644 --- a/platform/app/.webpack/webpack.pwa.js +++ b/platform/app/.webpack/webpack.pwa.js @@ -25,7 +25,7 @@ const PROXY_DOMAIN = process.env.PROXY_DOMAIN; const PROXY_PATH_REWRITE_FROM = process.env.PROXY_PATH_REWRITE_FROM; const PROXY_PATH_REWRITE_TO = process.env.PROXY_PATH_REWRITE_TO; -const OHIF_PORT = Number(process.env.OHIF_PORT || 3000); +const OHIF_PORT = Number(process.env.OHIF_PORT || 3030); const ENTRY_TARGET = process.env.ENTRY_TARGET || `${SRC_DIR}/index.js`; const Dotenv = require('dotenv-webpack'); const writePluginImportFile = require('./writePluginImportsFile.js'); diff --git a/platform/app/public/config/default.js b/platform/app/public/config/default.js index c145905..f85e728 100644 --- a/platform/app/public/config/default.js +++ b/platform/app/public/config/default.js @@ -1,6 +1,10 @@ /** @type {AppTypes.Config} */ - +function sas_get_token() { + //implement token here + return ''; +} window.config = { + sasGetToken: sas_get_token, routerBasename: '/', // whiteLabeling: {}, extensions: [], @@ -25,7 +29,7 @@ window.config = { }, expertise: false, //* Tambahan untuk enable expertise (CustomizableViewportOverlay) expertise_host: `https://devone.aplikasi.web.id/one-api/mockup/pacsmwl/Workorder/get_dummy_expertise`, //* Tambahan untuk fetch data Expertise) - pacs_document_host: `152.42.173.210`, + pacs_document_host: `${window.location.hostname}`, pacs_document_port: 8080, // filterQueryParam: false, // defaultDataSourceName: 'dicomweb', diff --git a/platform/app/public/config/google.js b/platform/app/public/config/google.js index cd3c6b2..5fa956c 100644 --- a/platform/app/public/config/google.js +++ b/platform/app/public/config/google.js @@ -1,7 +1,7 @@ /** @type {AppTypes.Config} */ window.config = { routerBasename: '/', - pacs_document_host: '152.42.173.210', + pacs_document_host: `${window.location.hostname}`, pacs_document_port: 8080, expertise: false, enableGoogleCloudAdapter: false, @@ -41,16 +41,16 @@ window.config = { configuration: { friendlyName: 'dcmjs DICOMWeb Server', name: 'GCP', - wadoUriRoot: `http://152.42.173.210:5555/dicomWeb`, - qidoRoot: `http://152.42.173.210:5555/dicomWeb`, - wadoRoot: `http://152.42.173.210:5555/dicomWeb`, + wadoUriRoot: `http://${window.location.hostname}:5555/dicomWeb`, + qidoRoot: `http://${window.location.hostname}:5555/dicomWeb`, + wadoRoot: `http://${window.location.hostname}:5555/dicomWeb`, qidoSupportsIncludeField: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, - supportsFuzzyMatching: true, + supportsFuzzyMatching: false, supportsWildcard: true, - dicomUploadEnabled: true, + dicomUploadEnabled: false, omitQuotationForMultipartRequest: true, configurationAPI: 'ohif.dataSourceConfigurationAPI.google', // defaultDicomStoreConfiguredItems: { diff --git a/platform/app/src/App.tsx b/platform/app/src/App.tsx index 253080b..1436ded 100644 --- a/platform/app/src/App.tsx +++ b/platform/app/src/App.tsx @@ -36,6 +36,49 @@ import createRoutes from './routes'; import appInit from './appInit.js'; import OpenIdConnectRoutes from './utils/OpenIdConnectRoutes'; import { ShepherdJourneyProvider } from 'react-shepherd'; +import { initializeCustomAuth } from './utils/initUserAuthenticationService'; + +function injectAuth() { + console.log('---> Inject Auth'); + const originalXHROpen = XMLHttpRequest.prototype.open; + const originalXHRSend = XMLHttpRequest.prototype.send; + + // Kalau ingin disable study list (Role Patient) + // window.config.showStudyList = false; + + const authToken = sessionStorage.getItem('ohif-auth-token'); + + XMLHttpRequest.prototype.open = function (method, url, async, user, password) { + this._url = url; // Save URL if you want conditional logic + return originalXHROpen.apply(this, arguments); + }; + + XMLHttpRequest.prototype.send = function (body) { + this.setRequestHeader('Authorization', `Bearer ${authToken}`); + + this.addEventListener('readystatechange', function () { + if (this.readyState === 4) { + // readyState 4 = DONE + try { + // Check for auth errors (401/403) and redirect to login if needed + if (this.status === 401 || this.status === 403) { + window.sessionStorage.removeItem('ohif-auth-token'); + window.location.href = '/login'; + } + } catch (e) { + console.error('Error handling auth response:', e); + } + } + }); + return originalXHRSend.apply(this, arguments); + }; +} + +// Setup token access function +window.config.sasGetToken = () => window.sessionStorage.getItem('ohif-auth-token'); + +// Enable auth token injection +// injectAuth(); let commandsManager: CommandsManager, extensionManager: ExtensionManager, @@ -108,6 +151,9 @@ function App({ customizationService, } = servicesManager.services; + // Initialize our custom authentication service + initializeCustomAuth(userAuthenticationService); + const providers = [ [AppConfigProvider, { value: appConfigState }], [UserAuthenticationProvider, { service: userAuthenticationService }], diff --git a/platform/app/src/routes/Login.tsx b/platform/app/src/routes/Login.tsx new file mode 100644 index 0000000..11ea2fc --- /dev/null +++ b/platform/app/src/routes/Login.tsx @@ -0,0 +1,129 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { useUserAuthentication } from '@ohif/ui'; + +const Login = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const location = useLocation(); + const [, authContext] = useUserAuthentication(); + + // Get the intended destination from URL query params or default to home + const searchParams = new URLSearchParams(location.search); + const redirectPath = searchParams.get('redirect') || '/'; + + // Check if already authenticated + useEffect(() => { + const token = window.sessionStorage.getItem('ohif-auth-token'); + if (token) { + // Already logged in, redirect to destination + navigate(redirectPath, { replace: true }); + } + }, [redirectPath, navigate]); + + const handleLogin = async e => { + e.preventDefault(); + setError(''); + setIsLoading(true); + + try { + // Use window.config.goProxyHost for authentication endpoint + const proxyHost = window.config?.goProxyHost || `http://${window.location.hostname}:5555`; + const authEndpoint = `${proxyHost}/auth/login`; + + // Call go-ohif-proxy login endpoint + const response = await fetch(authEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ email, password }), + }); + + if (!response.ok) { + throw new Error('Login failed. Please check your credentials.'); + } + + const data = await response.json(); + + // Store token in sessionStorage + window.sessionStorage.setItem('ohif-auth-token', data.access_token); + + // Decode token to extract role and user information + let userInfo = data.user; + + // Update the auth context + authContext.setUser({ + ...userInfo, + token: data.access_token, + }); + + // Set window.config.sasGetToken for the injectAuth function + if (window.config) { + window.config.sasGetToken = () => window.sessionStorage.getItem('ohif-auth-token'); + } + + // Handle role-specific redirects if specified in response + if (data.redirect_url) { + navigate(data.redirect_url, { replace: true }); + } else { + // Redirect to the original destination + navigate(redirectPath, { replace: true }); + } + } catch (error) { + console.error('Login error:', error); + setError(error.message || 'Failed to log in. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + return ( +
+
+

Login to OHIF Viewer

+ + {error &&
{error}
} + +
+
+ + setEmail(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + /> +
+ +
+ +
+
+
+
+ ); +}; + +export default Login; diff --git a/platform/app/src/routes/ShortlinkLogin.tsx b/platform/app/src/routes/ShortlinkLogin.tsx new file mode 100644 index 0000000..b4339ac --- /dev/null +++ b/platform/app/src/routes/ShortlinkLogin.tsx @@ -0,0 +1,131 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { useUserAuthentication } from '@ohif/ui'; + +const ShortlinkLogin = () => { + const [dob, setDob] = useState(''); + const [shortToken, setShortToken] = useState(''); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const location = useLocation(); + const [, authContext] = useUserAuthentication(); + + // Parse the short token from URL query params + useEffect(() => { + const searchParams = new URLSearchParams(location.search); + const token = searchParams.get('short'); + + if (token) { + setShortToken(token); + } else { + // No short token found, redirect to regular login + setError('No shortlink token found in URL'); + setTimeout(() => { + navigate('/login', { replace: true }); + }, 3000); + } + }, [location.search, navigate]); + + // Handle form submission + const handleSubmit = async e => { + e.preventDefault(); + setError(''); + setIsLoading(true); + + try { + // Use window.config.goProxyHost for authentication endpoint + const proxyHost = window.config?.goProxyHost || `http://${window.location.hostname}:5555`; + const authEndpoint = `${proxyHost}/auth/shortlink`; + + // Call the shortlink authentication endpoint + const response = await fetch(authEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ short_token: shortToken, dob }), + }); + + if (!response.ok) { + throw new Error('Authentication failed. Please check your date of birth and try again.'); + } + + const data = await response.json(); + + // Store token in sessionStorage + window.sessionStorage.setItem('ohif-auth-token', data.access_token); + + // Decode token to extract user information (if available in token) + let userInfo = data.user; + + // Update the auth context + authContext.setUser({ + ...userInfo, + token: data.access_token, + }); + + // Set window.config.sasGetToken for the injectAuth function + if (window.config) { + window.config.sasGetToken = () => window.sessionStorage.getItem('ohif-auth-token'); + } + + // Navigate to the viewer page with the authenticated patient's study + // The actual URL would depend on how studies are loaded in your OHIF instance + if (data.redirect_url) { + navigate(data.redirect_url, { replace: true }); + } else { + // Default navigation if no specific redirect is provided + navigate('/', { replace: true }); + } + } catch (error) { + console.error('Authentication error:', error); + setError(error.message || 'Failed to authenticate. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + const handleDateChange = e => { + // Format date input as YYYY-MM-DD + setDob(e.target.value); + }; + + return ( +
+
+

Patient Access

+ + {error &&
{error}
} + +
+
+ + +

Format: Bulan - Tanggal - Tahun

+
+ +
+ +
+
+
+
+ ); +}; + +export default ShortlinkLogin; diff --git a/platform/app/src/routes/WorkList/WorkList.tsx b/platform/app/src/routes/WorkList/WorkList.tsx index 50c8a4b..2e05fae 100644 --- a/platform/app/src/routes/WorkList/WorkList.tsx +++ b/platform/app/src/routes/WorkList/WorkList.tsx @@ -85,6 +85,38 @@ function WorkList({ const debouncedFilterValues = useDebounce(filterValues, 200); const { resultsPerPage, pageNumber, sortBy, sortDirection } = filterValues; + /* + * Patch untuk Role checking patient gabisa akses ke study list + */ + const token = window.sessionStorage.getItem('ohif-auth-token'); + if (!token) { + return; + } + const decodedToken = decodeToken(token); + + // Check jika 'role' = 'patient' tapi akses '/' return ke viewer + if (decodedToken && decodedToken.role === 'patient') { + const currentPath = window.location.pathname + window.location.search; + if (currentPath === '/') { + console.log( + 'User is a patient and trying to access the root path. Redirecting to his/her home URL.' + ); + window.location.href = `${decodedToken.home_url}`; + } + } + + function decodeToken(token) { + try { + const payload = token.split('.')[1]; + if (payload) { + return JSON.parse(atob(payload)); + } + } catch (e) { + console.error('Error parsing JWT token', e); + } + return null; + } + /* * The default sort value keep the filters synchronized with runtime conditional sorting * Only applied if no other sorting is specified and there are less than 101 studies @@ -542,7 +574,7 @@ function WorkList({ /> -
+
dataSourceConfigurationComponent() // : undefined // } - getDataSourceConfigurationComponent={ - undefined - } + getDataSourceConfigurationComponent={undefined} />
{hasStudies ? ( diff --git a/platform/app/src/routes/index.tsx b/platform/app/src/routes/index.tsx index 78ad5db..39d171e 100644 --- a/platform/app/src/routes/index.tsx +++ b/platform/app/src/routes/index.tsx @@ -12,6 +12,8 @@ import buildModeRoutes from './buildModeRoutes'; import PrivateRoute from './PrivateRoute'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; +import Login from './Login'; +import ShortlinkLogin from './ShortlinkLogin'; const NotFoundServer = ({ message = 'Unable to query for studies at this time. Check your data source configuration or network connection', @@ -74,6 +76,15 @@ const bakedInRoutes = [ path: '/localbasic', children: Local.bind(null, { modePath: 'viewer/dicomlocal' }), }, + // * Custom Patch untuk Login go-ohif-proxy + { + path: '/login', + children: Login, + }, + { + path: '/short-auth', + children: ShortlinkLogin, + }, ]; // NOT FOUND (404) diff --git a/platform/app/src/utils/initUserAuthenticationService.js b/platform/app/src/utils/initUserAuthenticationService.js new file mode 100644 index 0000000..a527066 --- /dev/null +++ b/platform/app/src/utils/initUserAuthenticationService.js @@ -0,0 +1,72 @@ +/** + * Initializes the custom authentication service for OHIF Viewer + * to work with go-ohif-proxy authentication + */ +export function initializeCustomAuth(userAuthenticationService) { + // Set up the authentication service with custom implementation + userAuthenticationService.setServiceImplementation({ + // Custom implementation to handle unauthenticated users + handleUnauthenticated: () => { + // Check if there's a shortlink token in the URL + const urlParams = new URLSearchParams(window.location.search); + const shortToken = urlParams.get('short'); + + // If there's a shortlink token, redirect to the shortlink login page + if (shortToken) { + window.location.href = `/short-auth?short=${shortToken}`; + return null; + } + + // Otherwise, handle as normal login flow + // Get the current path for redirect after login + const currentPath = window.location.pathname + window.location.search; + + // Clear any existing tokens + window.sessionStorage.removeItem('ohif-auth-token'); + + // Redirect to login page with the redirect URL in query params + window.location.href = `/login?redirect=${encodeURIComponent(currentPath)}`; + + // Return null to prevent rendering while redirecting + return null; + }, + + // Custom implementation to get the authorization header + // di ohif3.9.1 ini sepertinya masih development + // getAuthorizationHeader: () => { + // const token = window.sessionStorage.getItem('ohif-auth-token'); + // return token ? `Bearer ${token}` : undefined; + // }, + }); + + // Set authentication as enabled + userAuthenticationService.set({ enabled: true }); + + // Check if we already have a token and set the user if we do + const token = window.sessionStorage.getItem('ohif-auth-token'); + if (!token) { + return; + } + const decodedToken = decodeToken(token); + + // Check jika 'role' = 'patient' tapi akses '/' return ke viewer + if (decodedToken && decodedToken.role === 'patient') { + const currentPath = window.location.pathname + window.location.search; + if (currentPath === '/') { + console.log('User is a patient and trying to access the root path. Redirecting to /patient.'); + window.location.href = `${decodedToken.home_url}`; + } + } + + function decodeToken(token) { + try { + const payload = token.split('.')[1]; + if (payload) { + return JSON.parse(atob(payload)); + } + } catch (e) { + console.error('Error parsing JWT token', e); + } + return null; + } +}