* angular2 uplift to version 5

* meta tag stuff added
* ssr changes for universal
This commit is contained in:
Danyi Dávid 2018-02-25 21:18:58 +01:00
parent afa49a8400
commit 291ad5047f
15 changed files with 7164 additions and 2648 deletions

View File

@ -6,7 +6,7 @@
"apps": [ "apps": [
{ {
"root": "src", "root": "src",
"outDir": "dist", "outDir": "dist/browser",
"assets": [ "assets": [
"assets", "assets",
"favicon.ico" "favicon.ico"
@ -32,12 +32,14 @@
}, },
{ {
"name": "ssr", "name": "ssr",
"platform": "server",
"root": "src", "root": "src",
"outDir": "dist-ssr", "outDir": "dist/server",
"assets": [ "assets": [
"assets", "assets",
"favicon.ico" "favicon.ico"
], ],
"index": "index.html",
"main": "main-ssr.ts", "main": "main-ssr.ts",
"tsconfig": "tsconfig.app-ssr.json", "tsconfig": "tsconfig.app-ssr.json",
"prefix": "app", "prefix": "app",
@ -51,8 +53,7 @@
"environments": { "environments": {
"dev": "environments/environment.ts", "dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts" "prod": "environments/environment.prod.ts"
}, }
"platform": "server"
} }
], ],
"e2e": { "e2e": {

View File

@ -20,17 +20,18 @@ host('alfheim.ragnarok.yvan.hu')
->stage('production') ->stage('production')
->user('yvan') ->user('yvan')
->forwardAgent() ->forwardAgent()
->set('ng_target', 'production') // ->set('ng_target', 'production')
->set('ng_environment', 'prod') // ->set('ng_environment', 'prod')
->set('env_vars', 'NODE_ENV=production') ->set('env_vars', 'NODE_ENV=production')
->set('deploy_path', '/var/www/photos/frontend'); ->set('deploy_path', '/var/www/photos/frontend');
// Tasks // Tasks
desc('Prepare release'); desc('Prepare release');
task('deploy:ng-prepare', function() { task('deploy:ng-prepare', function() {
runLocally("ng build --target={{ng_target}} --environment={{ng_environment}}"); runLocally("npm run build:ssr");
runLocally("ng build --target={{ng_target}} --environment={{ng_environment}} --app ssr --output-hashing=none"); // runLocally("ng build --target={{ng_target}} --environment={{ng_environment}}");
runLocally("tar -cJf dist.tar.xz dist dist-ssr render.js package.json pm2.json"); // runLocally("ng build --target={{ng_target}} --environment={{ng_environment}} --app ssr --output-hashing=none");
runLocally("tar -cJf dist.tar.xz dist server.js package.json pm2.json");
}); });
desc('Upload release'); desc('Upload release');

9421
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,49 +5,53 @@
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build --prod",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",
"e2e": "ng e2e", "e2e": "ng e2e",
"sbuild": "ng build --aot && ng build --aot --app ssr" "build:ssr": "npm run build:client-and-server-bundles",
"serve:ssr": "node dist/server.js",
"build:client-and-server-bundles": "ng build --prod && ng build --prod --app ssr --output-hashing=false"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^4.2.4", "@angular/animations": "^5.2.0",
"@angular/common": "^4.2.4", "@angular/common": "^5.2.0",
"@angular/compiler": "^4.2.4", "@angular/compiler": "^5.2.0",
"@angular/core": "^4.2.4", "@angular/core": "^5.2.0",
"@angular/forms": "^4.2.4", "@angular/forms": "^5.2.0",
"@angular/http": "^4.2.4", "@angular/http": "^5.2.0",
"@angular/platform-browser": "^4.2.4", "@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^4.2.4", "@angular/platform-browser-dynamic": "^5.2.0",
"@angular/platform-server": "^4.3.3", "@angular/platform-server": "^5.2.0",
"@angular/router": "^4.2.4", "@angular/router": "^5.2.0",
"@nguniversal/express-engine": "^5.0.0-beta.5",
"@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"normalize.css": "^7.0.0", "normalize.css": "^7.0.0",
"pm2": "^2.6.1", "pm2": "^2.6.1",
"rxjs": "^5.4.2", "rxjs": "^5.5.6",
"zone.js": "^0.8.14" "zone.js": "^0.8.19"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "1.3.0-rc.5", "@angular/cli": "^1.7.1",
"@angular/compiler-cli": "^4.2.4", "@angular/compiler-cli": "^5.2.0",
"@angular/language-service": "^4.2.4", "@angular/language-service": "^5.2.0",
"@types/jasmine": "~2.5.53", "@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2", "@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60", "@types/node": "~6.0.60",
"codelyzer": "~3.1.1", "codelyzer": "~4.0.1",
"jasmine-core": "~2.6.2", "jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.1.0", "jasmine-spec-reporter": "~4.2.1",
"karma": "~1.7.0", "karma": "~2.0.0",
"karma-chrome-launcher": "~2.1.1", "karma-chrome-launcher": "~2.2.0",
"karma-cli": "~1.0.1", "karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1", "karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0", "karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2", "karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2", "protractor": "~5.1.2",
"ts-node": "~3.2.0", "ts-node": "~4.1.0",
"tslint": "~5.3.2", "tslint": "~5.9.1",
"typescript": "~2.3.3" "typescript": "~2.5.3"
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"apps" : [{ "apps" : [{
"name" : "gallery-frontend", "name" : "gallery-frontend",
"script" : "./render.js", "script" : "./server.js",
"watch" : true, "watch" : true,
"env": { "env": {
"NODE_ENV": "development" "NODE_ENV": "development"

View File

@ -1,35 +0,0 @@
const PORT = 4000;
//require('reflect-metadata');
require('zone.js/dist/zone-node');
const { renderModuleFactory } = require('@angular/platform-server');
const { enableProdMode } = require('@angular/core');
const { join } = require('path');
const express = require('express');
const fs = require('fs');
const { AppServerModuleNgFactory } = require('./dist-ssr/main.bundle');
enableProdMode();
const app = express();
let template = fs.readFileSync('dist/index.html', 'utf8');
app.engine('html', (_, options, callback) => {
renderModuleFactory(AppServerModuleNgFactory, {
document: template,
url: options.req.url
}).then(html => callback(null, html));
});
app.set('view engine', 'html');
app.set('views', 'dist')
app.get('*.*', express.static(join(__dirname, 'dist')));
app.get('*', (req, res) => {
res.render('index', { req });
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});

43
server.js Normal file
View File

@ -0,0 +1,43 @@
"use strict";
require("zone.js/dist/zone-node");
require("reflect-metadata");
const { enableProdMode } = require('@angular/core');
const express = require('express');
const { join } = require('path');
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');
// Express Engine
const { ngExpressEngine } = require("@nguniversal/express-engine");
// Import module map for lazy loading
const { provideModuleMap } = require("@nguniversal/module-map-ngfactory-loader");
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
// All regular routes use the Universal engine
app.get('*', function (req, res) {
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req: req });
});
// Start up the Node server
app.listen(PORT, function () {
console.log("Node server listening on http://localhost:" + PORT);
});

View File

@ -1,10 +1,10 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] styleUrls: ['./app.component.css']
}) })
export class AppComponent { export class AppComponent {
title = 'app'; title = 'app';
} }

View File

@ -1,25 +1,33 @@
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" import { BrowserAnimationsModule } from "@angular/platform-browser/animations"
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { PLATFORM_ID, APP_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { GalleryModule } from "./gallery/gallery.module"; import { GalleryModule } from "./gallery/gallery.module";
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent AppComponent
], ],
imports: [ imports: [
BrowserModule.withServerTransition({ BrowserModule.withServerTransition({
appId: 'angullary' appId: 'angullary'
}), }),
BrowserAnimationsModule, BrowserAnimationsModule,
AppRoutingModule, AppRoutingModule,
GalleryModule, GalleryModule,
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { export class AppModule {
constructor(@Inject(PLATFORM_ID) private platformId: Object,
@Inject(APP_ID) private appId: string) {
const platform = isPlatformBrowser(platformId) ?
'in the browser' : 'on the server';
console.log(`Running ${platform} with appId=${appId}`);
}
} }

View File

@ -1,14 +1,18 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server'; import { ServerModule } from '@angular/platform-server';
import { AppComponent } from './app.component'; import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({ @NgModule({
imports: [ imports: [
AppModule, AppModule,
ServerModule ServerModule,
], ModuleMapLoaderModule,
bootstrap: [ ],
AppComponent bootstrap: [
] AppComponent,
]
}) })
export class AppServerModule {} export class AppServerModule {}

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { Title } from "@angular/platform-browser"; import { Meta, Title } from "@angular/platform-browser";
import { Album } from "../shared/album.model";
import { DisplayImageService } from "../display-image/display-image.service";
import { environment } from "../../../environments/environment"; import { environment } from "../../../environments/environment";
import { Image } from "../shared/image.model"; import { Album, Image } from "../shared";
import { DisplayImageService } from "../display-image/display-image.service";
import { Thumbnail } from "../shared/thumbnail.model"; import { Thumbnail } from "../shared/thumbnail.model";
const IDX_LEFT = 0; const IDX_LEFT = 0;
@ -32,7 +31,8 @@ export class AlbumComponent implements OnInit {
constructor(private titleService: Title, constructor(private titleService: Title,
private route: ActivatedRoute, private route: ActivatedRoute,
private displayImageService: DisplayImageService) { private displayImageService: DisplayImageService,
private metaService: Meta) {
} }
ngOnInit() { ngOnInit() {
@ -40,6 +40,10 @@ export class AlbumComponent implements OnInit {
this.album = data.albums.find(album => album.slug == this.route.snapshot.params.slug); this.album = data.albums.find(album => album.slug == this.route.snapshot.params.slug);
this.titleService.setTitle(`${this.album.name} - photos.yvan.hu`); this.titleService.setTitle(`${this.album.name} - photos.yvan.hu`);
this.initThumbnails(); this.initThumbnails();
this.metaService.updateTag({
property: 'og:image',
content: this.imageUrl(this.album.coverImage)
});
this.route.params.subscribe((params: { this.route.params.subscribe((params: {
slug: string, slug: string,
image: string image: string

View File

@ -1,10 +1,10 @@
import {Component, OnInit} from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {Title} from "@angular/platform-browser"; import { Title, Meta } from "@angular/platform-browser";
import {Album} from "../shared"; import { environment } from "../../../environments/environment";
import {ActivatedRoute} from "@angular/router"; import { Album } from "../shared";
import {environment} from "../../../environments/environment"; import { ActivatedRoute } from "@angular/router";
import {Thumbnail} from "../shared/thumbnail.model"; import { Thumbnail } from "../shared/thumbnail.model";
const IDX_LEFT = 0; const IDX_LEFT = 0;
const IDX_MIDDLE = 1; const IDX_MIDDLE = 1;
@ -12,66 +12,71 @@ const IDX_RIGHT = 2;
const THUMB_MARGIN = 10; const THUMB_MARGIN = 10;
@Component({ @Component({
selector: 'app-gallery', selector: 'app-gallery',
templateUrl: './gallery.component.html', templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.css'], styleUrls: ['./gallery.component.css'],
}) })
export class GalleryComponent implements OnInit { export class GalleryComponent implements OnInit {
public albums: Array<Album> = []; public albums: Array<Album> = [];
private leftHeight: number = 0; private leftHeight: number = 0;
private middleHeight: number = 0; private middleHeight: number = 0;
private rightHeight: number = 0; private rightHeight: number = 0;
public thumbnailsLeft: Array<Thumbnail> = []; public thumbnailsLeft: Array<Thumbnail> = [];
public thumbnailsMiddle: Array<Thumbnail> = []; public thumbnailsMiddle: Array<Thumbnail> = [];
public thumbnailsRight: Array<Thumbnail> = []; public thumbnailsRight: Array<Thumbnail> = [];
constructor(private titleService: Title, constructor(private titleService: Title,
private route: ActivatedRoute) { private route: ActivatedRoute,
} private metaService: Meta) {
}
ngOnInit() { ngOnInit() {
this.titleService.setTitle('Albums - photos.yvan.hu'); this.titleService.setTitle('Albums - photos.yvan.hu');
this.route.data.subscribe((data: { albums: Array<Album> }) => { this.route.data.subscribe((data: { albums: Array<Album> }) => {
this.albums = data.albums.filter(album => album.isPublic).reverse(); this.albums = data.albums.filter(album => album.isPublic).reverse();
this.initThumbnails(); this.initThumbnails();
}); this.metaService.updateTag({
} property: 'og:image',
content: this.imageUrl(this.albums[0])
});
});
}
private initThumbnails() { private initThumbnails() {
this.albums.map(album => <Thumbnail>{ this.albums.map(album => <Thumbnail>{
slug: album.slug, slug: album.slug,
image: this.imageUrl(album), image: this.imageUrl(album),
label: { label: {
title: album.name, title: album.name,
extraRight: album.images.length + ' images', extraRight: album.images.length + ' images',
extraLeft: album.date, extraLeft: album.date,
}, },
height: album.coverImage.thumbHeight, height: album.coverImage.thumbHeight,
}).map(thumbnail => { }).map(thumbnail => {
let heights = [this.leftHeight, this.middleHeight, this.rightHeight]; let heights = [this.leftHeight, this.middleHeight, this.rightHeight];
let idx = heights.reduce((low, nxt, idx) => nxt < heights[low] ? idx : low, 0); let idx = heights.reduce((low, nxt, idx) => nxt < heights[low] ? idx : low, 0);
switch(idx) { switch (idx) {
case IDX_LEFT: case IDX_LEFT:
this.thumbnailsLeft.push(thumbnail); this.thumbnailsLeft.push(thumbnail);
this.leftHeight += thumbnail.height + THUMB_MARGIN; this.leftHeight += thumbnail.height + THUMB_MARGIN;
break; break;
case IDX_MIDDLE: case IDX_MIDDLE:
this.thumbnailsMiddle.push(thumbnail); this.thumbnailsMiddle.push(thumbnail);
this.middleHeight += thumbnail.height + THUMB_MARGIN; this.middleHeight += thumbnail.height + THUMB_MARGIN;
break; break;
case IDX_RIGHT: case IDX_RIGHT:
this.thumbnailsRight.push(thumbnail); this.thumbnailsRight.push(thumbnail);
this.rightHeight += thumbnail.height + THUMB_MARGIN; this.rightHeight += thumbnail.height + THUMB_MARGIN;
break; break;
} }
}); });
} }
private imageUrl(album: Album): string { private imageUrl(album: Album): string {
return environment.apiUrl + `/image/${album.slug}/${album.coverImage.path}/thumb`; return environment.apiUrl + `/image/${album.slug}/${album.coverImage.path}/thumb`;
} }
} }

View File

@ -8,7 +8,7 @@ import { environment } from "../../../environments/environment";
import { Album, Image } from "."; import { Album, Image } from ".";
@Injectable() @Injectable()
export class GalleryService implements Resolve<Array<Album>> { export class GalleryService implements Resolve<Array<Album>|false> {
public albums: Array<Album> = []; public albums: Array<Album> = [];
@ -20,17 +20,12 @@ export class GalleryService implements Resolve<Array<Album>> {
return this.httpService.get(environment.apiUrl + '/api/galleries').map(res => res.json()); return this.httpService.get(environment.apiUrl + '/api/galleries').map(res => res.json());
} }
// public listGalleryImages(gallerySlug: string): Observable<Image> {
// return this.httpService.get(environment.apiUrl + '/api/gallery/' + gallerySlug).map(res => res.json());
// }
/** /**
*
* @param {ActivatedRouteSnapshot} route * @param {ActivatedRouteSnapshot} route
* @returns {Promise<Array<Album>>} * @returns {Promise<Array<Album>>}
* @todo add some cache ttl to albums? * @todo add some cache ttl to albums?
*/ */
public resolve(route: ActivatedRouteSnapshot): Promise<Array<Album>> { public resolve(route: ActivatedRouteSnapshot): Promise<Array<Album>|false> {
if(this.albums.length) { if(this.albums.length) {
return Promise.resolve(this.albums); return Promise.resolve(this.albums);
} }

View File

@ -1,23 +1,26 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Gallery</title> <title>Gallery</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="og:title" content="Angullery"/>
<link rel="icon" type="image/x-icon" href="favicon.ico"> <meta name="og:description" content="A really simple photo album"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head> </head>
<body> <body>
<app-root> <app-root>
<div class="ui main container"> <div class="ui main container">
<div class="ui visible massive floating icon message"> <div class="ui visible massive floating icon message">
<i class="notched circle loading icon"></i> <i class="notched circle loading icon"></i>
<div class="content"> <div class="content">
<div class="header">Loading ...</div> <div class="header">Loading ...</div>
</div>
</div> </div>
</div>
</div> </div>
</app-root> </app-root>
</body> </body>
</html> </html>

View File

@ -5,7 +5,7 @@ import { AppModule } from './app/app.module';
import { environment } from './environments/environment'; import { environment } from './environments/environment';
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(AppModule); platformBrowserDynamic().bootstrapModule(AppModule);