diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..0a5218d --- /dev/null +++ b/server.ts @@ -0,0 +1,110 @@ +import 'zone.js/node'; + +import { ngExpressEngine } from '@nguniversal/express-engine'; +import * as express from 'express'; +import * as compression from 'compression'; +import { join } from 'path'; + +import { AppServerModule } from './src/main.server'; +import { APP_BASE_HREF } from '@angular/common'; +import { existsSync } from 'fs'; +import {Prometheus} from "./prometheus"; +import {Counter} from "prom-client"; +import {REQUEST, RESPONSE} from "./src/app/openaireLibrary/utils/tokens"; + +// The Express app is exported so that it can be used by serverless Functions. +export function app() { + const server = express(); + server.use(compression()); + const distFolder = join(process.cwd(), 'dist/eosc/browser'); + const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; + + const prometheus: Prometheus = new Prometheus(); + + // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) + server.engine('html', ngExpressEngine({ + bootstrap: AppServerModule, + inlineCriticalCss: false + })); + + server.set('view engine', 'html'); + server.set('views', distFolder); + + // Example Express Rest API endpoints + // server.get('/api/**', (req, res) => { }); + // Serve static files from /browser + server.get('*.*', express.static(distFolder, { + maxAge: '1y' + })); + + server.get('/metrics', (req, res) => { + res.set('Content-Type', prometheus.register.contentType); + res.end(prometheus.register.metrics()); + }); + + // All regular routes use the Universal engine + server.get('*', (req, res) => { + let start = new Date(); + let counter: Counter = prometheus.counters.get(req.path); + if(counter !== undefined) { + counter.inc(1, new Date()); + res.render(indexHtml, { + req, providers: [ + { + provide: APP_BASE_HREF, + useValue: req.baseUrl + }, + { + provide: REQUEST, useValue: (req) + }, + { + provide: RESPONSE, useValue: (res) + } + ] + }); + // event triggers when express is done sending response + res.on('finish', function() { + console.log(new Date().getTime() - start.getTime()); + }); + } else { + res.render(indexHtml, { + req, providers: [ + { + provide: APP_BASE_HREF, + useValue: req.baseUrl + }, + { + provide: REQUEST, useValue: (req) + }, + { + provide: RESPONSE, useValue: (res) + } + ] + }); + } + }); + + return server; +} + +function run() { + const port = process.env.PORT || 4000; + + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} + +// Webpack will replace 'require' with '__webpack_require__' +// '__non_webpack_require__' is a proxy to Node 'require' +// The below code is to ensure that the server is run only when not requiring the bundle. +declare const __non_webpack_require__: NodeRequire; +const mainModule = __non_webpack_require__.main; +const moduleFilename = mainModule && mainModule.filename || ''; +if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { + run(); +} + +export * from './src/main.server'; diff --git a/src/app/app.server.module.ts b/src/app/app.server.module.ts new file mode 100644 index 0000000..561695a --- /dev/null +++ b/src/app/app.server.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import {ServerModule} from '@angular/platform-server'; + +import { AppModule } from './app.module'; +import { AppComponent } from './app.component'; + +@NgModule({ + imports: [ + AppModule, + ServerModule + ], + bootstrap: [AppComponent], +}) +export class AppServerModule {} diff --git a/src/app/openaireLibrary b/src/app/openaireLibrary index 25b3c61..c8caa12 160000 --- a/src/app/openaireLibrary +++ b/src/app/openaireLibrary @@ -1 +1 @@ -Subproject commit 25b3c6121b00b18f482b08ad9d7bf1199cb63a9a +Subproject commit c8caa1277aa456aab7502614e5482b373d6858f0 diff --git a/src/assets/common-assets b/src/assets/common-assets index 038684a..ee1a55a 160000 --- a/src/assets/common-assets +++ b/src/assets/common-assets @@ -1 +1 @@ -Subproject commit 038684a0dca02700164e467abd390bbd865df3d9 +Subproject commit ee1a55a529fde844f54761633e59f3be791717e3 diff --git a/src/main.server.ts b/src/main.server.ts new file mode 100644 index 0000000..0930b37 --- /dev/null +++ b/src/main.server.ts @@ -0,0 +1,15 @@ +/*************************************************************************************************** + * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. +*/ +import '@angular/localize/init'; + +import { enableProdMode } from '@angular/core'; + +import {properties} from './environments/environment'; + +if (properties.environment !== "development") { + enableProdMode(); +} + +export { AppServerModule } from './app/app.server.module'; + diff --git a/src/tsconfig.server.json b/src/tsconfig.server.json new file mode 100644 index 0000000..5601502 --- /dev/null +++ b/src/tsconfig.server.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.app.json", + "compilerOptions": { + "outDir": "../out-tsc/app-server", + "types": [ + "node" + ] + }, + "files": [ + "main.server.ts", + "../server.ts" + ], + "angularCompilerOptions": { + "entryModule": "./app/app.server.module#AppServerModule" + } +}