Переглянути джерело

session tidy up

tags/2.037
Patrick Sun 4 роки тому
джерело
коміт
05722d02c9
25 змінених файлів з 367 додано та 303 видалено
  1. +1
    -1
      proxy.conf.json
  2. +4
    -1
      src/app/app.component.scss
  3. +6
    -3
      src/app/app.component.ts
  4. +2
    -0
      src/app/app.config.ts
  5. +31
    -11
      src/app/auth/auth-http-interceptor.service.ts
  6. +2
    -4
      src/app/auth/auth.component.ts
  7. +0
    -27
      src/app/auth/dist/auth.component.css
  8. +0
    -61
      src/app/auth/dist/auth.component.js
  9. +3
    -0
      src/app/chart-type-of-loans/chart-type-of-loans.component.html
  10. +30
    -4
      src/app/chart-type-of-loans/chart-type-of-loans.component.ts
  11. +19
    -0
      src/app/helper.function.ts
  12. +2
    -2
      src/app/image-popup-dialog/image-popup-dialog.component.ts
  13. +0
    -4
      src/app/list-all-loans/list-all-loans.component.ts
  14. +15
    -30
      src/app/models/api-v1-login-response.ts
  15. +34
    -0
      src/app/models/payout.ex.model.ts
  16. +0
    -29
      src/app/models/payout.model.ts
  17. +19
    -19
      src/app/models/reward-ex.model.ts
  18. +2
    -2
      src/app/models/userExModel.ts
  19. +5
    -5
      src/app/pay-out-details/pay-out-details.component.ts
  20. +14
    -53
      src/app/service/auth.service.ts
  21. +19
    -0
      src/app/service/payout.service.ts
  22. +108
    -31
      src/app/service/session.service.ts
  23. +12
    -2
      src/app/top-bar/top-bar.component.ts
  24. +38
    -13
      src/app/websocket.ts
  25. +1
    -1
      tsconfig.json

+ 1
- 1
proxy.conf.json Переглянути файл

