Initiate angular project with firebase and base components

This commit is contained in:
2019-05-21 11:29:32 +09:00
parent fb83703c54
commit aa073c1b77
20 changed files with 639 additions and 62 deletions

View File

@ -1,5 +1,5 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ErrorHandler, NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -7,6 +7,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularFireModule } from '@angular/fire';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { environment } from '@env/environment';
import { GlobalErrorHandler } from '@app/core/global-error-handler/global-error-handler.service';
import { LogService } from '@app/core/services';
@NgModule({
declarations: [
@ -18,7 +20,13 @@ import { environment } from '@env/environment';
AngularFireModule.initializeApp(environment.firebaseConfig),
AngularFireDatabaseModule,
],
providers: [],
providers: [
LogService,
{
provide: ErrorHandler,
useClass: GlobalErrorHandler
},
],
bootstrap: [ AppComponent ]
})
export class AppModule {

View File

@ -0,0 +1,16 @@
/* tslint:disable:no-unused-variable */
import { inject, TestBed } from '@angular/core/testing';
import { GlobalErrorHandler } from './global-error-handler.service';
describe( 'Service: GlobalErrorHandler', () => {
beforeEach( () => {
TestBed.configureTestingModule( {
providers: [ GlobalErrorHandler ]
} );
} );
it( 'should ...', inject( [ GlobalErrorHandler ], ( service: GlobalErrorHandler ) => {
expect( service ).toBeTruthy();
} ) );
} );

View File

@ -0,0 +1,35 @@
import { ErrorHandler, Injectable, Injector } from '@angular/core';
import * as StackTrace from 'stacktrace-js';
import { LogService } from '@app/core/services';
@Injectable()
export class GlobalErrorHandler extends ErrorHandler {
constructor( private injector: Injector ) {
super();
}
public handleError( error ) {
const logService: LogService = this.injector.get( LogService );
const message = error.message ? error.message : error.toString();
if ( error.status ) {
error = new Error( message );
}
StackTrace.fromError( error ).then( ( stackframes ) => {
const stackString = stackframes
.splice( 0, 10 )
.map( ( sf ) => {
return sf.toString();
} )
.toString();
const errorTraceStr = `Error message: ${ message }. Stack trace: ${ stackString }`;
logService.logError( errorTraceStr );
throw error;
} );
}
}

View File

@ -0,0 +1,2 @@
export * from './storage/storage';
export * from './user/user';

View File

@ -0,0 +1,5 @@
export class Storage {
pageData: object;
sessionData: object;
localData: object;
}

View File

@ -0,0 +1,36 @@
export class User {
timeGetToken: number;
token: string;
dataUser: DataUser;
}
export class DataUser {
firstName: string;
lastName: string;
address: string;
postalCode: string;
city: string;
country: string;
phoneNumber: string;
email: string;
birthday: number;
licenseNumber: string;
licenseValidity: number;
licenseCategories: string[];
constructor() {
this.firstName = null;
this.lastName = null;
this.address = null;
this.postalCode = null;
this.city = null;
this.country = null;
this.phoneNumber = null;
this.email = null;
this.birthday = null;
this.licenseNumber = null;
this.licenseValidity = null;
this.licenseCategories = null;
}
}

View File

@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe( 'AuthService', () => {
beforeEach( () => TestBed.configureTestingModule( {} ) );
it( 'should be created', () => {
const service: AuthService = TestBed.get( AuthService );
expect( service ).toBeTruthy();
} );
} );

View File

@ -0,0 +1,90 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { Router } from '@angular/router';
import * as sha512 from 'js-sha512';
// import * as config from '@assets/config.json';
import { User } from '@app/core/model';
import { StorageService } from '@app/core/services/storage/storage.service';
import { LogService } from '@app/core/services/log/log.service';
@Injectable( { providedIn: 'root' } )
export class AuthService {
public currentUser: Observable<User>;
// public emailRegex = new RegExp( );
private currentUserSubject: BehaviorSubject<User>;
constructor( private http: HttpClient, private router: Router, private stor: StorageService, private looger: LogService ) {
this.currentUserSubject = new BehaviorSubject<User>( stor.getLocal( 'user' ) as User );
this.currentUser = this.currentUserSubject.asObservable();
}
public get currentUserValue(): User {
if ( this.currentUserSubject.value && this.currentUserSubject.value.timeGetToken + 60 * 60 * 1000 < new Date().getTime() ) {
const data = new FormData();
data.append( 'auth_token', this.currentUserSubject.value.token );
// this.http.post<any>( environment.baseUrl + config.url.upauth, data ).subscribe( ( res ) => {
// if ( res.statusCode !== 200 ) {
// this.logout();
// } else {
// this.currentUserSubject.value.timeGetToken = new Date().getTime();
// this.stor.setLocal( 'user', this.currentUserSubject.value );
// }
// } );
}
return this.currentUserSubject.value;
}
public login( username: string, password: string ): any {
const data: FormData = new FormData();
data.append( 'email', username );
data.append( 'password', sha512.sha512( password ) );
// return this.http.post<any>( environment.baseUrl + config.url.auth, data )
// .pipe( map( result => {
// let user = new User();
//
// this.looger.logInfo( result );
// // login successful if there's a jwt token in the response
// if ( result && result.body && result.statusCode === 200 ) {
//
// user.token = result.body;
// user.timeGetToken = new Date().getTime();
//
// // store user details and jwt token in local storage to keep user logged in between page refreshes
// this.stor.setLocal( 'user', user );
// this.currentUserSubject.next( user );
// } else {
// user = result.statusCode;
// }
//
// return user;
// } ) );
}
public logout(): void {
// remove user from local storage to log user out
this.stor.setLocal( 'user' );
this.currentUserSubject.next( null );
this.router.navigate( [ '/login' ] );
}
public createAccount( email: string, password: string, repeat: string ): any {
// const data: FormData = new FormData();
// if ( password === repeat && this.emailRegex.test( email ) ) {
// data.append( 'email', email );
// data.append( 'password', sha512.sha512( password ) );
// return this.http.post( environment.baseUrl + config.url.register, data );
// }
return null;
}
}

View File

@ -0,0 +1,2 @@
export * from './auth/auth.service';
export * from './log/log.service';

View File

@ -0,0 +1,8 @@
export interface LogFields {
userId?: string;
elapsedTime?: number;
requestPath?: string;
environment?: string;
appVersion?: string;
url?: string;
}

View File

@ -0,0 +1,20 @@
/* tslint:disable:no-unused-variable */
import { inject, TestBed } from '@angular/core/testing';
import { LogService } from './log.service';
describe( 'Service: Log', () => {
beforeEach( () => {
TestBed.configureTestingModule( {
imports: [],
providers: [ LogService ]
} );
} );
it(
'should ...',
inject( [ LogService ], ( service: LogService ) => {
expect( service ).toBeTruthy();
} )
);
} );

View File

@ -0,0 +1,63 @@
import { Injectable } from '@angular/core';
import { environment } from '@env/environment';
import { LogFields } from './log-data.interface';
import { Logger } from './logger';
@Injectable( {
providedIn: 'root'
} )
export class LogService {
private logger: Logger;
constructor() {
this.initialize();
}
public initialize() {
this.logger = new Logger( environment.appName );
}
public logHttpInfo( info: any, elapsedTime: number, requestPath: string ) {
// TODO: create and set correlation id
const url = location.href;
const logFields: LogFields = {
environment: environment.env,
// userId: this.userId,
requestPath,
elapsedTime,
url,
};
this.logger.log( 'Information', `${ info }`, logFields );
}
public logError( errorMsg: string ) {
const url = location.href;
const logFields: LogFields = {
environment: environment.env,
// userId: this.userId,
requestPath: '',
elapsedTime: 0,
url: url,
};
this.logger.log( 'Error', errorMsg, logFields );
}
public logInfo( info: any ) {
const url = location.href;
const logFields: LogFields = {
environment: environment.env,
// userId: this.userId,
requestPath: '',
elapsedTime: 0,
url,
};
this.logger.log( 'Information', info, logFields );
}
}

View File

@ -0,0 +1,140 @@
import { environment } from '@env/environment';
import * as moment from 'moment';
import { Subject } from 'rxjs';
import { debounceTime, filter } from 'rxjs/operators';
import { LogFields } from './log-data.interface';
export type LogType = 'Error' | 'Information';
interface LogEntry {
type: LogType;
message: string;
data: LogFields;
}
enum LoggerEvents {
Flush = 1
}
export class Logger {
private readonly APP_FIELD = 'Application';
private readonly ENV_FIELD = 'Environment';
private readonly VERSION_FIELD = 'Version';
private readonly USER_NAME_FIELD = 'UserName';
private readonly ELAPSED_MS_FIELD = 'ElapsedMilliseconds';
private readonly REQUEST_PATH_FIELD = 'RequestPath';
private readonly URL_FIELD = 'Url';
private readonly APP_STATE_FIELD = 'AppState';
private buffer: LogEntry[] = [];
private flush = new Subject<LoggerEvents>();
constructor( private appName: string, private logEndpoint?: string ) {
this.flush
.pipe( debounceTime( 5000 ), filter( ( event ) => event === LoggerEvents.Flush ) )
.subscribe( () => this.flushBuffer() );
}
public log( type: LogType, message: string, data: LogFields ) {
this.buffer.push( {
type,
message,
data
} );
this.flush.next( LoggerEvents.Flush );
}
private flushBuffer() {
const data = this.buffer.splice( 0 );
if ( data.length === 0 ) {
return;
}
const body = data
.map( ( entry ) => this.buildLogString( entry ) )
.reduce( ( sum, entry ) => ( sum += entry ), '' );
if ( !environment.production ) {
// This is nested to make sure we always end up in here when running locally
// as in do not && this to the above if...
// tslint:disable-next-line:no-console
console.log( {
body,
data
} );
} else {
const xobj = new XMLHttpRequest();
// tslint:disable-next-line:no-console
if ( this.logEndpoint ) {
xobj.onerror = ( err ) => console.error( err );
xobj.open( 'POST', this.logEndpoint, true );
xobj.send( body );
}
}
}
private buildLogString( entry: LogEntry ): string {
const index = this.buildIndexChunk();
const body = this.buildBodyChunk( entry );
return `${ index }\n${ body }\n`;
}
private buildIndexChunk() {
const date = moment();
const index = {
index: {
_index: `logstash-${ date.format( 'YYYY.M.D' ) }`,
_type: 'logevent'
}
};
return JSON.stringify( index );
}
private buildBodyChunk( entry: LogEntry ) {
const { type, message, data } = entry;
const level = type;
const date = moment();
const messageTemplate = this.getMessageTemplate();
const fields = this.getFields( data );
const body = {
'@timestamp': `${ date.toISOString() }`,
level,
messageTemplate,
message,
fields
};
return JSON.stringify( body );
}
private getMessageTemplate() {
const fields: string[] = [
this.APP_FIELD,
this.ENV_FIELD,
this.VERSION_FIELD,
this.USER_NAME_FIELD,
this.ELAPSED_MS_FIELD,
this.REQUEST_PATH_FIELD,
this.URL_FIELD,
this.APP_STATE_FIELD
];
const template = fields.map( ( field ) => `{${ field }}` ).join( ' - ' );
return template;
}
private getFields( data: LogFields ) {
return {
[ this.APP_FIELD ]: this.appName,
[ this.ENV_FIELD ]: data.environment,
[ this.VERSION_FIELD ]: data.appVersion,
[ this.USER_NAME_FIELD ]: data.userId,
[ this.ELAPSED_MS_FIELD ]: data.elapsedTime,
[ this.REQUEST_PATH_FIELD ]: data.requestPath,
[ this.URL_FIELD ]: data.url
};
}
}

View File

@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { StorageService } from './storage.service';
describe( 'StorageService', () => {
beforeEach( () => TestBed.configureTestingModule( {} ) );
it( 'should be created', () => {
const service: StorageService = TestBed.get( StorageService );
expect( service ).toBeTruthy();
} );
} );

View File

@ -0,0 +1,103 @@
import { Injectable } from '@angular/core';
import { Storage } from '@app/core/model';
@Injectable()
export class StorageService {
private data: Storage;
constructor() {
const localData = localStorage.getItem( 'c2a' );
const sessionData = sessionStorage.getItem( 'c2a' );
this.data = new Storage();
this.data.pageData = {};
this.data.sessionData = ( sessionData ? JSON.parse( sessionData ) : {} );
this.data.localData = ( localData ? JSON.parse( localData ) : {} );
}
public getLocal( key: string ): string | number | boolean | object {
const value = this.data.localData[ key ];
if ( !value ) {
return null;
}
return value;
}
public getSession( key: string ): string | number | boolean | object {
const value = this.data.sessionData[ key ];
if ( !value ) {
return null;
}
return value;
}
public getPage( key: string ): string | number | boolean | object {
const value = this.data.pageData[ key ];
if ( !value ) {
return null;
}
return value;
}
public setLocal( key: string, value?: any ): void {
let val: string | number | boolean;
if ( typeof value === 'boolean' || 'string' || 'number' || 'object' ) {
val = value;
} else {
val = null;
}
if ( val === null ) {
if ( this.data.localData[ key ] ) {
delete this.data.localData[ key ];
}
} else {
this.data.localData[ key ] = val;
}
localStorage.setItem( 'c2a', JSON.stringify( this.data.localData ) );
}
public setSession( key: string, value?: any ): void {
let val: string | number | boolean;
if ( typeof value === 'boolean' || 'string' || 'number' || 'object' ) {
val = value;
} else {
val = null;
}
if ( val === null ) {
if ( this.data.sessionData[ key ] ) {
delete this.data.sessionData[ key ];
}
} else {
this.data.sessionData[ key ] = val;
}
sessionStorage.setItem( 'c2a', JSON.stringify( this.data.localData ) );
}
public setPage( key: string, value?: any ): void {
let val: string | number | boolean;
if ( typeof value === 'boolean' || 'string' || 'number' || 'object' ) {
val = value;
} else {
val = null;
}
if ( val === null ) {
if ( this.data.pageData[ key ] ) {
delete this.data.pageData[ key ];
}
} else {
this.data.pageData[ key ] = val;
}
}
}

0
src/assets/config.json Normal file
View File

View File

@ -1,5 +1,7 @@
export const environment = {
production: true,
appName: 'DiscoTrip',
production: false,
env: 'production',
firebaseConfig: {
apiKey: 'AIzaSyDe48emSyoXS3WV6vM4GjYiJwdcMhcspqY',
authDomain: 'disco-trip.firebaseapp.com',

View File

@ -3,7 +3,9 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
appName: 'DiscoTrip',
production: false,
env: 'local',
firebaseConfig: {
apiKey: 'AIzaSyDe48emSyoXS3WV6vM4GjYiJwdcMhcspqY',
authDomain: 'disco-trip.firebaseapp.com',