diff --git a/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html b/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html
index d661507a..ba2d664c 100644
--- a/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html
+++ b/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html
@@ -2,6 +2,8 @@
diff --git a/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.ts b/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.ts
index 832e8340..bad075ab 100644
--- a/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.ts
+++ b/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
import { Store, select } from '@ngrx/store';
@@ -7,20 +7,45 @@ import { Company } from '@ucap-webmessenger/api-external';
import * as AppStore from '@app/store';
import * as AuthenticationStore from '@app/store/account/authentication';
import * as CompanyStore from '@app/store/setting/company';
-import { Observable } from 'rxjs';
+import { Observable, Subscription } from 'rxjs';
import { Router } from '@angular/router';
+import { tap, map } from 'rxjs/operators';
+import {
+ DialogService,
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+} from '@ucap-webmessenger/ui';
+import { StringUtil } from '@ucap-webmessenger/core';
@Component({
selector: 'app-page-account-login',
templateUrl: './login.page.component.html',
styleUrls: ['./login.page.component.scss']
})
-export class LoginPageComponent implements OnInit {
+export class LoginPageComponent implements OnInit, OnDestroy {
companyList$: Observable
;
- constructor(private store: Store, private router: Router) {}
+ loginFailureCount: Subscription;
+
+ defatulLoginBtnText: string;
+ loginBtnText: string;
+ loginBtnEnable: boolean;
+
+ timeChecker: any;
+ defatulWaitingTime: number;
+ waitingTime: number;
+
+ constructor(
+ private store: Store,
+ private router: Router,
+ private dialogService: DialogService
+ ) {}
ngOnInit(): void {
+ this.defatulLoginBtnText = 'LOGIN';
+ this.defatulWaitingTime = 5 * 60; // sec
+
this.store.dispatch(
CompanyStore.companyList({
companyGroupCode: 'LG'
@@ -30,6 +55,80 @@ export class LoginPageComponent implements OnInit {
this.companyList$ = this.store.pipe(
select(AppStore.SettingSelector.CompanySelector.companyList)
);
+
+ this.loginFailureCount = this.store
+ .pipe(
+ select(AppStore.AccountSelector.AuthenticationSelector.loginFailCount),
+ map(count => {
+ if (count > 0) {
+ if (count < 5) {
+ this.dialogService.open<
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+ >(AlertDialogComponent, {
+ width: '360px',
+ data: {
+ title: 'Alert',
+ html: `아이디 또는 패스워드가
일치하지 않습니다.`
+ }
+ });
+ }
+
+ if (count === 5) {
+ this.dialogService.open<
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+ >(AlertDialogComponent, {
+ width: '360px',
+ data: {
+ title: 'Alert',
+ html: `비밀번호 오류 횟수 초과입니다.
비밀번호를 확인하신 후
잠시 후 다시 시작해 주세요.`
+ }
+ });
+
+ this.timeChecker = setInterval(() => this.getCheckTime(), 1000);
+ }
+ return;
+ } else {
+ if (!!this.timeChecker) {
+ clearInterval(this.timeChecker);
+ }
+ this.waitingTime = this.defatulWaitingTime;
+ this.loginBtnText = this.defatulLoginBtnText;
+ this.loginBtnEnable = true;
+ }
+ })
+ )
+ .subscribe();
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.loginFailureCount) {
+ this.loginFailureCount.unsubscribe();
+ }
+ }
+
+ getCheckTime() {
+ if (this.waitingTime <= 0) {
+ // reset.
+ if (!!this.timeChecker) {
+ clearInterval(this.timeChecker);
+ }
+ this.waitingTime = this.defatulWaitingTime;
+ this.loginBtnText = this.defatulLoginBtnText;
+ this.loginBtnEnable = true;
+ } else {
+ // wait.
+ this.waitingTime = this.waitingTime - 1;
+
+ this.loginBtnText = `${StringUtil.zeroFill(
+ Math.floor(this.waitingTime / 60),
+ 2
+ )}:${StringUtil.zeroFill(this.waitingTime % 60, 2)}`;
+ this.loginBtnEnable = false;
+ }
}
onLogin(value: {
diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts
index 4ad59d61..b7b9849c 100644
--- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts
@@ -6,7 +6,7 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { LoginInfo } from '@app/types';
import {
UserPasswordSetRequest,
- UserPasswordSetResponse,
+ UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';
export const webLogin = createAction(
@@ -43,6 +43,15 @@ export const loginFailure = createAction(
props<{ error: any }>()
);
+export const increaseLoginFailCount = createAction(
+ '[Account::Authentication] Increase Login Failure Count',
+ props()
+);
+export const initialLoginFailCount = createAction(
+ '[Account::Authentication] Initialize Login Failure Count',
+ props()
+);
+
export const loginRedirect = createAction(
'[Account::Authentication] Login Redirect'
);
diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts
index d33291f4..331398e1 100644
--- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts
@@ -9,14 +9,14 @@ import { Actions, ofType, createEffect } from '@ngrx/effects';
import {
PiService,
Login2Response,
- UserTermsActionResponse,
+ UserTermsActionResponse
} from '@ucap-webmessenger/pi';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import {
DialogService,
ConfirmDialogComponent,
ConfirmDialogData,
- ConfirmDialogResult,
+ ConfirmDialogResult
} from '@ucap-webmessenger/ui';
import {
@@ -35,12 +35,14 @@ import {
changePassword,
changePasswordFailure,
changePasswordSuccess,
+ increaseLoginFailCount,
+ initialLoginFailCount
} from './actions';
import {
LoginInfo,
KEY_LOGIN_INFO,
EnvironmentsInfo,
- KEY_ENVIRONMENTS_INFO,
+ KEY_ENVIRONMENTS_INFO
} from '@app/types';
import { AppAuthenticationService } from '@app/services/authentication.service';
import { NGXLogger } from 'ngx-logger';
@@ -48,7 +50,7 @@ import { Store } from '@ngrx/store';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import {
ServiceProtocolService,
- UserPasswordSetResponse,
+ UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';
@Injectable()
@@ -62,17 +64,19 @@ export class Effects {
.login2({
loginId: params.loginInfo.loginId,
loginPw: params.loginInfo.loginPw,
- companyCode: params.loginInfo.companyCode,
+ companyCode: params.loginInfo.companyCode
})
.pipe(
map((res: Login2Response) => {
if ('success' !== res.status.toLowerCase()) {
+ this.store.dispatch(increaseLoginFailCount({}));
return webLoginFailure({ error: 'Failed' });
} else {
+ this.store.dispatch(initialLoginFailCount({}));
return webLoginSuccess({
loginInfo: params.loginInfo,
rememberMe: params.rememberMe,
- login2Response: res,
+ login2Response: res
});
}
}),
@@ -147,8 +151,8 @@ export class Effects {
width: '220px',
data: {
title: 'Logout',
- message: 'Logout ?',
- },
+ message: 'Logout ?'
+ }
});
return result.choice;
@@ -177,7 +181,7 @@ export class Effects {
token: loginRes.tokenString,
deviceType: environmentsInfo.deviceType,
localeCode: loginInfo.localeCode,
- textOnly: 'true',
+ textOnly: 'true'
});
const result = await this.dialogService.open<
@@ -189,9 +193,9 @@ export class Effects {
height: '500px',
disableClose: true,
data: {
- title: '개인정보 동의',
+ title: '개인정보 동의'
// html: ``
- },
+ }
});
if (result.choice) {
@@ -213,8 +217,8 @@ export class Effects {
disableClose: true,
data: {
title: '패스워드 만료',
- message: '',
- },
+ message: ''
+ }
});
if (result.choice) {
@@ -241,7 +245,7 @@ export class Effects {
return {
loginInfo,
environmentsInfo,
- loginResponse: action.loginRes,
+ loginResponse: action.loginRes
};
}),
exhaustMap(params =>
@@ -249,7 +253,7 @@ export class Effects {
.userTermsAction({
userSeq: params.loginResponse.userSeq,
token: params.loginResponse.tokenString,
- deviceType: params.environmentsInfo.deviceType,
+ deviceType: params.environmentsInfo.deviceType
})
.pipe(
map((res: UserTermsActionResponse) => {
@@ -273,7 +277,7 @@ export class Effects {
this.serviceProtocolService.userPasswordSet(req).pipe(
map((res: UserPasswordSetResponse) => {
return changePasswordSuccess({
- res,
+ res
});
}),
catchError(error => of(changePasswordFailure({ error })))
diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/reducers.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/reducers.ts
index 77e08802..1f91456d 100644
--- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/reducers.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/reducers.ts
@@ -1,13 +1,30 @@
import { Action, combineReducers, createReducer, on } from '@ngrx/store';
import { State, initialState } from './state';
-import { loginSuccess } from './actions';
+import {
+ loginSuccess,
+ increaseLoginFailCount,
+ initialLoginFailCount
+} from './actions';
export const reducer = createReducer(
initialState,
on(loginSuccess, (state, action) => {
return {
...state,
- loginRes: action.loginRes,
+ loginRes: action.loginRes
+ };
+ }),
+
+ on(increaseLoginFailCount, (state, action) => {
+ return {
+ ...state,
+ loginFailCount: state.loginFailCount + 1
+ };
+ }),
+ on(initialLoginFailCount, (state, action) => {
+ return {
+ ...state,
+ loginFailCount: 0
};
})
);
diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/state.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/state.ts
index 2c34d04b..005cd098 100644
--- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/state.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/state.ts
@@ -4,14 +4,20 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
// tslint:disable-next-line: no-empty-interface
export interface State {
loginRes: LoginResponse | null;
+ loginFailCount: number;
}
export const initialState: State = {
loginRes: null,
+ loginFailCount: 0
};
export function selectors(selector: Selector) {
return {
loginRes: createSelector(selector, (state: State) => state.loginRes),
+ loginFailCount: createSelector(
+ selector,
+ (state: State) => state.loginFailCount
+ )
};
}
diff --git a/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts b/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts
index 162888bc..ffd83d2d 100644
--- a/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts
+++ b/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts
@@ -1 +1,18 @@
-export class StringUtil {}
+export class StringUtil {
+ /**
+ * prefix zero fill
+ * @param str target string
+ * @param len fill in length
+ */
+ public static zeroFill(str: any, len: number): string {
+ if (typeof str === 'string') {
+ let fillin = '';
+ for (let i = 0; i < len - str.length; i++) {
+ fillin += '0';
+ }
+ return fillin + str;
+ } else if (typeof str === 'number') {
+ return StringUtil.zeroFill(str.toString(), len);
+ }
+ }
+}
diff --git a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.html b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.html
index 0625d156..e7404854 100644
--- a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.html
+++ b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.html
@@ -2,7 +2,7 @@
LOGIN TO YOUR ACCOUNT