{ {
"/api/v1": { "/api/v1": {
"target": "http://svr2021:8080",
"target": "https://svr2021.lawipac.com:8080",
"secure": false, "secure": false,
"pathRewrite": {"^/api/v1" : "/api/v1"} "pathRewrite": {"^/api/v1" : "/api/v1"}
} }

+ 4
- 1
src/app/app.component.scss Переглянути файл



.appbar, .appbar,
.k-appbar-sticky{ .k-appbar-sticky{
z-index: 1000;
/* z-index: 1000; */
}
.k-notification-group{
z-index:1000;
} }


#topBar .main-menu-item , #topBar .k-icon, #topBar .k-menu-item { #topBar .main-menu-item , #topBar .k-icon, #topBar .k-menu-item {

+ 6
- 3
src/app/app.component.ts Переглянути файл

import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import {WsLoginEventModel} from './models/websocket/ws.login.event.model'; import {WsLoginEventModel} from './models/websocket/ws.login.event.model';
import {SessionService} from './service/session.service'; import {SessionService} from './service/session.service';
import {AppConfig} from './app.config';


@Component({ @Component({
selector: 'app-root', selector: 'app-root',
// tslint:disable-next-line:typedef // tslint:disable-next-line:typedef
ngOnInit() { ngOnInit() {
this.listenToMenuEvent(); this.listenToMenuEvent();
// must be the last, as it may emit events
this.authService.AutoLogin();
} }


listenToMenuEvent(): void { listenToMenuEvent(): void {


onWSLogin(e: WsLoginEventModel): void { onWSLogin(e: WsLoginEventModel): void {
if ( e.T === 'logout' ) { // regardless where are they, logout means logout if ( e.T === 'logout' ) { // regardless where are they, logout means logout
if ( this.ss.isCurrentUser(e.Uid) ) {
if ( this.ss.isLoggedIn() && this.ss.isCurrentUser(e.Uid) ) {
if ( e.Sid === this.ss.SessionId ) {
this.ss.logoutAndClearLocalStorage(); this.ss.logoutAndClearLocalStorage();
}else{
this.ss.logout();
}
} }
}else{ // some one logged in from another browser tab }else{ // some one logged in from another browser tab
if ( e.Mid === this.ss.loggedIn.machineId) { // same browser if ( e.Mid === this.ss.loggedIn.machineId) { // same browser

+ 2
- 0
src/app/app.config.ts Переглянути файл

try { try {
if ( location.href.includes('//localhost:4200/') ) { if ( location.href.includes('//localhost:4200/') ) {
AppConfig.config = AppConfig.debugConfig; AppConfig.config = AppConfig.debugConfig;
// AppConfig.config.Server = '/api/v1/';
console.log('Using Debug Config:', AppConfig.config); console.log('Using Debug Config:', AppConfig.config);
}else{ }else{
const json = this.decode(el.innerText); const json = this.decode(el.innerText);
AppConfig.config = JSON.parse(json); AppConfig.config = JSON.parse(json);
// AppConfig.config.Server = '/api/v1/';
console.log('Using Production Config:', AppConfig.config); console.log('Using Production Config:', AppConfig.config);
} }
resolve(); resolve();

+ 31
- 11
src/app/auth/auth-http-interceptor.service.ts Переглянути файл

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let h = req.headers; let h = req.headers;


if (this.ss.loggedIn.hasValidSession()) {
if (this.ss.SessionId !== '') {
h = h.set('Biukop-Session', this.ss.loggedIn.session); h = h.set('Biukop-Session', this.ss.loggedIn.session);
} }

if (this.ss.loggedIn.hasValidMachineId()) {
h = h.set('Biukop-Mid', this.ss.loggedIn.machineId);
if (this.ss.MachineId !== '') {
h = h.set('Biukop-Mid', this.ss.MachineId);
} }


const authReq = req.clone({ const authReq = req.clone({
headers: h, headers: h,
withCredentials: true withCredentials: true
}); });

const self = this;
return next.handle(authReq).pipe( return next.handle(authReq).pipe(
tap(event => { tap(event => {
// console.log(event); // console.log(event);
if (event.type === HttpEventType.Response){ if (event.type === HttpEventType.Response){
const bs = event.headers.get('biukop-session'); const bs = event.headers.get('biukop-session');
if (bs !== undefined){
if ( this.ss.loggedIn.session !== bs ){
this.ss.loggedIn.session = bs;
this.ss.saveSessionInfo();
console.log('switch session:' , bs);
}
}
self.setSession(bs);
const mid = event.headers.get('biukop-mid');
self.setMachineId(mid);
} }
}) })
); );
} }

public setSession(bs: string): void {
// console.log('receive session:' , bs);
if (bs){
if ( this.ss.loggedIn.session !== bs ){
this.ss.loggedIn.session = bs;
this.ss.saveSessionInfo();
console.log('switch session:' , bs);
}
}
}

public setMachineId(mid: string): void{
// console.log('receive mid:' , mid);
if ( mid && mid !== '' ){
if ( this.ss.MachineId !== mid ){
this.ss.MachineId = mid;
console.log('switch machine id:' , mid);
}
}
}


} }

+ 2
- 4
src/app/auth/auth.component.ts Переглянути файл

private router: Router, private notificationService: NotificationService) { } private router: Router, private notificationService: NotificationService) { }


ngOnInit(): void { ngOnInit(): void {
if ( this.ss.loggedIn.login ){
this.ss.logout();
}
this.ss.logout();


// this.ss.logoutAndClearLocalStorage(); // this.ss.logoutAndClearLocalStorage();
this.loginSub = this.ss.loginSuccess.subscribe(
this.loginSub = this.ss.loginResult.subscribe(
responseData => { responseData => {
// console.log(responseData); // console.log(responseData);
this.onLogin(responseData); this.onLogin(responseData);

+ 0
- 27
src/app/auth/dist/auth.component.css Переглянути файл

.k-form.none {
width: 400px;
}

div.parent {
display: table;
width: 100%;
height: 100vh;
opacity: 0.95;
background: url("../../assets/img/body_bg.jpg") no-repeat center center fixed;
background-size: cover;
}

.form_login {
margin-left: auto;
margin-right: auto;
margin-top: 10%;
max-width: 400px;
padding-top: 20px;
text-align: center;
vertical-align: middle;
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
box-shadow: 0 0 6px black;
border-radius: 5px;
}

+ 0
- 61
src/app/auth/dist/auth.component.js Переглянути файл

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
exports.__esModule = true;
exports.AuthComponent = void 0;
var core_1 = require("@angular/core");
var forms_1 = require("@angular/forms");
var AuthComponent = /** @class */ (function () {
function AuthComponent(authService, rounter, notificationService) {
this.authService = authService;
this.rounter = rounter;
this.notificationService = notificationService;
this.userForm = new forms_1.FormGroup({
password: new forms_1.FormControl('password', [forms_1.Validators.minLength(3), forms_1.Validators.maxLength(20)]),
email: new forms_1.FormControl('email@email.com', forms_1.Validators.email)
});
}
AuthComponent.prototype.ngOnInit = function () {
var _this = this;
this.loginSub = this.authService.loginSuccess.subscribe(function (ok) { _this.onLogin(ok); });
};
AuthComponent.prototype.ngOnDestroy = function () {
this.loginSub.unsubscribe();
};
AuthComponent.prototype.submitForm = function () {
console.log(this.userForm);
this.loading = true;
this.authService.login(this.userForm.value.email, this.userForm.value.password);
};
AuthComponent.prototype.onLogin = function (ok) {
this.loading = false;
console.log(" found login success " + ok);
if (ok)
this.rounter.navigate(["/dashboard"]);
else
this.show();
};
AuthComponent.prototype.show = function () {
this.notificationService.show({
content: 'Your loggin is failed, please try again.',
cssClass: 'button-notification',
animation: { type: 'slide', duration: 400 },
position: { horizontal: 'center', vertical: 'top' },
type: { style: 'error', icon: true },
hideAfter: 2000
});
};
AuthComponent = __decorate([
core_1.Component({
selector: 'app-auth',
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss']
})
], AuthComponent);
return AuthComponent;
}());
exports.AuthComponent = AuthComponent;

+ 3
- 0
src/app/chart-type-of-loans/chart-type-of-loans.component.html Переглянути файл



<kendo-chart [transitions]="true"> <kendo-chart [transitions]="true">
<ng-template kendoChartDonutCenterTemplate>
<h3>{{totalNumberOfLoans}}</h3>
</ng-template>
<kendo-chart-title text="Stages of Loans"></kendo-chart-title> <kendo-chart-title text="Stages of Loans"></kendo-chart-title>
<kendo-chart-series> <kendo-chart-series>
<kendo-chart-series-item <kendo-chart-series-item

+ 30
- 4
src/app/chart-type-of-loans/chart-type-of-loans.component.ts Переглянути файл

import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {Observable} from 'rxjs';
import {Observable, Subscriber} from 'rxjs';
import {TypeOfLoansModel} from '../models/type-of-loans.model'; import {TypeOfLoansModel} from '../models/type-of-loans.model';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {AuthService} from '../service/auth.service'; import {AuthService} from '../service/auth.service';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import {equalsArray, randomNumber} from '../helper.function';


@Component({ @Component({
selector: 'app-chart-type-of-loans', selector: 'app-chart-type-of-loans',
}) })
export class ChartTypeOfLoansComponent implements OnInit { export class ChartTypeOfLoansComponent implements OnInit {
public autoFit = true; public autoFit = true;
// public data: Observable<TypeOfLoansModel[]>;
public data: Observable<TypeOfLoansModel[]>; public data: Observable<TypeOfLoansModel[]>;


public totalNumberOfLoans = 0;

constructor(private auth: AuthService, private http: HttpClient) { } constructor(private auth: AuthService, private http: HttpClient) { }


private previous: TypeOfLoansModel[] = [];
ngOnInit(): void { ngOnInit(): void {
this.data = this.getTypeOfLoans();
this.data = new Observable<TypeOfLoansModel[]>( (observer) => {
this.updateTypeOfLoans(observer);
});
} }


public labelContent(e: any): string { public labelContent(e: any): string {
return e.category;
return e.dataItem.Count + ' ' + e.category + ' (' + + (e.dataItem.Share * 100).toFixed(2) + '%' + ')' ;
}

public updateTypeOfLoans(observer: Subscriber<TypeOfLoansModel[]>): void {
this.getTypeOfLoans().subscribe(
resp => {
if ( ! equalsArray(this.previous, resp ) ){
this.previous = resp;
this.totalNumberOfLoans = 0;
resp.reduce( (acc, curr) => {
this.totalNumberOfLoans += curr.Count;
return acc;
});
observer.next(resp);
}
});
const refresh = randomNumber(3000, 6000);
setTimeout( () => {
// this.updateTypeOfLoans(observer);
}, refresh);
} }


public getTypeOfLoans(): Observable<TypeOfLoansModel[]> { public getTypeOfLoans(): Observable<TypeOfLoansModel[]> {
if (Array.isArray(input)) { if (Array.isArray(input)) {
input.forEach( (value, index) =>{ input.forEach( (value, index) =>{
ret.push({ ret.push({
Kind: value.Kind + ' ' + (value.Share * 100).toFixed(2) + '%',
Kind: value.Kind,
Count: value.Count, Count: value.Count,
Share: value.Share, Share: value.Share,
Amount: value.Amount Amount: value.Amount

+ 19
- 0
src/app/helper.function.ts Переглянути файл



export const equalsArray = (a, b) => {
if (a === b) { return true; }
if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) {
return a === b;
}
if (a.prototype !== b.prototype) { return false; }
const keys = Object.keys(a);
if (keys.length !== Object.keys(b).length) { return false; }
return keys.every(k => equalsArray(a[k], b[k]));
};

export function randomNumber(min: number, max: number): number { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}

+ 2
- 2
src/app/image-popup-dialog/image-popup-dialog.component.ts Переглянути файл

this.dialogWidth = this.scrWidth; this.dialogWidth = this.scrWidth;
} }


console.log(this.img.nativeElement.naturalWidth, this.img.nativeElement.naturalHeight,
this.img.nativeElement.width, this.img.nativeElement.height, this.iWidth, this.imgWidthPercent);
// console.log(this.img.nativeElement.naturalWidth, this.img.nativeElement.naturalHeight,
// this.img.nativeElement.width, this.img.nativeElement.height, this.iWidth, this.imgWidthPercent);


} }



+ 0
- 4
src/app/list-all-loans/list-all-loans.component.ts Переглянути файл

return 'url("' + url + '")'; return 'url("' + url + '")';
} }


public allowEdit(): boolean {
return this.auth.allowEditLoan();
}

public allData = (): Observable<GridDataResult> => { public allData = (): Observable<GridDataResult> => {
return this.service.queryAll({skip: 0, take: 999999, sort: this.sort, filter: this.filter}); return this.service.queryAll({skip: 0, take: 999999, sort: this.sort, filter: this.filter});
} }

+ 15
- 30
src/app/models/api-v1-login-response.ts Переглянути файл



// tslint:disable-next-line:class-name
import {PeopleModel} from './people.model'; import {PeopleModel} from './people.model';
import {UserExtraModel} from './user-extra.model'; import {UserExtraModel} from './user-extra.model';


export class ApiV1LoginResponse { export class ApiV1LoginResponse {


constructor(
public login: boolean,
public machineId: string,
public session: string,
public sessionExpire: number, // unix timestamp
public role: string,
public User: PeopleModel,
public UserExtra?: UserExtraModel // extra user information
) {
this.login = login;
this.machineId = machineId;
this.session = session;
this.sessionExpire = sessionExpire;
this.role = role;
this.User = User;
this.UserExtra = UserExtra;
}
public login: boolean;
public machineId: string;
public session: string;
public sessionExpire: number; // unix timestamp
public role: string;
public User: PeopleModel;
public UserExtra?: UserExtraModel; // extra user information


public static EmptyNew(): ApiV1LoginResponse{
return new ApiV1LoginResponse(
false, '', '', 0, '', PeopleModel.EmptyNew() );
constructor(payload: Partial<ApiV1LoginResponse>) {
this.login = payload.login || false;
this.machineId = payload.machineId || '';
this.session = payload.session || '' ;
this.sessionExpire = payload.sessionExpire || new Date().getTime();
this.role = payload.role || '';
this.User = new PeopleModel(payload.User || {});
this.UserExtra = new UserExtraModel(payload.UserExtra || {});
} }


public hasValidSession(): boolean { public hasValidSession(): boolean {
} }
} }


public hasValidMachineId(): boolean {
if (this.machineId === undefined || this.machineId === '') {
return false;
}else{
return true;
}
}
} }

+ 34
- 0
src/app/models/payout.ex.model.ts Переглянути файл

import {UserExModel} from './userExModel';

export class PayOutExModel{
Id: number;
Ts: Date;
To: UserExModel;
Prepared: UserExModel;
Approved: UserExModel;
Paid: UserExModel;
PayDate: Date;
PayAmount: number;
Status: string;
constructor( payload: Partial<PayOutExModel>) {
this.Id = payload.Id || 0;
if (payload.Ts){
this.Ts = new Date( payload.Ts);
}else{
this.Ts = new Date('1800-01-01');
}

this.To = new UserExModel(payload.To);
this.Prepared = new UserExModel(payload.Prepared);
this.Approved = new UserExModel(payload.Approved);
this.Paid = new UserExModel(payload.Paid);

if (payload.PayDate) {
this.PayDate = new Date(payload.PayDate);
}else{
this.PayDate = new Date('1800-01-01');
}
this.PayAmount = payload.PayAmount || 0;
this.Status = payload.Status || '';
}
}

+ 0
- 29
src/app/models/payout.model.ts Переглянути файл

import { PayoutAudit } from "./payout-audit.model";

export class PayOutModel{
id: string;

//parent
loan: string

//parties
id_user: string
role: string;

//
amount: number;

//paid
paid: boolean;
paid_date: Date;
paid_to: string; //bank details;

//confirmed, by receiptient
confirmed: boolean;

//source
source?: string;

//audit
audits: PayoutAudit[];
}

+ 19
- 19
src/app/models/reward-ex.model.ts Переглянути файл

import {LoanModel} from './loan.model'; import {LoanModel} from './loan.model';
import {PayOutModel} from './payout.model';
import {PayOutModel} from './payout.ex.model';
import {PeopleModel} from './people.model'; import {PeopleModel} from './people.model';
import {UserExModel} from './userExModel';


export class RewardExModel { export class RewardExModel {
public Id: number; public Id: number;
public FromId: string; public FromId: string;
public PayOutId: number; public PayOutId: number;


public To?: PeopleModel;
public To?: UserExModel;
public Loan?: LoanModel; public Loan?: LoanModel;
public From?: PeopleModel;
public From?: UserExModel;
public PayOut?: PayOutModel; public PayOut?: PayOutModel;


constructor(payload: Partial<RewardExModel>) { constructor(payload: Partial<RewardExModel>) {
this.FromId = payload.FromId || ''; this.FromId = payload.FromId || '';
this.PayOutId = payload.PayOutId || 0; this.PayOutId = payload.PayOutId || 0;


// TODO: must make reward Ex workable
// if (payload.To) {
// this.To = new PeopleModel(payload.To);
// }
//
// if (payload.Loan) {
// this.Loan = new LoanModel(payload.Loan);
// }
//
// if ( payload.From) {
// this.From = new PeopleModel(payload.From);
// }
//
// if ( payload. PayOut ) {
// this.PayOut = new PayOutModel(payload.PayOut);
// }
if (payload.To) {
this.To = new UserExModel(payload.To);
}

if (payload.Loan) {
this.Loan = new LoanModel(payload.Loan);
}

if ( payload.From) {
this.From = new UserExModel(payload.From);
}

if ( payload. PayOut ) {
this.PayOut = new PayOutModel(payload.PayOut);
}


} }
} }

src/app/models/user.model.ts → src/app/models/userExModel.ts Переглянути файл

Super = 'super', Super = 'super',
} }


export class UserModel extends PeopleModel{
export class UserExModel extends PeopleModel{
Role: UserRoles; Role: UserRoles;
Broker?: BrokerModel; Broker?: BrokerModel;
Login: string; Login: string;
constructor(payload: Partial<UserModel>) {
constructor(payload: Partial<UserExModel>) {
super(payload); super(payload);
this.Role = payload.Role || 'People' as UserRoles; this.Role = payload.Role || 'People' as UserRoles;
this.Login = payload.Login || ''; this.Login = payload.Login || '';

+ 5
- 5
src/app/pay-out-details/pay-out-details.component.ts Переглянути файл

export class PayOutDetailsComponent implements OnInit { export class PayOutDetailsComponent implements OnInit {
sales = []; sales = [];


background = "url('https://svr2021.lawipac.com:8080/api/v1/avatar/0')";
background = 'url(\'https://svr2021.lawipac.com:8080/api/v1/avatar/0\')';
constructor() { } constructor() { }


ngOnInit(): void { ngOnInit(): void {
this.sales = this.range(1, 20);
console.log(this.sales);
} }


range(start, end) {
if(start === end) return [start];
range(start: number, end: number): number[] {
if (start === end) { return [start]; }
return [start, ...this.range(start + 1, end)]; return [start, ...this.range(start + 1, end)];
} }



+ 14
- 53
src/app/service/auth.service.ts Переглянути файл

private ss: SessionService, private ss: SessionService,
private config: AppConfig) { } private config: AppConfig) { }


public AutoLogin(): void {
const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey));
if (!sfm) {
console.log('no auto login');
return;
}
this.ss.loggedIn = new ApiV1LoginResponse(
sfm.login,
sfm.machineId,
sfm.session,
sfm.sessionExpire,
sfm.role,
new PeopleModel(sfm.User)
);

if ( sfm.UserExtra !== undefined ) {
this.ss.loggedIn.UserExtra = new UserExtraModel(sfm.UserExtra);
}
this.ss.loginSuccess.emit(this.ss.loggedIn);
}


isAuthenticated(): boolean { isAuthenticated(): boolean {
return this.ss.loggedIn.login;
}

allowEditLoan(): boolean{
return true;
return this.ss.isLoggedIn();
} }


login(email: string, password: string): void { login(email: string, password: string): void {

this.http.post<ApiV1LoginResponse>(this.config.apiUrl + 'login', {u: email, p: password}).subscribe( this.http.post<ApiV1LoginResponse>(this.config.apiUrl + 'login', {u: email, p: password}).subscribe(
responseData => { responseData => {
this.ss.loggedIn.session = responseData['Biukop-Session'];
this.ss.loggedIn.login = responseData.login;
this.ss.loggedIn.machineId = responseData['Biukop-Mid'];
this.ss.loggedIn.sessionExpire = responseData.sessionExpire;
this.ss.loggedIn.role = responseData.role;
if ( responseData.User !== undefined) {
this.ss.loggedIn.User = new PeopleModel(responseData.User);

if (responseData.UserExtra !== undefined ) {
this.ss.loggedIn.UserExtra = new UserExtraModel(responseData.UserExtra);
}else{
this.ss.loggedIn.UserExtra = UserExtraModel.EmptyNew();
}
}else{
this.ss.loggedIn.User = PeopleModel.EmptyNew();
}

this.ss.saveSessionInfo();
this.ss.loginSuccess.emit(responseData);
const sfm = new ApiV1LoginResponse({
session: responseData['Biukop-Session'],
login: responseData.login,
machineId: responseData['Biukop-Mid'],
sessionExpire: responseData.sessionExpire,
role: responseData.role,
User: responseData.User || new PeopleModel({}),
UserExtra: responseData.UserExtra || new UserExtraModel({}),
});
this.ss.login(sfm);
this.ss.saveSessionInfo();
}, },
error => { error => {
this.ss.loggedIn = ApiV1LoginResponse.EmptyNew();
console.log('login error', error);
this.ss.loginSuccess.emit(this.ss.loggedIn);
this.ss.login( new ApiV1LoginResponse({}) ) ;
} }

); );
} }


this.ss.logout(); this.ss.logout();
} }



public getUrl(key: string): string{ public getUrl(key: string): string{
return this.config.getUrl(key); return this.config.getUrl(key);
} }


// whether a specific login id is available
public LoginAvailable(v: string): Observable<boolean> { public LoginAvailable(v: string): Observable<boolean> {
return this.http.get<boolean>(this.getUrl('login-available/' + v)); return this.http.get<boolean>(this.getUrl('login-available/' + v));
} }

+ 19
- 0
src/app/service/payout.service.ts Переглянути файл

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {AuthService} from './auth.service';
import {Observable} from 'rxjs';
import {PayOutExModel} from '../models/payout.ex.model';
import {CompositeFilterDescriptor} from '@progress/kendo-data-query';

@Injectable({providedIn: 'root'})
export class PayOutService {
constructor(private http: HttpClient, private auth: AuthService ){ }

public getPayOutEx(id: string): Observable<PayOutExModel> {
return this.http.get<PayOutExModel>(this.auth.getUrl('payout-ex/' + id));
}

public getPayOutList(filter: CompositeFilterDescriptor): Observable<PayOutExModel[]> {
return this.http.post<PayOutExModel[]>(this.auth.getUrl('payout-ex-list/'), filter);
}
}

+ 108
- 31
src/app/service/session.service.ts Переглянути файл

import {EventEmitter, Injectable} from '@angular/core';
import {EventEmitter, Injectable, OnInit} from '@angular/core';
import {AppConfig} from '../app.config'; import {AppConfig} from '../app.config';
import {ApiV1LoginResponse} from '../models/api-v1-login-response'; import {ApiV1LoginResponse} from '../models/api-v1-login-response';
import {PeopleModel} from '../models/people.model'; import {PeopleModel} from '../models/people.model';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {NotificationService} from '@progress/kendo-angular-notification'; import {NotificationService} from '@progress/kendo-angular-notification';
import {debounce} from 'ts-debounce'; import {debounce} from 'ts-debounce';
import {UserExtraModel} from '../models/user-extra.model';
import {WebSocketService} from '../websocket';


@Injectable() @Injectable()
export class SessionService { export class SessionService {
public loggedIn = ApiV1LoginResponse.EmptyNew();
loginSuccess = new EventEmitter <ApiV1LoginResponse>();

public loggedIn = new ApiV1LoginResponse({});
loginResult = new EventEmitter <ApiV1LoginResponse>();
logoutEvent = new EventEmitter <ApiV1LoginResponse>();
private machineId = localStorage.getItem('mid') || '';
debouncedLocalStorageMonitor = debounce( this.localStorageChange, 1000); // to avoid to frequent local storage changes debouncedLocalStorageMonitor = debounce( this.localStorageChange, 1000); // to avoid to frequent local storage changes


constructor(private config: AppConfig, private http: HttpClient, private router: Router, private ns: NotificationService){
const self = this;
window.addEventListener('storage', event => {
if (event.storageArea === localStorage && event.key === this.config.storageKey) {
// It's local storage
self.debouncedLocalStorageMonitor(event).then( r => {});
}
}, false);
constructor(private config: AppConfig, private http: HttpClient,
private router: Router, private ns: NotificationService,
private ws: WebSocketService){
//
const self = this;
this.AutoLogin();
window.addEventListener('storage', event => {
if (event.storageArea === localStorage && event.key === this.config.storageKey) {
// It's local storage
self.debouncedLocalStorageMonitor(event).then( r => {});
}
}, false);
}

private AutoLogin(): void {
const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey));
if (!sfm) {
return;
}
this.login(sfm);
}


public login( resp: ApiV1LoginResponse): void{
this.loggedIn = new ApiV1LoginResponse(resp);

if ( this.MachineId !== '' && resp.machineId !== '' && this.MachineId !== resp.machineId ) {
this.MachineId = resp.machineId; // update machine id
}

if (this.loggedIn.session !== ''){
this.ws.SessionId = this.loggedIn.session;
}

if ( resp.UserExtra !== undefined ) {
this.loggedIn.UserExtra = new UserExtraModel(resp.UserExtra);
}
this.loginResult.emit(this.loggedIn);
} }


private localStorageChange(event: StorageEvent): void { private localStorageChange(event: StorageEvent): void {
console.log(event);
console.log('local storage change', event);
const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey)); const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey));
if ( sfm && sfm.session && sfm.User && sfm.User.Id && this.loggedIn.User.Id) { if ( sfm && sfm.session && sfm.User && sfm.User.Id && this.loggedIn.User.Id) {
if ( sfm.session === this.loggedIn.session && sfm.User.Id !== this.loggedIn.User.Id){ if ( sfm.session === this.loggedIn.session && sfm.User.Id !== this.loggedIn.User.Id){


public saveSessionInfo(): void { public saveSessionInfo(): void {
localStorage.setItem(this.config.storageKey, JSON.stringify(this.loggedIn)); localStorage.setItem(this.config.storageKey, JSON.stringify(this.loggedIn));
localStorage.setItem('mid', this.loggedIn.machineId);
} }



public isAdmin(): boolean { public isAdmin(): boolean {
return this.loggedIn.role === 'admin'; return this.loggedIn.role === 'admin';
} }
return this.loggedIn.login === true; return this.loggedIn.login === true;
} }


public get CurrentUser(): PeopleModel {
if (this.isLoggedIn() ) {
return this.loggedIn.User;
}else{
return new PeopleModel({});
}
}

public isCurrentUser(id: string): boolean { public isCurrentUser(id: string): boolean {
return id !== '' && this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id; return id !== '' && this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id;
} }
} }


logout(): void { logout(): void {
if ( !this.loggedIn.login ){
if ( ! this.isLoggedIn() ){
return; return;
} }
this.loggedIn = ApiV1LoginResponse.EmptyNew();
this.loginSuccess.emit(this.loggedIn);
this.router.navigate(['/login']);
localStorage.removeItem(this.config.storageKey);

this.http.post(`${this.config.apiUrl}logout`, '').subscribe( this.http.post(`${this.config.apiUrl}logout`, '').subscribe(
resp => { resp => {
this.show();
this.ToastMessage();
}, },
event => { event => {
this.show('logout from server (with warnings)');
this.ToastMessage('logout from server (with warnings)');
this.reLogin();
}, () => {
this.reLogin();
} }
); );
} }


public isLoggedIn(): boolean{
if ( this.loggedIn.login !== undefined &&
this.loggedIn.login &&
this.machineId !== '' &&
this.loggedIn.session !== '' ) {
return true;
}
return false;
}

private reLogin(): void {
this.loggedIn = new ApiV1LoginResponse({});
localStorage.removeItem(this.config.storageKey);
this.router.navigate(['/login']).then( () => {
this.logoutEvent.emit(this.loggedIn);
});
}

logoutWithoutPersistingStorage(): void { logoutWithoutPersistingStorage(): void {
if ( !this.loggedIn.login ){
if ( !this.isLoggedIn() ){
return; return;
} }
this.loggedIn = ApiV1LoginResponse.EmptyNew();
this.loginSuccess.emit(this.loggedIn);
console.log('logout without storate');
this.loggedIn = new ApiV1LoginResponse({});
this.logoutEvent.emit(this.loggedIn);
this.router.navigate(['/login']).then(r => { this.router.navigate(['/login']).then(r => {
this.show();
this.ToastMessage();
} ); } );
} }


logoutAndClearLocalStorage(): void { logoutAndClearLocalStorage(): void {
if ( !this.loggedIn.login ){
if ( !this.isLoggedIn() ){
return; return;
} }
console.log('logout ++++++++++++++ storate');
localStorage.removeItem(this.config.storageKey); localStorage.removeItem(this.config.storageKey);
this.loggedIn = ApiV1LoginResponse.EmptyNew();
this.loginSuccess.emit(this.loggedIn);
this.loggedIn = new ApiV1LoginResponse({});
this.logoutEvent.emit(this.loggedIn);
this.router.navigate(['/login']).then(r => { this.router.navigate(['/login']).then(r => {
this.show();
this.ToastMessage();
} ); } );
} }


public show( msg: string = 'Successfully logged out'): void {
public ToastMessage(msg: string = 'Successfully logged out'): void {
this.ns.show({ this.ns.show({
content: msg, content: msg,
cssClass: 'button-notification', cssClass: 'button-notification',
animation: { type: 'slide', duration: 400 }, animation: { type: 'slide', duration: 400 },
position: { horizontal: 'center', vertical: 'top' },
position: { horizontal: 'right', vertical: 'top' },
type: { style: 'info', icon: true }, type: { style: 'info', icon: true },
hideAfter : 2000 hideAfter : 2000
}); });
} }

public get MachineId(): string {
return this.machineId || '';
}

public set MachineId(mid) {
if ( this.machineId === '' || this.machineId !== mid ){
this.machineId = mid;
localStorage.setItem('mid', mid);
}
}

public get SessionId(): string{
return this.loggedIn.session || '';
}

} }

+ 12
- 2
src/app/top-bar/top-bar.component.ts Переглянути файл

Avatar = './assets/img/logout.png'; Avatar = './assets/img/logout.png';
public items: any[] = mainMenuItems; public items: any[] = mainMenuItems;
private loginSub: Subscription; private loginSub: Subscription;
private logoutSub: Subscription;


public showMenu = true; public showMenu = true;
public opened = false; public opened = false;


this.selectMenuShow(this.ss.loggedIn.role); this.selectMenuShow(this.ss.loggedIn.role);


this.loginSub = this.ss.loginSuccess.subscribe(
this.loginSub = this.ss.loginResult.subscribe(
(rsp: ApiV1LoginResponse) => { (rsp: ApiV1LoginResponse) => {
this.login = rsp.login; this.login = rsp.login;
this.LoggedInUser = this.ss.loggedIn.User;
this.LoggedInUser = this.ss.CurrentUser;
this.selectMenuShow(rsp.role); this.selectMenuShow(rsp.role);
} }
); );

this.logoutSub = this.ss.logoutEvent.subscribe(
resp => {
this.login = false;
this.LoggedInUser = this.ss.CurrentUser; // always empty user
}
);
} }


private selectMenuShow(role: string): void { private selectMenuShow(role: string): void {


ngOnDestroy(): void { ngOnDestroy(): void {
this.loginSub.unsubscribe(); this.loginSub.unsubscribe();
this.logoutSub.unsubscribe();
} }


public logout(): void{ public logout(): void{
public close(action: string): void { public close(action: string): void {
this.opened = false; this.opened = false;
if ( action === 'yes') { if ( action === 'yes') {
// this.login = false;
this.authService.logout(); this.authService.logout();
} }
} }

+ 38
- 13
src/app/websocket.ts Переглянути файл

@Injectable() @Injectable()
export class WebSocketService extends Subject<string>{ export class WebSocketService extends Subject<string>{


private connected = false;
private sessionId = '';
public ws: WebSocket; public ws: WebSocket;
private readonly url: string; private readonly url: string;
public LoginEvent: Subject<WsLoginEventModel>; public LoginEvent: Subject<WsLoginEventModel>;


// cannot have session as service, as session use us as building block
constructor(private config: AppConfig) { constructor(private config: AppConfig) {
super(); super();
this.url = config.apiWsUrl; this.url = config.apiWsUrl;
private startWebsocket(): void { private startWebsocket(): void {
console.log('starting websocket now..', this.url); console.log('starting websocket now..', this.url);
this.ws = new WebSocket(this.url); this.ws = new WebSocket(this.url);
this.ws.onopen = this.onOpen.bind(this);
this.ws.onmessage = this.onMessage.bind(this); this.ws.onmessage = this.onMessage.bind(this);
this.ws.onerror = this.onError.bind(this); this.ws.onerror = this.onError.bind(this);
this.ws.onclose = this.onClose.bind(this); this.ws.onclose = this.onClose.bind(this);
} }


// public createObservableSocket(url: string): Observable<string> {
// this.url = url;
// const ret = new Observable<string> ( observer => {
// this.observer = observer;
// });
// this.startWebsocket();
// return ret;
// }
public onOpen(): void {
this.connected = true;
if ( this.sessionId !== '' ){
this.registerSessionId( this.sessionId);
}
}

public set SessionId( sid: string ) {
if ( this.sessionId !== sid ){
this.registerSessionId(sid);
}
this.sessionId = sid;
}

private registerSessionId(sid: string): void {
const msg = {
t: 'register-session',
session: sid,
};
this.Send(JSON.stringify(msg));
}


public Send(msg: string): void {
if ( !this.connected ) {
return;
}
try {
this.ws.send(msg);
}catch (e){
this.connected = false;
}
}


public onMessage(event): void{ public onMessage(event): void{
this.connected = true;
super.next(event.data); super.next(event.data);
try { try {
const e = JSON.parse(event.data); const e = JSON.parse(event.data);
} }


public onError(event): void{ public onError(event): void{
this.connected = false;
console.log('on error ', event); console.log('on error ', event);
} }


public onClose(event): void{ public onClose(event): void{
this.connected = false;
console.log('websocket closed.. reconnect in 1s ..', event); console.log('websocket closed.. reconnect in 1s ..', event);
setTimeout(() => { this.startWebsocket(); } , 1000); setTimeout(() => { this.startWebsocket(); } , 1000);
} }




public sendMessage(message: any): void {
this.ws.send(message);
}
} }

+ 1
- 1
tsconfig.json Переглянути файл

"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"target": "es2015",
"target": "es5",
"module": "es2020", "module": "es2020",
"lib": [ "lib": [
"es2018", "es2018",

Завантаження…
Відмінити
Зберегти