Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
198eaf57d3 | |||
6783ea2ebb | |||
a35701fe24 | |||
5db90f1787 | |||
81fe538484 | |||
51884913be | |||
8886082dfa | |||
3b12e5b85b | |||
6c1119caec |
20
CHANGELOG.md
20
CHANGELOG.md
@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 1.17.0 - 15.06.2021
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved the error page of the sign in with fingerprint
|
||||||
|
- Disable the sign in with fingerprint selector for the demo user
|
||||||
|
- Upgraded `angular` from version `11.2.4` to `12.0.4`
|
||||||
|
- Upgraded `angular-material-css-vars` from version `1.1.2` to `1.2.0`
|
||||||
|
- Upgraded `chart.js` from version `3.2.1` to `3.3.2`
|
||||||
|
- Upgraded `date-fns` from version `2.19.0` to `2.22.1`
|
||||||
|
- Upgraded `eslint` and `prettier` dependencies
|
||||||
|
- Upgraded `ngx-device-detector` from version `2.0.6` to `2.1.1`
|
||||||
|
- Upgraded `ngx-markdown` from version `11.1.2` to `12.0.1`
|
||||||
|
|
||||||
|
## 1.16.0 - 14.06.2021
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved the sign in with fingerprint
|
||||||
|
|
||||||
## 1.15.0 - 14.06.2021
|
## 1.15.0 - 14.06.2021
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
12
angular.json
12
angular.json
@ -86,7 +86,6 @@
|
|||||||
"main": "apps/client/src/main.ts",
|
"main": "apps/client/src/main.ts",
|
||||||
"polyfills": "apps/client/src/polyfills.ts",
|
"polyfills": "apps/client/src/polyfills.ts",
|
||||||
"tsConfig": "apps/client/tsconfig.app.json",
|
"tsConfig": "apps/client/tsconfig.app.json",
|
||||||
"aot": true,
|
|
||||||
"assets": [
|
"assets": [
|
||||||
"apps/client/src/assets",
|
"apps/client/src/assets",
|
||||||
{
|
{
|
||||||
@ -121,7 +120,13 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": ["apps/client/src/styles.scss"],
|
"styles": ["apps/client/src/styles.scss"],
|
||||||
"scripts": ["node_modules/marked/lib/marked.js"]
|
"scripts": ["node_modules/marked/lib/marked.js"],
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false,
|
||||||
|
"namedChunks": true
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@ -152,7 +157,8 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": ["{options.outputPath}"]
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"defaultConfiguration": ""
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
@ -57,8 +57,9 @@ export class WebAuthService {
|
|||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
attestationType: 'indirect',
|
attestationType: 'indirect',
|
||||||
authenticatorSelection: {
|
authenticatorSelection: {
|
||||||
userVerification: 'preferred',
|
authenticatorAttachment: 'platform',
|
||||||
requireResidentKey: false
|
requireResidentKey: false,
|
||||||
|
userVerification: 'required'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,7 +144,7 @@ export class WebAuthService {
|
|||||||
{
|
{
|
||||||
id: device.credentialId,
|
id: device.credentialId,
|
||||||
type: 'public-key',
|
type: 'public-key',
|
||||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
transports: ['internal']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
userVerification: 'preferred',
|
userVerification: 'preferred',
|
||||||
|
@ -16,8 +16,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
coverageDirectory: '../../coverage/apps/client',
|
coverageDirectory: '../../coverage/apps/client',
|
||||||
snapshotSerializers: [
|
snapshotSerializers: [
|
||||||
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
|
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||||
'jest-preset-angular/build/AngularSnapshotSerializer.js',
|
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||||
'jest-preset-angular/build/HTMLCommentSerializer.js'
|
'jest-preset-angular/build/serializers/html-comment'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -92,6 +92,13 @@ const routes: Routes = [
|
|||||||
(m) => m.TransactionsPageModule
|
(m) => m.TransactionsPageModule
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'webauthn',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./pages/webauthn/webauthn-page.module').then(
|
||||||
|
(m) => m.WebauthnPageModule
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'zen',
|
path: 'zen',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
|
@ -11,8 +11,11 @@ import { Router } from '@angular/router';
|
|||||||
import { LoginWithAccessTokenDialog } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.component';
|
import { LoginWithAccessTokenDialog } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.component';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||||
|
import {
|
||||||
|
STAY_SIGNED_IN,
|
||||||
|
SettingsStorageService
|
||||||
|
} from '@ghostfolio/client/services/settings-storage.service';
|
||||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||||
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
|
|
||||||
import { InfoItem, User } from '@ghostfolio/common/interfaces';
|
import { InfoItem, User } from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import { EMPTY, Subject } from 'rxjs';
|
import { EMPTY, Subject } from 'rxjs';
|
||||||
@ -43,8 +46,8 @@ export class HeaderComponent implements OnChanges {
|
|||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private impersonationStorageService: ImpersonationStorageService,
|
private impersonationStorageService: ImpersonationStorageService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private tokenStorageService: TokenStorageService,
|
private settingsStorageService: SettingsStorageService,
|
||||||
private webAuthnService: WebAuthnService
|
private tokenStorageService: TokenStorageService
|
||||||
) {
|
) {
|
||||||
this.impersonationStorageService
|
this.impersonationStorageService
|
||||||
.onChangeHasImpersonation()
|
.onChangeHasImpersonation()
|
||||||
@ -108,14 +111,17 @@ export class HeaderComponent implements OnChanges {
|
|||||||
takeUntil(this.unsubscribeSubject)
|
takeUntil(this.unsubscribeSubject)
|
||||||
)
|
)
|
||||||
.subscribe(({ authToken }) => {
|
.subscribe(({ authToken }) => {
|
||||||
this.setToken(authToken, data.staySignedIn);
|
this.setToken(authToken);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setToken(aToken: string, staySignedIn: boolean) {
|
public setToken(aToken: string) {
|
||||||
this.tokenStorageService.saveToken(aToken, staySignedIn);
|
this.tokenStorageService.saveToken(
|
||||||
|
aToken,
|
||||||
|
this.settingsStorageService.getSetting(STAY_SIGNED_IN) === 'true'
|
||||||
|
);
|
||||||
|
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||||
|
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import {
|
||||||
|
STAY_SIGNED_IN,
|
||||||
|
SettingsStorageService
|
||||||
|
} from '@ghostfolio/client/services/settings-storage.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gf-login-with-access-token-dialog',
|
selector: 'gf-login-with-access-token-dialog',
|
||||||
@ -9,13 +14,21 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|||||||
})
|
})
|
||||||
export class LoginWithAccessTokenDialog {
|
export class LoginWithAccessTokenDialog {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
public dialogRef: MatDialogRef<LoginWithAccessTokenDialog>,
|
public dialogRef: MatDialogRef<LoginWithAccessTokenDialog>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
private settingsStorageService: SettingsStorageService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
public onClose(): void {
|
public onChangeStaySignedIn(aValue: MatCheckboxChange) {
|
||||||
|
this.settingsStorageService.setSetting(
|
||||||
|
STAY_SIGNED_IN,
|
||||||
|
aValue.checked?.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onClose() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions>
|
<div mat-dialog-actions>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<mat-checkbox i18n [(ngModel)]="data.staySignedIn"
|
<mat-checkbox i18n (change)="onChangeStaySignedIn($event)"
|
||||||
>Stay signed in</mat-checkbox
|
>Stay signed in</mat-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,10 +79,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
|
|||||||
}
|
}
|
||||||
} else if (error.status === StatusCodes.UNAUTHORIZED) {
|
} else if (error.status === StatusCodes.UNAUTHORIZED) {
|
||||||
if (this.webAuthnService.isEnabled()) {
|
if (this.webAuthnService.isEnabled()) {
|
||||||
this.webAuthnService.login().subscribe(({ authToken }) => {
|
this.router.navigate(['/webauthn']);
|
||||||
this.tokenStorageService.saveToken(authToken, false);
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.tokenStorageService.signOut();
|
this.tokenStorageService.signOut();
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
<mat-slide-toggle
|
<mat-slide-toggle
|
||||||
#toggleSignInWithFingerprintEnabledElement
|
#toggleSignInWithFingerprintEnabledElement
|
||||||
color="primary"
|
color="primary"
|
||||||
|
[disabled]="!hasPermissionToUpdateUserSettings"
|
||||||
(change)="onSignInWithFingerprintChange($event)"
|
(change)="onSignInWithFingerprintChange($event)"
|
||||||
></mat-slide-toggle>
|
></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import {
|
||||||
|
STAY_SIGNED_IN,
|
||||||
|
SettingsStorageService
|
||||||
|
} from '@ghostfolio/client/services/settings-storage.service';
|
||||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -14,6 +18,7 @@ export class AuthPageComponent implements OnInit {
|
|||||||
public constructor(
|
public constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private settingsStorageService: SettingsStorageService,
|
||||||
private tokenStorageService: TokenStorageService
|
private tokenStorageService: TokenStorageService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -23,7 +28,10 @@ export class AuthPageComponent implements OnInit {
|
|||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.route.params.subscribe((params) => {
|
this.route.params.subscribe((params) => {
|
||||||
const jwt = params['jwt'];
|
const jwt = params['jwt'];
|
||||||
this.tokenStorageService.saveToken(jwt);
|
this.tokenStorageService.saveToken(
|
||||||
|
jwt,
|
||||||
|
this.settingsStorageService.getSetting(STAY_SIGNED_IN) === 'true'
|
||||||
|
);
|
||||||
|
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
});
|
});
|
||||||
|
@ -26,8 +26,7 @@ export class LandingPageComponent implements OnDestroy, OnInit {
|
|||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private tokenStorageService: TokenStorageService,
|
private tokenStorageService: TokenStorageService
|
||||||
private webAuthnService: WebAuthnService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -257,7 +256,7 @@ export class LandingPageComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setToken(aToken: string) {
|
public setToken(aToken: string) {
|
||||||
this.tokenStorageService.saveToken(aToken);
|
this.tokenStorageService.saveToken(aToken, true);
|
||||||
|
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
@ -78,19 +78,13 @@ export class RegisterPageComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data) => {
|
dialogRef.afterClosed().subscribe((data) => {
|
||||||
if (data?.authToken) {
|
if (data?.authToken) {
|
||||||
this.tokenStorageService.saveToken(authToken);
|
this.tokenStorageService.saveToken(authToken, true);
|
||||||
|
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setToken(aToken: string) {
|
|
||||||
this.tokenStorageService.saveToken(aToken);
|
|
||||||
|
|
||||||
this.router.navigate(['/']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component';
|
||||||
|
|
||||||
|
const routes: Routes = [{ path: '', component: WebauthnPageComponent }];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class WebauthnPageRoutingModule {}
|
@ -0,0 +1,46 @@
|
|||||||
|
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||||
|
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'gf-webauthn-page',
|
||||||
|
templateUrl: './webauthn-page.html',
|
||||||
|
styleUrls: ['./webauthn-page.scss']
|
||||||
|
})
|
||||||
|
export class WebauthnPageComponent implements OnInit {
|
||||||
|
public hasError = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private router: Router,
|
||||||
|
private tokenStorageService: TokenStorageService,
|
||||||
|
private webAuthnService: WebAuthnService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.signIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public deregisterDevice() {
|
||||||
|
this.webAuthnService.deregister().subscribe(() => {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public signIn() {
|
||||||
|
this.hasError = false;
|
||||||
|
|
||||||
|
this.webAuthnService.login().subscribe(
|
||||||
|
({ authToken }) => {
|
||||||
|
this.tokenStorageService.saveToken(authToken, false);
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error(error);
|
||||||
|
this.hasError = true;
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
34
apps/client/src/app/pages/webauthn/webauthn-page.html
Normal file
34
apps/client/src/app/pages/webauthn/webauthn-page.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div
|
||||||
|
class="align-items-center d-flex flex-column justify-content-center mb-4 w-100"
|
||||||
|
>
|
||||||
|
<gf-logo size="medium"></gf-logo>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!hasError" class="col d-flex justify-content-center">
|
||||||
|
<mat-spinner [diameter]="20"></mat-spinner>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
*ngIf="hasError"
|
||||||
|
class="align-items-center col d-flex flex-column justify-content-center"
|
||||||
|
>
|
||||||
|
<h1 class="d-flex h5 justify-content-center mb-0 text-center" i18n>
|
||||||
|
Oops, authentication has failed.
|
||||||
|
</h1>
|
||||||
|
<button
|
||||||
|
class="mb-3 mt-4"
|
||||||
|
color="primary"
|
||||||
|
i18n
|
||||||
|
mat-flat-button
|
||||||
|
(click)="signIn()"
|
||||||
|
>
|
||||||
|
Try again
|
||||||
|
</button>
|
||||||
|
<div class="text-muted" i18n>or</div>
|
||||||
|
<button class="mt-1" i18n mat-flat-button (click)="deregisterDevice()">
|
||||||
|
Go back to Home Page
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
22
apps/client/src/app/pages/webauthn/webauthn-page.module.ts
Normal file
22
apps/client/src/app/pages/webauthn/webauthn-page.module.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { GfLogoModule } from '@ghostfolio/client/components/logo/logo.module';
|
||||||
|
import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component';
|
||||||
|
|
||||||
|
import { WebauthnPageRoutingModule } from './webauthn-page-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [WebauthnPageComponent],
|
||||||
|
exports: [],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
GfLogoModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
WebauthnPageRoutingModule
|
||||||
|
],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
export class WebauthnPageModule {}
|
@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
export const RANGE = 'range';
|
export const RANGE = 'range';
|
||||||
|
export const STAY_SIGNED_IN = 'staySignedIn';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -21,7 +21,7 @@ export class TokenStorageService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveToken(token: string, staySignedIn: boolean = false): void {
|
public saveToken(token: string, staySignedIn = false): void {
|
||||||
if (staySignedIn) {
|
if (staySignedIn) {
|
||||||
window.localStorage.setItem(TOKEN_KEY, token);
|
window.localStorage.setItem(TOKEN_KEY, token);
|
||||||
}
|
}
|
||||||
|
@ -15,4 +15,4 @@ export const environment = {
|
|||||||
* This import should be commented out in production mode because it will have a negative impact
|
* This import should be commented out in production mode because it will have a negative impact
|
||||||
* on performance if an error is thrown.
|
* on performance if an error is thrown.
|
||||||
*/
|
*/
|
||||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* Zone JS is required by default for Angular itself.
|
* Zone JS is required by default for Angular itself.
|
||||||
*/
|
*/
|
||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
import 'zone.js'; // Included with Angular CLI.
|
||||||
|
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* APPLICATION IMPORTS
|
* APPLICATION IMPORTS
|
||||||
|
@ -1 +1 @@
|
|||||||
import 'jest-preset-angular';
|
import 'jest-preset-angular/setup-jest';
|
||||||
|
@ -2,9 +2,7 @@ module.exports = {
|
|||||||
displayName: 'common',
|
displayName: 'common',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
globals: {
|
globals: {
|
||||||
'ts-jest': {
|
'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' }
|
||||||
tsConfig: '<rootDir>/tsconfig.spec.json'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.[tj]sx?$': 'ts-jest'
|
'^.+\\.[tj]sx?$': 'ts-jest'
|
||||||
|
25
nx.json
25
nx.json
@ -1,26 +1,39 @@
|
|||||||
{
|
{
|
||||||
"implicitDependencies": {
|
"implicitDependencies": {
|
||||||
"angular.json": "*",
|
"angular.json": "*",
|
||||||
"package.json": { "dependencies": "*", "devDependencies": "*" },
|
"package.json": {
|
||||||
|
"dependencies": "*",
|
||||||
|
"devDependencies": "*"
|
||||||
|
},
|
||||||
"tsconfig.base.json": "*",
|
"tsconfig.base.json": "*",
|
||||||
".eslintrc.json": "*",
|
".eslintrc.json": "*",
|
||||||
"nx.json": "*"
|
"nx.json": "*"
|
||||||
},
|
},
|
||||||
"affected": { "defaultBase": "origin/main" },
|
"affected": {
|
||||||
|
"defaultBase": "origin/main"
|
||||||
|
},
|
||||||
"npmScope": "ghostfolio",
|
"npmScope": "ghostfolio",
|
||||||
"tasksRunnerOptions": {
|
"tasksRunnerOptions": {
|
||||||
"default": {
|
"default": {
|
||||||
"runner": "@nrwl/workspace/tasks-runners/default",
|
"runner": "@nrwl/workspace/tasks-runners/default",
|
||||||
"options": { "cacheableOperations": ["build", "lint", "test", "e2e"] }
|
"options": {
|
||||||
|
"cacheableOperations": ["build", "lint", "test", "e2e"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
"api": { "tags": [] },
|
"api": {
|
||||||
"client": { "tags": [] },
|
"tags": []
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
"client-e2e": {
|
"client-e2e": {
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"implicitDependencies": ["client"]
|
"implicitDependencies": ["client"]
|
||||||
},
|
},
|
||||||
"common": { "tags": [] }
|
"common": {
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
package.json
78
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "1.15.0",
|
"version": "1.17.0",
|
||||||
"homepage": "https://ghostfol.io",
|
"homepage": "https://ghostfol.io",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -45,16 +45,16 @@
|
|||||||
"workspace-generator": "nx workspace-generator"
|
"workspace-generator": "nx workspace-generator"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "11.2.4",
|
"@angular/animations": "12.0.4",
|
||||||
"@angular/cdk": "11.0.4",
|
"@angular/cdk": "11.0.4",
|
||||||
"@angular/common": "11.2.4",
|
"@angular/common": "12.0.4",
|
||||||
"@angular/compiler": "11.2.4",
|
"@angular/compiler": "12.0.4",
|
||||||
"@angular/core": "11.2.4",
|
"@angular/core": "12.0.4",
|
||||||
"@angular/forms": "11.2.4",
|
"@angular/forms": "12.0.4",
|
||||||
"@angular/material": "11.0.4",
|
"@angular/material": "11.0.4",
|
||||||
"@angular/platform-browser": "11.2.4",
|
"@angular/platform-browser": "12.0.4",
|
||||||
"@angular/platform-browser-dynamic": "11.2.4",
|
"@angular/platform-browser-dynamic": "12.0.4",
|
||||||
"@angular/router": "11.2.4",
|
"@angular/router": "12.0.4",
|
||||||
"@codewithdan/observable-store": "2.2.11",
|
"@codewithdan/observable-store": "2.2.11",
|
||||||
"@nestjs/common": "7.6.5",
|
"@nestjs/common": "7.6.5",
|
||||||
"@nestjs/config": "0.6.1",
|
"@nestjs/config": "0.6.1",
|
||||||
@ -64,33 +64,33 @@
|
|||||||
"@nestjs/platform-express": "7.6.5",
|
"@nestjs/platform-express": "7.6.5",
|
||||||
"@nestjs/schedule": "0.4.1",
|
"@nestjs/schedule": "0.4.1",
|
||||||
"@nestjs/serve-static": "2.1.4",
|
"@nestjs/serve-static": "2.1.4",
|
||||||
"@nrwl/angular": "12.0.0",
|
"@nrwl/angular": "12.3.6",
|
||||||
"@prisma/client": "2.24.1",
|
"@prisma/client": "2.24.1",
|
||||||
"@simplewebauthn/browser": "3.0.0",
|
"@simplewebauthn/browser": "3.0.0",
|
||||||
"@simplewebauthn/server": "3.0.0",
|
"@simplewebauthn/server": "3.0.0",
|
||||||
"@simplewebauthn/typescript-types": "3.0.0",
|
"@simplewebauthn/typescript-types": "3.0.0",
|
||||||
"@types/lodash": "4.14.168",
|
"@types/lodash": "4.14.168",
|
||||||
"alphavantage": "2.2.0",
|
"alphavantage": "2.2.0",
|
||||||
"angular-material-css-vars": "1.1.2",
|
"angular-material-css-vars": "1.2.0",
|
||||||
"bent": "7.3.12",
|
"bent": "7.3.12",
|
||||||
"bootstrap": "4.6.0",
|
"bootstrap": "4.6.0",
|
||||||
"cache-manager": "3.4.3",
|
"cache-manager": "3.4.3",
|
||||||
"cache-manager-redis-store": "2.0.0",
|
"cache-manager-redis-store": "2.0.0",
|
||||||
"chart.js": "3.2.1",
|
"chart.js": "3.3.2",
|
||||||
"chartjs-adapter-date-fns": "1.1.0-beta.1",
|
"chartjs-adapter-date-fns": "2.0.0",
|
||||||
"cheerio": "1.0.0-rc.6",
|
"cheerio": "1.0.0-rc.6",
|
||||||
"class-transformer": "0.3.2",
|
"class-transformer": "0.3.2",
|
||||||
"class-validator": "0.13.1",
|
"class-validator": "0.13.1",
|
||||||
"countries-list": "2.6.1",
|
"countries-list": "2.6.1",
|
||||||
"countup.js": "2.0.7",
|
"countup.js": "2.0.7",
|
||||||
"cryptocurrencies": "7.0.0",
|
"cryptocurrencies": "7.0.0",
|
||||||
"date-fns": "2.19.0",
|
"date-fns": "2.22.1",
|
||||||
"envalid": "7.1.0",
|
"envalid": "7.1.0",
|
||||||
"http-status-codes": "2.1.4",
|
"http-status-codes": "2.1.4",
|
||||||
"ionicons": "5.5.1",
|
"ionicons": "5.5.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"ngx-device-detector": "2.0.6",
|
"ngx-device-detector": "2.1.1",
|
||||||
"ngx-markdown": "11.1.2",
|
"ngx-markdown": "12.0.1",
|
||||||
"ngx-skeleton-loader": "2.9.1",
|
"ngx-skeleton-loader": "2.9.1",
|
||||||
"passport": "0.4.1",
|
"passport": "0.4.1",
|
||||||
"passport-google-oauth20": "2.0.0",
|
"passport-google-oauth20": "2.0.0",
|
||||||
@ -102,47 +102,47 @@
|
|||||||
"svgmap": "2.1.1",
|
"svgmap": "2.1.1",
|
||||||
"uuid": "8.3.2",
|
"uuid": "8.3.2",
|
||||||
"yahoo-finance": "0.3.6",
|
"yahoo-finance": "0.3.6",
|
||||||
"zone.js": "0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "0.1102.3",
|
"@angular-devkit/build-angular": "12.0.4",
|
||||||
"@angular-eslint/eslint-plugin": "2.0.2",
|
"@angular-eslint/eslint-plugin": "12.0.0",
|
||||||
"@angular/cli": "11.2.3",
|
"@angular/cli": "12.0.4",
|
||||||
"@angular/compiler-cli": "11.2.4",
|
"@angular/compiler-cli": "12.0.4",
|
||||||
"@angular/language-service": "11.2.4",
|
"@angular/language-service": "12.0.4",
|
||||||
"@angular/localize": "11.0.9",
|
"@angular/localize": "11.0.9",
|
||||||
"@nestjs/schematics": "7.2.6",
|
"@nestjs/schematics": "7.2.6",
|
||||||
"@nestjs/testing": "7.6.5",
|
"@nestjs/testing": "7.6.5",
|
||||||
"@nrwl/cli": "12.0.0",
|
"@nrwl/cli": "12.3.6",
|
||||||
"@nrwl/cypress": "12.0.0",
|
"@nrwl/cypress": "12.3.6",
|
||||||
"@nrwl/eslint-plugin-nx": "12.0.0",
|
"@nrwl/eslint-plugin-nx": "12.3.6",
|
||||||
"@nrwl/jest": "12.0.0",
|
"@nrwl/jest": "12.3.6",
|
||||||
"@nrwl/nest": "12.0.0",
|
"@nrwl/nest": "12.3.6",
|
||||||
"@nrwl/node": "12.0.0",
|
"@nrwl/node": "12.3.6",
|
||||||
"@nrwl/tao": "12.0.0",
|
"@nrwl/tao": "12.3.6",
|
||||||
"@nrwl/workspace": "12.0.0",
|
"@nrwl/workspace": "12.3.6",
|
||||||
"@types/cache-manager": "3.4.0",
|
"@types/cache-manager": "3.4.0",
|
||||||
"@types/jest": "26.0.20",
|
"@types/jest": "26.0.20",
|
||||||
"@types/node": "14.14.33",
|
"@types/node": "14.14.33",
|
||||||
"@types/passport-google-oauth20": "2.0.6",
|
"@types/passport-google-oauth20": "2.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "4.19.0",
|
"@typescript-eslint/eslint-plugin": "4.27.0",
|
||||||
"@typescript-eslint/parser": "4.19.0",
|
"@typescript-eslint/parser": "4.27.0",
|
||||||
"codelyzer": "6.0.1",
|
"codelyzer": "6.0.1",
|
||||||
"cypress": "6.2.1",
|
"cypress": "6.2.1",
|
||||||
"eslint": "7.22.0",
|
"eslint": "7.28.0",
|
||||||
"eslint-config-prettier": "8.1.0",
|
"eslint-config-prettier": "8.3.0",
|
||||||
"eslint-plugin-import": "latest",
|
"eslint-plugin-import": "2.23.4",
|
||||||
"import-sort-cli": "6.0.0",
|
"import-sort-cli": "6.0.0",
|
||||||
"import-sort-parser-typescript": "6.0.0",
|
"import-sort-parser-typescript": "6.0.0",
|
||||||
"import-sort-style-module": "6.0.0",
|
"import-sort-style-module": "6.0.0",
|
||||||
"jest": "26.6.3",
|
"jest": "26.6.3",
|
||||||
"jest-preset-angular": "8.3.2",
|
"jest-preset-angular": "8.4.0",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.3.1",
|
||||||
"replace-in-file": "6.2.0",
|
"replace-in-file": "6.2.0",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"ts-jest": "26.4.4",
|
"ts-jest": "26.5.5",
|
||||||
"ts-node": "9.1.1",
|
"ts-node": "9.1.1",
|
||||||
"typescript": "4.1.4"
|
"typescript": "4.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14.x"
|
"node": "14.x"
|
||||||
|
Reference in New Issue
Block a user