diff --git a/deploy/app.js b/deploy/app.js index b354a4b6839602e057a013fbb4feba81d74ecbcd..9cff68eecab451b4656f6bb6c39738d722d9d594 100644 --- a/deploy/app.js +++ b/deploy/app.js @@ -71,6 +71,11 @@ const PUBLIC_PATH = process.env.NODE_ENV === 'production' */ app.use('/.well-known', express.static(path.join(__dirname, 'well-known'))) +app.use((_req, res, next) => { + res.setHeader('Referrer-Policy', 'origin-when-cross-origin') + next() +}) + /** * show dev banner * n.b., must be before express.static() call diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts index 548e57cca5009f997f10e40d28dbe185cfb184d3..41899d81dcdb348882b4ff3d995cf786c5900f91 100644 --- a/src/atlasViewer/atlasViewer.constantService.service.ts +++ b/src/atlasViewer/atlasViewer.constantService.service.ts @@ -44,7 +44,7 @@ export class AtlasViewerConstantsServices implements OnDestroy { * raceFetch */ public raceFetch = (url) => Promise.race([ - fetch(url), + fetch(url, this.getFetchOption()), new Promise((_, reject) => setTimeout(() => { reject(`fetch did not resolve under ${this.TIMEOUT} ms`) }, this.TIMEOUT)) as Promise<Response> @@ -61,7 +61,7 @@ export class AtlasViewerConstantsServices implements OnDestroy { /* to be provided by KG in future */ public templateUrlsPr : Promise<string[]> = new Promise((resolve, reject) => { - fetch(`${this.backendUrl}templates`) + fetch(`${this.backendUrl}templates`, this.getFetchOption()) .then(res => res.json()) .then(arr => { this.templateUrls = arr @@ -148,6 +148,23 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float public mobileWarningHeader = `Power and Network Usage warning` public mobileWarning = `It looks like you are on a mobile device. Please note that the atlas viewer is power and network usage intensive.` + /** + * When the selected regions becomes exceedingly many, referer header often gets too hard + * in nginx, it can result in 400 header to large + * as result, trim referer to only template and parcellation selected + */ + private getScopedReferer(): string{ + const url = new URL(window.location.href) + url.searchParams.delete('regionsSelected') + return url.toString() + } + + public getFetchOption() : RequestInit{ + return { + referrer: this.getScopedReferer() + } + } + get floatingWidgetStartingPos() : [number,number]{ return [400,100] } diff --git a/src/atlasViewer/atlasViewer.dataService.service.ts b/src/atlasViewer/atlasViewer.dataService.service.ts index c9bc95e95b2bd2c9bc4807b7f680f57234ec0483..a1abc3bf9003989b315011fc07171a96fea813ae 100644 --- a/src/atlasViewer/atlasViewer.dataService.service.ts +++ b/src/atlasViewer/atlasViewer.dataService.service.ts @@ -56,6 +56,11 @@ export class AtlasViewerDataService implements OnDestroy{ } + /** + * TODO + * DEPRECATED + */ + /* all units in mm */ public spatialSearch(obj:any){ const {center,searchWidth,templateSpace,pageNo} = obj diff --git a/src/atlasViewer/atlasViewer.pluginService.service.ts b/src/atlasViewer/atlasViewer.pluginService.service.ts index 1b2aedcdb5969832c9b7b6fe68ca6e15693bf649..de7e16025e503ca0f985171532d86ae665a9e82b 100644 --- a/src/atlasViewer/atlasViewer.pluginService.service.ts +++ b/src/atlasViewer/atlasViewer.pluginService.service.ts @@ -48,20 +48,19 @@ export class PluginServices{ */ const promiseFetchedPluginManifests : Promise<PluginManifest[]> = new Promise((resolve, reject) => { Promise.all([ - /** - * PLUGINDEV should return an array of - */ + // TODO convert to use this.fetch PLUGINDEV - ? this.fetch(PLUGINDEV).then(res => res.json()) + ? fetch(PLUGINDEV, this.constantService.getFetchOption()).then(res => res.json()) : Promise.resolve([]), new Promise(resolve => { - this.fetch(`${this.constantService.backendUrl}plugins`) + fetch(`${this.constantService.backendUrl}plugins`, this.constantService.getFetchOption()) + .then(res => res.json()) .then(arr => Promise.all( arr.map(url => new Promise(rs => /** * instead of failing all promises when fetching manifests, only fail those that fails to fetch */ - this.fetch(url).then(rs).catch(e => (this.constantService.catchError(`fetching manifest error: ${e.toString()}`), rs(null)))) + fetch(url, this.constantService.getFetchOption()).then(res => res.json()).then(rs).catch(e => (console.log('fetching manifest error', e), rs(null)))) ) )) .then(manifests => resolve( @@ -75,7 +74,7 @@ export class PluginServices{ Promise.all( BUNDLEDPLUGINS .filter(v => typeof v === 'string') - .map(v => this.fetch(`res/plugin_examples/${v}/manifest.json`).then(res => res.json())) + .map(v => fetch(`res/plugin_examples/${v}/manifest.json`, this.constantService.getFetchOption()).then(res => res.json())) ) .then(arr => arr.reduce((acc,curr) => acc.concat(curr) ,[])) ]) @@ -120,13 +119,15 @@ export class PluginServices{ isDefined(plugin.template) ? Promise.resolve('template already provided') : isDefined(plugin.templateURL) ? - this.fetch(plugin.templateURL, {responseType: 'text'}) + fetch(plugin.templateURL, this.constantService.getFetchOption()) + .then(res=>res.text()) .then(template=>plugin.template = template) : Promise.reject('both template and templateURL are not defined') , isDefined(plugin.script) ? Promise.resolve('script already provided') : isDefined(plugin.scriptURL) ? - this.fetch(plugin.scriptURL, {responseType: 'text'}) + fetch(plugin.scriptURL, this.constantService.getFetchOption()) + .then(res=>res.text()) .then(script=>plugin.script = script) : Promise.reject('both script and scriptURL are not defined') ]) diff --git a/src/atlasViewer/atlasViewer.urlService.service.ts b/src/atlasViewer/atlasViewer.urlService.service.ts index a756f235b1cec42a20f7cbc2784eb2f8727eb300..a7c1f220e0220e28ee166f142ed2540f04403f0d 100644 --- a/src/atlasViewer/atlasViewer.urlService.service.ts +++ b/src/atlasViewer/atlasViewer.urlService.service.ts @@ -98,9 +98,25 @@ export class AtlasViewerURLService{ */ const searchparams = new URLSearchParams(window.location.search) + /** + * TODO + * triage: change of template and parcellation names is breaking old links + * change back when camilla/oli updated the links to new versions + */ + /* first, check if any template and parcellations are to be loaded */ - const searchedTemplatename = searchparams.get('templateSelected') - const searchedParcellationName = searchparams.get('parcellationSelected') + const searchedTemplatename = (() => { + const param = searchparams.get('templateSelected') + if (param === 'Allen Mouse') return `Allen adult mouse brain reference atlas V3` + if (param === 'Waxholm Rat V2.0') return 'Waxholm Space rat brain atlas v.2.0' + return param + })() + const searchedParcellationName = (() => { + const param = searchparams.get('parcellationSelected') + if (param === 'Allen Mouse Brain Atlas') return 'Allen adult mouse brain reference atlas V3 Brain Atlas' + if (param === 'Whole Brain (v2.0)') return 'Waxholm Space rat brain atlas v.2.0' + return param + })() if (!searchedTemplatename) { const urlString = window.location.href @@ -262,14 +278,20 @@ export class AtlasViewerURLService{ const pluginStates = searchparams.get('pluginStates') if(pluginStates){ const arrPluginStates = pluginStates.split('__') - arrPluginStates.forEach(url => fetch(url).then(res => res.json()).then(json => this.pluginService.launchNewWidget(json)).catch(console.error)) + arrPluginStates.forEach(url => fetch(url, this.constantService.getFetchOption()).then(res => res.json()).then(json => this.pluginService.launchNewWidget(json)).catch(console.error)) } }) /* pushing state to url */ combineLatest( - this.changeQueryObservable$.pipe( - map(state=>{ + combineLatest( + this.changeQueryObservable$, + this.store.pipe( + select('viewerState'), + select('parcellationSelected') + ) + ).pipe( + map(([state, parcellationSelected])=>{ let _ = {} for(const key in state){ if(isDefined(state[key])){ diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts index 7cf161e590274b4198a84fa4fb2817872396d4ee..0393d0b927147fd52c9f7977a92789a144d71810 100644 --- a/src/ui/databrowserModule/databrowser.service.ts +++ b/src/ui/databrowserModule/databrowser.service.ts @@ -3,7 +3,7 @@ import { Subscription, Observable, combineLatest, BehaviorSubject, fromEvent, fr import { ViewerConfiguration } from "src/services/state/viewerConfig.store"; import { select, Store } from "@ngrx/store"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; -import { ADD_NG_LAYER, REMOVE_NG_LAYER, DataEntry, safeFilter, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA } from "src/services/stateStore.service"; +import { DataEntry, safeFilter, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA } from "src/services/stateStore.service"; import { map, distinctUntilChanged, debounceTime, filter, tap, switchMap, catchError, shareReplay, withLatestFrom } from "rxjs/operators"; import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; import { FilterDataEntriesByRegion } from "./util/filterDataEntriesByRegion.pipe"; @@ -230,7 +230,7 @@ export class DatabrowserService implements OnDestroy{ public fetchPreviewData(datasetName: string){ const encodedDatasetName = encodeURIComponent(datasetName) return new Promise((resolve, reject) => { - fetch(`${this.constantService.backendUrl}datasets/preview/${encodedDatasetName}`) + fetch(`${this.constantService.backendUrl}datasets/preview/${encodedDatasetName}`, this.constantService.getFetchOption()) .then(res => res.json()) .then(resolve) .catch(reject) @@ -253,9 +253,9 @@ export class DatabrowserService implements OnDestroy{ const encodedTemplateName = encodeURIComponent(templateName) const encodedParcellationName = encodeURIComponent(parcellationName) return Promise.all([ - fetch(`${this.constantService.backendUrl}datasets/templateName/${encodedTemplateName}`) + fetch(`${this.constantService.backendUrl}datasets/templateName/${encodedTemplateName}`, this.constantService.getFetchOption()) .then(res => res.json()), - fetch(`${this.constantService.backendUrl}datasets/parcellationName/${encodedParcellationName}`) + fetch(`${this.constantService.backendUrl}datasets/parcellationName/${encodedParcellationName}`, this.constantService.getFetchOption()) .then(res => res.json()) ]) .then(arr => [...arr[0], ...arr[1]]) diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 1419f1ee0ea34b38790539583add518ab5af0371..90174a621c3419f467dd174f7905107f345a626c 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -662,13 +662,16 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ ), this.ngLayers$.pipe( map(state => state.forceShowSegment) - ) + ), + this.selectedParcellation$ ) - .subscribe(([regions,hideSegmentFlag,forceShowSegment])=>{ + .subscribe(([regions,hideSegmentFlag,forceShowSegment, selectedParcellation])=>{ if(!this.nehubaViewer) return + const { ngId: defaultNgId } = selectedParcellation + /* selectedregionindexset needs to be updated regardless of forceshowsegment */ - this.selectedRegionIndexSet = new Set(regions.map(({ngId, labelIndex})=>generateLabelIndexId({ ngId, labelIndex }))) + this.selectedRegionIndexSet = new Set(regions.map(({ngId = defaultNgId, labelIndex})=>generateLabelIndexId({ ngId, labelIndex }))) if( forceShowSegment === false || (forceShowSegment === null && hideSegmentFlag) ){ this.nehubaViewer.hideAllSeg() diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts b/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts index 6a059daa3e7790bd06e5b6796545813187b7cd3a..917129c6cac2087d4e55903c30faa61d040a891b 100644 --- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts +++ b/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts @@ -11,11 +11,11 @@ const insertHighlight :(name:string, searchTerm:string) => string = (name:string name.replace(regex, (s) => `<span class = "highlight">${s}</span>`) } -const getDisplayTreeNode : (searchTerm:string, selectedRegions:any[]) => (item:any) => string = (searchTerm:string = '', selectedRegions:any[] = []) => ({ ngId, name, status, labelIndex }) => { +const getDisplayTreeNode : (searchTerm:string, selectedRegions:any[], defaultNgId: string) => (item:any) => string = (searchTerm:string = '', selectedRegions:any[] = [], defaultNgId) => ({ ngId = defaultNgId, name, status, labelIndex }) => { return !!labelIndex && !!ngId && selectedRegions.findIndex(re => - generateLabelIndexId({ labelIndex: re.labelIndex, ngId: re.ngId }) === generateLabelIndexId({ ngId, labelIndex }) + generateLabelIndexId({ labelIndex: re.labelIndex, ngId: re.ngId || defaultNgId }) === generateLabelIndexId({ ngId, labelIndex }) ) >= 0 ? `<span class="cursor-default regionSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``) : `<span class="cursor-default regionNotSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``) @@ -105,7 +105,7 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ children: this.parcellationSelected.regions } } - this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions) + this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions, this.selectedParcellation.ngId) this.filterTreeBySearch = getFilterTreeBySearch(this.filterNameBySearchPipe, this.searchTerm) } @@ -123,7 +123,7 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ } ngOnInit(){ - this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions) + this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions, this.selectedParcellation.ngId) this.filterTreeBySearch = getFilterTreeBySearch(this.filterNameBySearchPipe, this.searchTerm) this.subscriptions.push( diff --git a/webpack.dev.js b/webpack.dev.js index 3fe037c472f808ff4a811607d1992412c5e30d72..d32be20fe85174ca5ae969f2d1b99ca43d87558b 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -20,5 +20,10 @@ module.exports = merge(common,ngAssets,staticAssets,{ new HtmlWebpackPlugin({ template : 'src/index.html' }) - ] + ], + devServer: { + headers: { + 'Referrer-Policy': 'origin-when-cross-origin' + } + } }) \ No newline at end of file