mirror of
https://github.com/goharbor/harbor
synced 2025-04-16 07:10:31 +00:00
Merge pull request #1405 from vmware/feature/harbor_shell_user_settings
Feature/harbor shell user settings
This commit is contained in:
commit
d8047740ea
|
@ -1,15 +1,20 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { CoreModule } from '../core/core.module';
|
||||
|
||||
import { SignInComponent } from './sign-in/sign-in.component';
|
||||
import { PasswordSettingComponent } from './password/password-setting.component';
|
||||
|
||||
import { PasswordSettingService } from './password/password-setting.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
CoreModule,
|
||||
RouterModule
|
||||
],
|
||||
declarations: [SignInComponent],
|
||||
exports: [SignInComponent]
|
||||
declarations: [SignInComponent, PasswordSettingComponent],
|
||||
exports: [SignInComponent, PasswordSettingComponent],
|
||||
|
||||
providers: [PasswordSettingService]
|
||||
})
|
||||
export class AccountModule { }
|
|
@ -0,0 +1,55 @@
|
|||
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true">
|
||||
<h3 class="modal-title">Change Password</h3>
|
||||
<div class="modal-body" style="min-height: 250px; overflow-y: hidden;">
|
||||
<form #changepwdForm="ngForm" class="form">
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label for="oldPassword">Current Password</label>
|
||||
<label for="oldPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="oldPassInput.invalid && (oldPassInput.dirty || oldPassInput.touched)">
|
||||
<input type="password" id="oldPassword" placeholder="Enter current password"
|
||||
required
|
||||
name="oldPassword"
|
||||
[(ngModel)]="oldPwd"
|
||||
#oldPassInput="ngModel" size="25">
|
||||
<span class="tooltip-content">
|
||||
Current password is Required.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newPassword">New Password</label>
|
||||
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="newPassInput.invalid && (newPassInput.dirty || newPassInput.touched)">
|
||||
<input type="password" id="newPassword" placeholder="Enter new password"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
|
||||
name="newPassword"
|
||||
[(ngModel)]="newPwd"
|
||||
#newPassInput="ngModel" size="25">
|
||||
<span class="tooltip-content">
|
||||
Password should be at least 7 characters with 1 uppercase,lowercase letetr and 1 number
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reNewPassword">Confirm Password</label>
|
||||
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="(reNewPassInput.invalid && (reNewPassInput.dirty || reNewPassInput.touched)) || (!newPassInput.invalid && reNewPassInput.value != newPassInput.value)">
|
||||
<input type="password" id="reNewPassword" placeholder="Confirm new password"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
|
||||
name="reNewPassword"
|
||||
[(ngModel)]="reNewPwd"
|
||||
#reNewPassInput="ngModel" size="25">
|
||||
<span class="tooltip-content">
|
||||
Password should be at least 7 characters with 1 uppercase,lowercase letetr and 1 number and consisted with new password
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
|
||||
<button type="button" class="btn btn-outline" (click)="close()">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="doOk()">Ok</button>
|
||||
</div>
|
||||
</clr-modal>
|
|
@ -0,0 +1,104 @@
|
|||
import { Component, ViewChild, AfterViewChecked, Output, EventEmitter } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { PasswordSettingService } from './password-setting.service';
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
|
||||
@Component({
|
||||
selector: 'password-setting',
|
||||
templateUrl: "password-setting.component.html"
|
||||
})
|
||||
export class PasswordSettingComponent implements AfterViewChecked {
|
||||
opened: boolean = false;
|
||||
oldPwd: string = "";
|
||||
newPwd: string = "";
|
||||
reNewPwd: string = "";
|
||||
|
||||
private formValueChanged: boolean = false;
|
||||
private onCalling: boolean = false;
|
||||
|
||||
pwdFormRef: NgForm;
|
||||
@ViewChild("changepwdForm") pwdForm: NgForm;
|
||||
|
||||
@Output() private pwdChange = new EventEmitter<any>();
|
||||
|
||||
constructor(private passwordService: PasswordSettingService, private session: SessionService){}
|
||||
|
||||
//If form is valid
|
||||
public get isValid(): boolean {
|
||||
if (this.pwdForm && this.pwdForm.form.get("newPassword")) {
|
||||
return this.pwdForm.valid &&
|
||||
this.pwdForm.form.get("newPassword").value === this.pwdForm.form.get("reNewPassword").value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public get valueChanged(): boolean {
|
||||
return this.formValueChanged;
|
||||
}
|
||||
|
||||
public get showProgress(): boolean {
|
||||
return this.onCalling;
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
if (this.pwdFormRef != this.pwdForm) {
|
||||
this.pwdFormRef = this.pwdForm;
|
||||
if (this.pwdFormRef) {
|
||||
this.pwdFormRef.valueChanges.subscribe(data => {
|
||||
this.formValueChanged = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Open modal dialog
|
||||
open(): void {
|
||||
this.opened = true;
|
||||
this.pwdForm.reset();
|
||||
}
|
||||
|
||||
//Close the moal dialog
|
||||
close(): void {
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
//handle the ok action
|
||||
doOk(): void {
|
||||
if (this.onCalling) {
|
||||
return;//To avoid duplicate click events
|
||||
}
|
||||
|
||||
if (!this.isValid) {
|
||||
return;//Double confirm
|
||||
}
|
||||
|
||||
//Double confirm session is valid
|
||||
let cUser = this.session.getCurrentUser();
|
||||
if(!cUser){
|
||||
return;
|
||||
}
|
||||
|
||||
//Call service
|
||||
this.onCalling = true;
|
||||
|
||||
this.passwordService.changePassword(cUser.user_id,
|
||||
{
|
||||
new_password: this.pwdForm.value.newPassword,
|
||||
old_password: this.pwdForm.value.oldPassword
|
||||
})
|
||||
.then(() => {
|
||||
this.onCalling = false;
|
||||
//Tell shell to reset current view
|
||||
this.pwdChange.emit(true);
|
||||
|
||||
this.close();
|
||||
})
|
||||
.catch(error => {
|
||||
this.onCalling = false;
|
||||
console.error(error);//TODO:
|
||||
});
|
||||
//TODO:publish the successful message to general messae box
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Headers, Http, RequestOptions } from '@angular/http';
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
|
||||
import { PasswordSetting } from './password-setting';
|
||||
|
||||
const passwordChangeEndpoint = "/api/users/:user_id/password";
|
||||
|
||||
@Injectable()
|
||||
export class PasswordSettingService {
|
||||
private headers: Headers = new Headers({
|
||||
"Accept": 'application/json',
|
||||
"Content-Type": 'application/json'
|
||||
});
|
||||
private options: RequestOptions = new RequestOptions({
|
||||
'headers': this.headers
|
||||
});
|
||||
|
||||
constructor(private http: Http) { }
|
||||
|
||||
changePassword(userId: number, setting: PasswordSetting): Promise<any> {
|
||||
if(!setting || setting.new_password.trim()==="" || setting.old_password.trim()===""){
|
||||
return Promise.reject("Invalid data");
|
||||
}
|
||||
|
||||
let putUrl = passwordChangeEndpoint.replace(":user_id", userId+"");
|
||||
return this.http.put(putUrl, JSON.stringify(setting), this.options)
|
||||
.toPromise()
|
||||
.then(() => null)
|
||||
.catch(error=>{
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
11
harbor-app/src/app/account/password/password-setting.ts
Normal file
11
harbor-app/src/app/account/password/password-setting.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
*
|
||||
* Struct for password change
|
||||
*
|
||||
* @export
|
||||
* @class PasswordSetting
|
||||
*/
|
||||
export class PasswordSetting {
|
||||
old_password: string;
|
||||
new_password: string;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'reset-password',
|
||||
templateUrl: "reset-password.component.html"
|
||||
})
|
||||
export class ResetPasswordComponent {
|
||||
// constructor(private router: Router){}
|
||||
}
|
|
@ -97,8 +97,7 @@ export class SignInComponent implements AfterViewChecked {
|
|||
//Trigger the signin action
|
||||
signIn(): void {
|
||||
//Should validate input firstly
|
||||
if (!this.validate()) {
|
||||
console.info("return");
|
||||
if (!this.validate() || this.signInStatus === signInStatusOnGoing) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,14 +112,18 @@ export class SignInComponent implements AfterViewChecked {
|
|||
|
||||
//Validate the sign-in session
|
||||
this.session.retrieveUser()
|
||||
.then(() => {
|
||||
.then(user => {
|
||||
//Routing to the right location
|
||||
let nextRoute = ["/harbor", "dashboard"];
|
||||
let nextRoute = ["/harbor", "projects"];
|
||||
this.router.navigate(nextRoute);
|
||||
})
|
||||
.catch(this.handleError);
|
||||
.catch(error => {
|
||||
this.handleError(error);
|
||||
});
|
||||
})
|
||||
.catch(this.handleError);
|
||||
.catch(error => {
|
||||
this.handleError(error);
|
||||
});
|
||||
}
|
||||
|
||||
//Help user navigate to the sign up
|
||||
|
|
|
@ -4,25 +4,23 @@ import { FormsModule } from '@angular/forms';
|
|||
import { HttpModule } from '@angular/http';
|
||||
import { ClarityModule } from 'clarity-angular';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AccountModule } from './account/account.module';
|
||||
|
||||
import { BaseModule } from './base/base.module';
|
||||
|
||||
import { HarborRoutingModule } from './harbor-routing.module';
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
],
|
||||
imports: [
|
||||
CoreModule,
|
||||
AccountModule,
|
||||
SharedModule,
|
||||
BaseModule,
|
||||
HarborRoutingModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [ AppComponent ]
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop">
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalSize]="'lg'">
|
||||
<h3 class="modal-title">Accout Settings</h3>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
|
|
23
harbor-app/src/app/base/base-routing-resolver.service.ts
Normal file
23
harbor-app/src/app/base/base-routing-resolver.service.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
Router, Resolve, ActivatedRouteSnapshot, RouterStateSnapshot
|
||||
} from '@angular/router';
|
||||
|
||||
import { SessionService } from '../shared/session.service';
|
||||
import { SessionUser } from '../shared/session-user';
|
||||
|
||||
@Injectable()
|
||||
export class BaseRoutingResolver implements Resolve<SessionUser> {
|
||||
|
||||
constructor(private session: SessionService, private router: Router) { }
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SessionUser> {
|
||||
return this.session.retrieveUser()
|
||||
.then(sessionUser => {
|
||||
return sessionUser;
|
||||
})
|
||||
.catch(error => {
|
||||
console.info("Anonymous user");
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,12 +5,21 @@ import { HarborShellComponent } from './harbor-shell/harbor-shell.component';
|
|||
import { DashboardComponent } from '../dashboard/dashboard.component';
|
||||
import { ProjectComponent } from '../project/project.component';
|
||||
|
||||
import { BaseRoutingResolver } from './base-routing-resolver.service';
|
||||
|
||||
const baseRoutes: Routes = [
|
||||
{
|
||||
path: 'harbor', component: HarborShellComponent,
|
||||
{
|
||||
path: 'harbor',
|
||||
component: HarborShellComponent,
|
||||
children: [
|
||||
{ path: 'dashboard', component: DashboardComponent },
|
||||
{ path: 'projects', component: ProjectComponent }
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'projects',
|
||||
component: ProjectComponent
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
|
@ -18,7 +27,9 @@ const baseRoutes: Routes = [
|
|||
imports: [
|
||||
RouterModule.forChild(baseRoutes)
|
||||
],
|
||||
exports: [ RouterModule ]
|
||||
exports: [RouterModule],
|
||||
|
||||
providers: [BaseRoutingResolver]
|
||||
})
|
||||
export class BaseRoutingModule {
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<clr-main-container>
|
||||
<navigator (showAccountSettingsModal)="openModal($event)" (searchEvt)="doSearch($event)"></navigator>
|
||||
<navigator (showAccountSettingsModal)="openModal($event)" (searchEvt)="doSearch($event)" (showPwdChangeModal)="openModal($event)"></navigator>
|
||||
<global-message></global-message>
|
||||
<div class="content-container">
|
||||
<div class="content-area" [class.container-override]="showSearch">
|
||||
|
@ -26,4 +26,5 @@
|
|||
</nav>
|
||||
</div>
|
||||
</clr-main-container>
|
||||
<account-settings-modal></account-settings-modal>
|
||||
<account-settings-modal></account-settings-modal>
|
||||
<password-setting (pwdChange)="watchPwdChange($event)"></password-setting>
|
|
@ -1,12 +1,14 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
import { ModalEvent } from '../modal-event';
|
||||
import { SearchEvent } from '../search-event';
|
||||
import { modalAccountSettings, modalPasswordSetting } from '../modal-events.const';
|
||||
|
||||
import { AccountSettingsModalComponent } from '../account-settings/account-settings-modal.component';
|
||||
import { SearchResultComponent } from '../global-search/search-result.component';
|
||||
import { PasswordSettingComponent } from '../../account/password/password-setting.component';
|
||||
import { NavigatorComponent } from '../navigator/navigator.component';
|
||||
|
||||
@Component({
|
||||
selector: 'harbor-shell',
|
||||
|
@ -22,18 +24,22 @@ export class HarborShellComponent implements OnInit {
|
|||
@ViewChild(SearchResultComponent)
|
||||
private searchResultComponet: SearchResultComponent;
|
||||
|
||||
@ViewChild(PasswordSettingComponent)
|
||||
private pwdSetting: PasswordSettingComponent;
|
||||
|
||||
@ViewChild(NavigatorComponent)
|
||||
private navigator: NavigatorComponent;
|
||||
|
||||
//To indicator whwther or not the search results page is displayed
|
||||
//We need to use this property to do some overriding work
|
||||
private isSearchResultsOpened: boolean = false;
|
||||
|
||||
constructor(private session: SessionService) { }
|
||||
constructor(private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit() {
|
||||
let cUser = this.session.getCurrentUser();
|
||||
if (!cUser) {
|
||||
//Try to update the session
|
||||
this.session.retrieveUser();
|
||||
}
|
||||
this.route.data.subscribe(data => {
|
||||
//dummy
|
||||
});
|
||||
}
|
||||
|
||||
public get showSearch(): boolean {
|
||||
|
@ -43,8 +49,12 @@ export class HarborShellComponent implements OnInit {
|
|||
//Open modal dialog
|
||||
openModal(event: ModalEvent): void {
|
||||
switch (event.modalName) {
|
||||
case "account-settings":
|
||||
case modalAccountSettings:
|
||||
this.accountSettingsModal.open();
|
||||
break;
|
||||
case modalPasswordSetting:
|
||||
this.pwdSetting.open();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -63,8 +73,15 @@ export class HarborShellComponent implements OnInit {
|
|||
//Search results page closed
|
||||
//remove the related ovevriding things
|
||||
searchClose(event: boolean): void {
|
||||
if(event){
|
||||
if (event) {
|
||||
this.isSearchResultsOpened = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Watch password whether changed
|
||||
watchPwdChange(event: any): void {
|
||||
if (event) {
|
||||
this.navigator.logOut(true);
|
||||
}
|
||||
}
|
||||
}
|
2
harbor-app/src/app/base/modal-events.const.ts
Normal file
2
harbor-app/src/app/base/modal-events.const.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const modalAccountSettings= "account-settings";
|
||||
export const modalPasswordSetting = "password-setting";
|
16
harbor-app/src/app/base/navigator/navigator.component.css
Normal file
16
harbor-app/src/app/base/navigator/navigator.component.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
.sign-in-override {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 5px !important;
|
||||
}
|
||||
|
||||
.sign-up-override {
|
||||
padding-left: 5px !important;
|
||||
}
|
||||
|
||||
.custom-divider {
|
||||
display: inline-block;
|
||||
border-right: 2px inset snow;
|
||||
padding: 2px 0px 2px 0px;
|
||||
vertical-align: middle;
|
||||
height: 24px;
|
||||
}
|
|
@ -7,15 +7,22 @@
|
|||
</div>
|
||||
<global-search (searchEvt)="transferSearchEvent($event)"></global-search>
|
||||
<div class="header-actions">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'" class="dropdown">
|
||||
<div *ngIf="!isSessionValid">
|
||||
<a href="javascript:void(0)" class="nav-link nav-text sign-in-override" routerLink="/sign-in" routerLinkActive="active">Sign In</a>
|
||||
<span class="custom-divider"></span>
|
||||
<a href="javascript:void(0)" class="nav-link nav-text sign-up-override" routerLink="/sign-up" routerLinkActive="active">Sign Up</a>
|
||||
</div>
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'" class="dropdown" *ngIf="isSessionValid">
|
||||
<button class="nav-text" clrDropdownToggle>
|
||||
<clr-icon shape="user" class="is-inverse" size="24" style="left: -2px;"></clr-icon>
|
||||
<span>Administrator</span>
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="open()">Account Setting</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem>Log out</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="openAccountSettingsModal()">Account Settings</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="openChangePwdModal()">Change Password</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="logOut(false)">Log out</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<clr-dropdown class="dropdown bottom-left">
|
||||
|
|
|
@ -1,22 +1,49 @@
|
|||
import { Component, Output, EventEmitter } from '@angular/core';
|
||||
import { Component, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ModalEvent } from '../modal-event';
|
||||
import { SearchEvent } from '../search-event';
|
||||
import { modalAccountSettings, modalPasswordSetting } from '../modal-events.const';
|
||||
|
||||
import { SessionUser } from '../../shared/session-user';
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
|
||||
@Component({
|
||||
selector: 'navigator',
|
||||
templateUrl: "navigator.component.html"
|
||||
templateUrl: "navigator.component.html",
|
||||
styleUrls: ["navigator.component.css"]
|
||||
})
|
||||
export class NavigatorComponent {
|
||||
|
||||
export class NavigatorComponent implements OnInit {
|
||||
// constructor(private router: Router){}
|
||||
@Output() showAccountSettingsModal = new EventEmitter<ModalEvent>();
|
||||
@Output() searchEvt = new EventEmitter<SearchEvent>();
|
||||
@Output() showPwdChangeModal = new EventEmitter<ModalEvent>();
|
||||
|
||||
private sessionUser: SessionUser = null;
|
||||
|
||||
constructor(private session: SessionService, private router: Router) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.sessionUser = this.session.getCurrentUser();
|
||||
}
|
||||
|
||||
public get isSessionValid(): boolean {
|
||||
return this.sessionUser != null;
|
||||
}
|
||||
|
||||
//Open the account setting dialog
|
||||
open():void {
|
||||
openAccountSettingsModal(): void {
|
||||
this.showAccountSettingsModal.emit({
|
||||
modalName:"account-settings",
|
||||
modalName: modalAccountSettings,
|
||||
modalFlag: true
|
||||
});
|
||||
}
|
||||
|
||||
//Open change password dialog
|
||||
openChangePwdModal(): void {
|
||||
this.showPwdChangeModal.emit({
|
||||
modalName: modalPasswordSetting,
|
||||
modalFlag: true
|
||||
});
|
||||
}
|
||||
|
@ -25,4 +52,20 @@ export class NavigatorComponent {
|
|||
transferSearchEvent(evt: SearchEvent): void {
|
||||
this.searchEvt.emit(evt);
|
||||
}
|
||||
|
||||
//Log out system
|
||||
logOut(reSignIn: boolean): void {
|
||||
this.session.signOff()
|
||||
.then(() => {
|
||||
this.sessionUser = null;
|
||||
if (reSignIn) {
|
||||
//Naviagte to the sign in route
|
||||
this.router.navigate(["/sign-in"]);
|
||||
} else {
|
||||
//Naviagte to the default route
|
||||
this.router.navigate(["/harbor"]);
|
||||
}
|
||||
})
|
||||
.catch()//TODO:
|
||||
}
|
||||
}
|
|
@ -3,9 +3,16 @@ import { NgModule } from '@angular/core';
|
|||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { SignInComponent } from './account/sign-in/sign-in.component';
|
||||
import { HarborShellComponent } from './base/harbor-shell/harbor-shell.component';
|
||||
|
||||
import { BaseRoutingResolver } from './base/base-routing-resolver.service';
|
||||
|
||||
const harborRoutes: Routes = [
|
||||
{ path: '', redirectTo: '/sign-in', pathMatch: 'full' },
|
||||
{
|
||||
path: 'harbor',
|
||||
component: HarborShellComponent
|
||||
},
|
||||
{ path: '', redirectTo: '/harbor', pathMatch: 'full' },
|
||||
{ path: 'sign-in', component: SignInComponent }
|
||||
];
|
||||
|
||||
|
@ -13,7 +20,7 @@ const harborRoutes: Routes = [
|
|||
imports: [
|
||||
RouterModule.forRoot(harborRoutes)
|
||||
],
|
||||
exports: [ RouterModule ]
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class HarborRoutingModule {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { HarborShellComponent} from '../base/harbor-shell/harbor-shell.component';
|
||||
import { HarborShellComponent } from '../base/harbor-shell/harbor-shell.component';
|
||||
import { ProjectComponent } from './project.component';
|
||||
import { ProjectDetailComponent } from './project-detail/project-detail.component';
|
||||
|
||||
|
@ -10,14 +10,29 @@ import { ReplicationComponent } from '../replication/replication.component';
|
|||
import { MemberComponent } from './member/member.component';
|
||||
import { AuditLogComponent } from '../log/audit-log.component';
|
||||
|
||||
import { BaseRoutingResolver } from '../base/base-routing-resolver.service';
|
||||
|
||||
const projectRoutes: Routes = [
|
||||
{ path: 'harbor',
|
||||
component: HarborShellComponent,
|
||||
{
|
||||
path: 'harbor',
|
||||
component: HarborShellComponent,
|
||||
resolve: {
|
||||
harborResolver: BaseRoutingResolver
|
||||
},
|
||||
children: [
|
||||
{ path: 'projects', component: ProjectComponent },
|
||||
{
|
||||
path: 'projects/:id',
|
||||
{
|
||||
path: 'projects',
|
||||
component: ProjectComponent,
|
||||
resolve: {
|
||||
projectsResolver: BaseRoutingResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:id',
|
||||
component: ProjectDetailComponent,
|
||||
resolve: {
|
||||
projectResolver: BaseRoutingResolver
|
||||
},
|
||||
children: [
|
||||
{ path: 'repository', component: RepositoryComponent },
|
||||
{ path: 'replication', component: ReplicationComponent },
|
||||
|
@ -33,6 +48,6 @@ const projectRoutes: Routes = [
|
|||
imports: [
|
||||
RouterModule.forChild(projectRoutes)
|
||||
],
|
||||
exports: [ RouterModule ]
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class ProjectRoutingModule {}
|
||||
export class ProjectRoutingModule { }
|
9
harbor-app/src/app/shared/session-user.ts
Normal file
9
harbor-app/src/app/shared/session-user.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
//Define the session user
|
||||
export class SessionUser {
|
||||
user_id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
role_name: string;
|
||||
role_id: number;
|
||||
has_admin_role: number;
|
||||
}
|
|
@ -2,7 +2,10 @@ import { Injectable } from '@angular/core';
|
|||
import { Headers, Http } from '@angular/http';
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
|
||||
import { SessionUser } from './session-user';
|
||||
|
||||
const currentUserEndpint = "/api/users/current";
|
||||
const signOffEndpoint = "/log_out";
|
||||
/**
|
||||
* Define related methods to handle account and session corresponding things
|
||||
*
|
||||
|
@ -11,7 +14,7 @@ const currentUserEndpint = "/api/users/current";
|
|||
*/
|
||||
@Injectable()
|
||||
export class SessionService {
|
||||
currentUser: any = null;
|
||||
currentUser: SessionUser = null;
|
||||
|
||||
private headers = new Headers({
|
||||
"Content-Type": 'application/json'
|
||||
|
@ -22,22 +25,41 @@ export class SessionService {
|
|||
/**
|
||||
* Get the related information of current signed in user from backend
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
* @returns {Promise<SessionUser>}
|
||||
*
|
||||
* @memberOf SessionService
|
||||
*/
|
||||
retrieveUser(): Promise<any> {
|
||||
retrieveUser(): Promise<SessionUser> {
|
||||
return this.http.get(currentUserEndpint, { headers: this.headers }).toPromise()
|
||||
.then(response => this.currentUser = response.json())
|
||||
.then(response => {
|
||||
this.currentUser = response.json() as SessionUser;
|
||||
return this.currentUser;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("An error occurred when getting current user ", error);//TODO: Will replaced with general error handler
|
||||
console.log("An error occurred when getting current user ", error);//TODO
|
||||
return Promise.reject(error);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* For getting info
|
||||
*/
|
||||
getCurrentUser(): any {
|
||||
getCurrentUser(): SessionUser {
|
||||
return this.currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out the system
|
||||
*/
|
||||
signOff(): Promise<any> {
|
||||
return this.http.get(signOffEndpoint, { headers: this.headers }).toPromise()
|
||||
.then(() => {
|
||||
//Destroy current session cache
|
||||
this.currentUser = null;
|
||||
}) //Nothing returned
|
||||
.catch(error => {
|
||||
console.log("An error occurred when signing off ", error);//TODO
|
||||
return Promise.reject(error);
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CoreModule } from '../core/core.module';
|
||||
import { AccountModule } from '../account/account.module';
|
||||
|
||||
import { SessionService } from '../shared/session.service';
|
||||
import { MessageComponent } from '../global-message/message.component';
|
||||
|
@ -7,13 +8,15 @@ import { MessageService } from '../global-message/message.service';
|
|||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
CoreModule,
|
||||
AccountModule
|
||||
],
|
||||
declarations: [
|
||||
MessageComponent
|
||||
],
|
||||
exports: [
|
||||
CoreModule,
|
||||
AccountModule,
|
||||
MessageComponent
|
||||
],
|
||||
providers: [SessionService, MessageService]
|
||||
|
|
Loading…
Reference in New Issue
Block a user