Skip to content

Commit c4f2e8c

Browse files
authored
Merge pull request #1642 from harvard-lts/145-base-path-support
[Issue 145] Support base path
2 parents 3a9783d + cfb4a62 commit c4f2e8c

File tree

17 files changed

+108
-37
lines changed

17 files changed

+108
-37
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
"start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"",
1010
"start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr",
1111
"start:mirador:prod": "yarn run build:mirador && yarn run start:prod",
12-
"serve": "ng serve -c development",
12+
"preserve": "yarn base-href",
13+
"serve": "ng serve --configuration development",
1314
"serve:ssr": "node dist/server/main",
1415
"analyze": "webpack-bundle-analyzer dist/browser/stats.json",
15-
"build": "ng build -c development",
16+
"build": "ng build --configuration development",
1617
"build:stats": "ng build --stats-json",
1718
"build:prod": "yarn run build:ssr",
1819
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
@@ -37,6 +38,7 @@
3738
"cypress:open": "cypress open",
3839
"cypress:run": "cypress run",
3940
"env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts",
41+
"base-href": "ts-node --project ./tsconfig.ts-node.json scripts/base-href.ts",
4042
"check-circ-deps": "npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\\.model\\.ts$' --circular --extensions ts ./"
4143
},
4244
"browser": {

scripts/base-href.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as fs from 'fs';
2+
import { join } from 'path';
3+
4+
import { AppConfig } from '../src/config/app-config.interface';
5+
import { buildAppConfig } from '../src/config/config.server';
6+
7+
/**
8+
* Script to set baseHref as `ui.nameSpace` for development mode. Adds `baseHref` to angular.json build options.
9+
*
10+
* Usage (see package.json):
11+
*
12+
* yarn base-href
13+
*/
14+
15+
const appConfig: AppConfig = buildAppConfig();
16+
17+
const angularJsonPath = join(process.cwd(), 'angular.json');
18+
19+
if (!fs.existsSync(angularJsonPath)) {
20+
console.error(`Error:\n${angularJsonPath} does not exist\n`);
21+
process.exit(1);
22+
}
23+
24+
try {
25+
const angularJson = require(angularJsonPath);
26+
27+
const baseHref = `${appConfig.ui.nameSpace}${appConfig.ui.nameSpace.endsWith('/') ? '' : '/'}`;
28+
29+
console.log(`Setting baseHref to ${baseHref} in angular.json`);
30+
31+
angularJson.projects['dspace-angular'].architect.build.options.baseHref = baseHref;
32+
33+
fs.writeFileSync(angularJsonPath, JSON.stringify(angularJson, null, 2) + '\n');
34+
} catch (e) {
35+
console.error(e);
36+
}

server.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ extendEnvironmentWithAppConfig(environment, appConfig);
6767
// The Express app is exported so that it can be used by serverless Functions.
6868
export function app() {
6969

70+
const router = express.Router();
71+
7072
/*
7173
* Create a new express application
7274
*/
@@ -138,7 +140,11 @@ export function app() {
138140
/**
139141
* Proxy the sitemaps
140142
*/
141-
server.use('/sitemap**', createProxyMiddleware({ target: `${environment.rest.baseUrl}/sitemaps`, changeOrigin: true }));
143+
router.use('/sitemap**', createProxyMiddleware({
144+
target: `${environment.rest.baseUrl}/sitemaps`,
145+
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
146+
changeOrigin: true
147+
}));
142148

143149
/**
144150
* Checks if the rateLimiter property is present
@@ -157,7 +163,7 @@ export function app() {
157163
* Serve static resources (images, i18n messages, …)
158164
* Handle pre-compressed files with [express-static-gzip](https://github.com/tkoenig89/express-static-gzip)
159165
*/
160-
server.get('*.*', cacheControl, expressStaticGzip(DIST_FOLDER, {
166+
router.get('*.*', cacheControl, expressStaticGzip(DIST_FOLDER, {
161167
index: false,
162168
enableBrotli: true,
163169
orderPreference: ['br', 'gzip'],
@@ -166,10 +172,12 @@ export function app() {
166172
/*
167173
* Fallthrough to the IIIF viewer (must be included in the build).
168174
*/
169-
server.use('/iiif', express.static(IIIF_VIEWER, {index:false}));
175+
router.use('/iiif', express.static(IIIF_VIEWER, { index: false }));
170176

171177
// Register the ngApp callback function to handle incoming requests
172-
server.get('*', ngApp);
178+
router.get('*', ngApp);
179+
180+
server.use(environment.ui.nameSpace, router);
173181

174182
return server;
175183
}
@@ -203,13 +211,25 @@ function ngApp(req, res) {
203211
if (hasValue(err)) {
204212
console.warn('Error details : ', err);
205213
}
206-
res.sendFile(DIST_FOLDER + '/index.html');
214+
res.render(indexHtml, {
215+
req,
216+
providers: [{
217+
provide: APP_BASE_HREF,
218+
useValue: req.baseUrl
219+
}]
220+
});
207221
}
208222
});
209223
} else {
210224
// If preboot is disabled, just serve the client
211225
console.log('Universal off, serving for direct CSR');
212-
res.sendFile(DIST_FOLDER + '/index.html');
226+
res.render(indexHtml, {
227+
req,
228+
providers: [{
229+
provide: APP_BASE_HREF,
230+
useValue: req.baseUrl
231+
}]
232+
});
213233
}
214234
}
215235

src/app/app.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ describe('App component', () => {
187187
link.setAttribute('rel', 'stylesheet');
188188
link.setAttribute('type', 'text/css');
189189
link.setAttribute('class', 'theme-css');
190-
link.setAttribute('href', '/custom-theme.css');
190+
link.setAttribute('href', 'custom-theme.css');
191191

192192
expect(headSpy.appendChild).toHaveBeenCalledWith(link);
193193
});

src/app/app.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export class AppComponent implements OnInit, AfterViewInit {
268268
link.setAttribute('rel', 'stylesheet');
269269
link.setAttribute('type', 'text/css');
270270
link.setAttribute('class', 'theme-css');
271-
link.setAttribute('href', `/${encodeURIComponent(themeName)}-theme.css`);
271+
link.setAttribute('href', `${encodeURIComponent(themeName)}-theme.css`);
272272
// wait for the new css to download before removing the old one to prevent a
273273
// flash of unstyled content
274274
link.onload = () => {

src/app/app.module.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { APP_BASE_HREF, CommonModule } from '@angular/common';
1+
import { APP_BASE_HREF, CommonModule, DOCUMENT } from '@angular/common';
22
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
33
import { APP_INITIALIZER, NgModule } from '@angular/core';
44
import { AbstractControl } from '@angular/forms';
@@ -42,9 +42,11 @@ export function getConfig() {
4242
return environment;
4343
}
4444

45-
export function getBase(appConfig: AppConfig) {
46-
return appConfig.ui.nameSpace;
47-
}
45+
const getBaseHref = (document: Document, appConfig: AppConfig): string => {
46+
const baseTag = document.querySelector('head > base');
47+
baseTag.setAttribute('href', `${appConfig.ui.nameSpace}${appConfig.ui.nameSpace.endsWith('/') ? '' : '/'}`);
48+
return baseTag.getAttribute('href');
49+
};
4850

4951
export function getMetaReducers(appConfig: AppConfig): MetaReducer<AppState>[] {
5052
return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
@@ -84,8 +86,8 @@ const PROVIDERS = [
8486
},
8587
{
8688
provide: APP_BASE_HREF,
87-
useFactory: getBase,
88-
deps: [APP_CONFIG]
89+
useFactory: getBaseHref,
90+
deps: [DOCUMENT, APP_CONFIG]
8991
},
9092
{
9193
provide: USER_PROVIDED_META_REDUCERS,

src/app/core/auth/auth.service.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,25 +377,25 @@ describe('AuthService test', () => {
377377
it('should redirect to reload with redirect url', () => {
378378
authService.navigateToRedirectUrl('/collection/123');
379379
// Reload with redirect URL set to /collection/123
380-
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*\\?redirect=' + encodeURIComponent('/collection/123'))));
380+
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*\\?redirect=' + encodeURIComponent('/collection/123'))));
381381
});
382382

383383
it('should redirect to reload with /home', () => {
384384
authService.navigateToRedirectUrl('/home');
385385
// Reload with redirect URL set to /home
386-
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*\\?redirect=' + encodeURIComponent('/home'))));
386+
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*\\?redirect=' + encodeURIComponent('/home'))));
387387
});
388388

389389
it('should redirect to regular reload and not to /login', () => {
390390
authService.navigateToRedirectUrl('/login');
391391
// Reload without a redirect URL
392-
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*(?!\\?)$')));
392+
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*(?!\\?)$')));
393393
});
394394

395395
it('should redirect to regular reload when no redirect url is found', () => {
396396
authService.navigateToRedirectUrl(undefined);
397397
// Reload without a redirect URL
398-
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*(?!\\?)$')));
398+
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*(?!\\?)$')));
399399
});
400400

401401
describe('impersonate', () => {

src/app/core/auth/auth.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,8 @@ export class AuthService {
468468
*/
469469
public navigateToRedirectUrl(redirectUrl: string) {
470470
// Don't do redirect if already on reload url
471-
if (!hasValue(redirectUrl) || !redirectUrl.includes('/reload/')) {
472-
let url = `/reload/${new Date().getTime()}`;
471+
if (!hasValue(redirectUrl) || !redirectUrl.includes('reload/')) {
472+
let url = `reload/${new Date().getTime()}`;
473473
if (isNotEmpty(redirectUrl) && !redirectUrl.startsWith(LOGIN_ROUTE)) {
474474
url += `?redirect=${encodeURIComponent(redirectUrl)}`;
475475
}

src/app/core/locale/locale.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export class LocaleService {
192192
this.routeService.getCurrentUrl().pipe(take(1)).subscribe((currentURL) => {
193193
// Hard redirect to the reload page with a unique number behind it
194194
// so that all state is definitely lost
195-
this._window.nativeWindow.location.href = `/reload/${new Date().getTime()}?redirect=` + encodeURIComponent(currentURL);
195+
this._window.nativeWindow.location.href = `reload/${new Date().getTime()}?redirect=` + encodeURIComponent(currentURL);
196196
});
197197

198198
}

src/app/core/reload/reload.guard.spec.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import { ReloadGuard } from './reload.guard';
21
import { Router } from '@angular/router';
2+
import { AppConfig } from '../../../config/app-config.interface';
3+
import { DefaultAppConfig } from '../../../config/default-app-config';
4+
import { ReloadGuard } from './reload.guard';
35

46
describe('ReloadGuard', () => {
57
let guard: ReloadGuard;
68
let router: Router;
9+
let appConfig: AppConfig;
710

811
beforeEach(() => {
912
router = jasmine.createSpyObj('router', ['parseUrl', 'createUrlTree']);
10-
guard = new ReloadGuard(router);
13+
appConfig = new DefaultAppConfig();
14+
guard = new ReloadGuard(router, appConfig);
1115
});
1216

1317
describe('canActivate', () => {
@@ -27,7 +31,7 @@ describe('ReloadGuard', () => {
2731

2832
it('should create a UrlTree with the redirect URL', () => {
2933
guard.canActivate(route, undefined);
30-
expect(router.parseUrl).toHaveBeenCalledWith(redirectUrl);
34+
expect(router.parseUrl).toHaveBeenCalledWith(redirectUrl.substring(1));
3135
});
3236
});
3337

0 commit comments

Comments
 (0)