implement harbor shell related things with authorization support

This commit is contained in:
Steven Zou 2017-02-21 14:04:15 +08:00
parent 87679380db
commit 59bea0e365
19 changed files with 190 additions and 89 deletions

View File

@ -1,15 +1,20 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { CoreModule } from '../core/core.module';
import { SignInComponent } from './sign-in/sign-in.component'; import { SignInComponent } from './sign-in/sign-in.component';
import { PasswordSettingComponent } from './password/password-setting.component';
import { PasswordSettingService } from './password/password-setting.service';
@NgModule({ @NgModule({
imports: [ imports: [
SharedModule, CoreModule,
RouterModule RouterModule
], ],
declarations: [SignInComponent], declarations: [SignInComponent, PasswordSettingComponent],
exports: [SignInComponent] exports: [SignInComponent, PasswordSettingComponent],
providers: [PasswordSettingService]
}) })
export class AccountModule { } export class AccountModule { }

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'forgot-password',
templateUrl: "forgot-password.component.html"
})
export class ForgotPasswordComponent {
// constructor(private router: Router){}
}

View File

@ -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){}
}

View File

@ -97,8 +97,7 @@ export class SignInComponent implements AfterViewChecked {
//Trigger the signin action //Trigger the signin action
signIn(): void { signIn(): void {
//Should validate input firstly //Should validate input firstly
if (!this.validate()) { if (!this.validate() || this.signInStatus === signInStatusOnGoing) {
console.info("return");
return; return;
} }
@ -113,14 +112,18 @@ export class SignInComponent implements AfterViewChecked {
//Validate the sign-in session //Validate the sign-in session
this.session.retrieveUser() this.session.retrieveUser()
.then(() => { .then(user => {
//Routing to the right location //Routing to the right location
let nextRoute = ["/harbor", "dashboard"]; let nextRoute = ["/harbor", "projects"];
this.router.navigate(nextRoute); 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 //Help user navigate to the sign up

View File

@ -1 +0,0 @@
<p>Placeholder for signup</p>

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'sign-up',
templateUrl: "sign-up.component.html"
})
export class SignUpComponent {
// constructor(private router: Router){}
}

View File

@ -4,25 +4,23 @@ import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
import { ClarityModule } from 'clarity-angular'; import { ClarityModule } from 'clarity-angular';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { AccountModule } from './account/account.module';
import { BaseModule } from './base/base.module'; import { BaseModule } from './base/base.module';
import { HarborRoutingModule } from './harbor-routing.module'; import { HarborRoutingModule } from './harbor-routing.module';
import { CoreModule } from './core/core.module'; import { SharedModule } from './shared/shared.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
], ],
imports: [ imports: [
CoreModule, SharedModule,
AccountModule,
BaseModule, BaseModule,
HarborRoutingModule HarborRoutingModule
], ],
providers: [], providers: [],
bootstrap: [ AppComponent ] bootstrap: [AppComponent]
}) })
export class AppModule { export class AppModule {
} }

View File

@ -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> <h3 class="modal-title">Accout Settings</h3>
<div class="modal-body"> <div class="modal-body">
<form> <form>

View File

@ -5,12 +5,21 @@ import { HarborShellComponent } from './harbor-shell/harbor-shell.component';
import { DashboardComponent } from '../dashboard/dashboard.component'; import { DashboardComponent } from '../dashboard/dashboard.component';
import { ProjectComponent } from '../project/project.component'; import { ProjectComponent } from '../project/project.component';
import { BaseRoutingResolver } from './base-routing-resolver.service';
const baseRoutes: Routes = [ const baseRoutes: Routes = [
{ {
path: 'harbor', component: HarborShellComponent, path: 'harbor',
component: HarborShellComponent,
children: [ 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: [ imports: [
RouterModule.forChild(baseRoutes) RouterModule.forChild(baseRoutes)
], ],
exports: [ RouterModule ] exports: [RouterModule],
providers: [BaseRoutingResolver]
}) })
export class BaseRoutingModule { export class BaseRoutingModule {

View File

@ -1,5 +1,5 @@
<clr-main-container> <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> <global-message></global-message>
<div class="content-container"> <div class="content-container">
<div class="content-area" [class.container-override]="showSearch"> <div class="content-area" [class.container-override]="showSearch">
@ -26,4 +26,5 @@
</nav> </nav>
</div> </div>
</clr-main-container> </clr-main-container>
<account-settings-modal></account-settings-modal> <account-settings-modal></account-settings-modal>
<password-setting (pwdChange)="watchPwdChange($event)"></password-setting>

View File

@ -1,12 +1,14 @@
import { Component, OnInit, ViewChild } from '@angular/core'; 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 { ModalEvent } from '../modal-event';
import { SearchEvent } from '../search-event'; import { SearchEvent } from '../search-event';
import { modalAccountSettings, modalPasswordSetting } from '../modal-events.const';
import { AccountSettingsModalComponent } from '../account-settings/account-settings-modal.component'; import { AccountSettingsModalComponent } from '../account-settings/account-settings-modal.component';
import { SearchResultComponent } from '../global-search/search-result.component'; import { SearchResultComponent } from '../global-search/search-result.component';
import { PasswordSettingComponent } from '../../account/password/password-setting.component';
import { NavigatorComponent } from '../navigator/navigator.component';
@Component({ @Component({
selector: 'harbor-shell', selector: 'harbor-shell',
@ -22,18 +24,22 @@ export class HarborShellComponent implements OnInit {
@ViewChild(SearchResultComponent) @ViewChild(SearchResultComponent)
private searchResultComponet: 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 //To indicator whwther or not the search results page is displayed
//We need to use this property to do some overriding work //We need to use this property to do some overriding work
private isSearchResultsOpened: boolean = false; private isSearchResultsOpened: boolean = false;
constructor(private session: SessionService) { } constructor(private route: ActivatedRoute) { }
ngOnInit() { ngOnInit() {
let cUser = this.session.getCurrentUser(); this.route.data.subscribe(data => {
if (!cUser) { //dummy
//Try to update the session });
this.session.retrieveUser();
}
} }
public get showSearch(): boolean { public get showSearch(): boolean {
@ -43,8 +49,12 @@ export class HarborShellComponent implements OnInit {
//Open modal dialog //Open modal dialog
openModal(event: ModalEvent): void { openModal(event: ModalEvent): void {
switch (event.modalName) { switch (event.modalName) {
case "account-settings": case modalAccountSettings:
this.accountSettingsModal.open(); this.accountSettingsModal.open();
break;
case modalPasswordSetting:
this.pwdSetting.open();
break;
default: default:
break; break;
} }
@ -63,8 +73,15 @@ export class HarborShellComponent implements OnInit {
//Search results page closed //Search results page closed
//remove the related ovevriding things //remove the related ovevriding things
searchClose(event: boolean): void { searchClose(event: boolean): void {
if(event){ if (event) {
this.isSearchResultsOpened = false; this.isSearchResultsOpened = false;
} }
} }
//Watch password whether changed
watchPwdChange(event: any): void {
if (event) {
this.navigator.logOut(true);
}
}
} }

View File

@ -7,15 +7,22 @@
</div> </div>
<global-search (searchEvt)="transferSearchEvent($event)"></global-search> <global-search (searchEvt)="transferSearchEvent($event)"></global-search>
<div class="header-actions"> <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> <button class="nav-text" clrDropdownToggle>
<clr-icon shape="user" class="is-inverse" size="24" style="left: -2px;"></clr-icon> <clr-icon shape="user" class="is-inverse" size="24" style="left: -2px;"></clr-icon>
<span>Administrator</span> <span>Administrator</span>
<clr-icon shape="caret down"></clr-icon> <clr-icon shape="caret down"></clr-icon>
</button> </button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a href="javascript:void(0)" clrDropdownItem (click)="open()">Account Setting</a> <a href="javascript:void(0)" clrDropdownItem (click)="openAccountSettingsModal()">Account Settings</a>
<a href="javascript:void(0)" clrDropdownItem>Log out</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> </div>
</clr-dropdown> </clr-dropdown>
<clr-dropdown class="dropdown bottom-left"> <clr-dropdown class="dropdown bottom-left">

View File

@ -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 { Router } from '@angular/router';
import { ModalEvent } from '../modal-event'; import { ModalEvent } from '../modal-event';
import { SearchEvent } from '../search-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({ @Component({
selector: 'navigator', 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){} // constructor(private router: Router){}
@Output() showAccountSettingsModal = new EventEmitter<ModalEvent>(); @Output() showAccountSettingsModal = new EventEmitter<ModalEvent>();
@Output() searchEvt = new EventEmitter<SearchEvent>(); @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 the account setting dialog
open():void { openAccountSettingsModal(): void {
this.showAccountSettingsModal.emit({ this.showAccountSettingsModal.emit({
modalName:"account-settings", modalName: modalAccountSettings,
modalFlag: true
});
}
//Open change password dialog
openChangePwdModal(): void {
this.showPwdChangeModal.emit({
modalName: modalPasswordSetting,
modalFlag: true modalFlag: true
}); });
} }
@ -25,4 +52,20 @@ export class NavigatorComponent {
transferSearchEvent(evt: SearchEvent): void { transferSearchEvent(evt: SearchEvent): void {
this.searchEvt.emit(evt); 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:
}
} }

View File

@ -3,9 +3,16 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { SignInComponent } from './account/sign-in/sign-in.component'; 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 = [ const harborRoutes: Routes = [
{ path: '', redirectTo: '/sign-in', pathMatch: 'full' }, {
path: 'harbor',
component: HarborShellComponent
},
{ path: '', redirectTo: '/harbor', pathMatch: 'full' },
{ path: 'sign-in', component: SignInComponent } { path: 'sign-in', component: SignInComponent }
]; ];
@ -13,7 +20,7 @@ const harborRoutes: Routes = [
imports: [ imports: [
RouterModule.forRoot(harborRoutes) RouterModule.forRoot(harborRoutes)
], ],
exports: [ RouterModule ] exports: [RouterModule]
}) })
export class HarborRoutingModule { export class HarborRoutingModule {

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; 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 { ProjectComponent } from './project.component';
import { ProjectDetailComponent } from './project-detail/project-detail.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 { MemberComponent } from './member/member.component';
import { AuditLogComponent } from '../log/audit-log.component'; import { AuditLogComponent } from '../log/audit-log.component';
import { BaseRoutingResolver } from '../base/base-routing-resolver.service';
const projectRoutes: Routes = [ const projectRoutes: Routes = [
{ path: 'harbor', {
component: HarborShellComponent, path: 'harbor',
component: HarborShellComponent,
resolve: {
harborResolver: BaseRoutingResolver
},
children: [ children: [
{ path: 'projects', component: ProjectComponent }, {
{ path: 'projects',
path: 'projects/:id', component: ProjectComponent,
resolve: {
projectsResolver: BaseRoutingResolver
}
},
{
path: 'projects/:id',
component: ProjectDetailComponent, component: ProjectDetailComponent,
resolve: {
projectResolver: BaseRoutingResolver
},
children: [ children: [
{ path: 'repository', component: RepositoryComponent }, { path: 'repository', component: RepositoryComponent },
{ path: 'replication', component: ReplicationComponent }, { path: 'replication', component: ReplicationComponent },
@ -33,6 +48,6 @@ const projectRoutes: Routes = [
imports: [ imports: [
RouterModule.forChild(projectRoutes) RouterModule.forChild(projectRoutes)
], ],
exports: [ RouterModule ] exports: [RouterModule]
}) })
export class ProjectRoutingModule {} export class ProjectRoutingModule { }

View File

@ -2,7 +2,10 @@ import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http'; import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise'; import 'rxjs/add/operator/toPromise';
import { SessionUser } from './session-user';
const currentUserEndpint = "/api/users/current"; const currentUserEndpint = "/api/users/current";
const signOffEndpoint = "/log_out";
/** /**
* Define related methods to handle account and session corresponding things * Define related methods to handle account and session corresponding things
* *
@ -11,7 +14,7 @@ const currentUserEndpint = "/api/users/current";
*/ */
@Injectable() @Injectable()
export class SessionService { export class SessionService {
currentUser: any = null; currentUser: SessionUser = null;
private headers = new Headers({ private headers = new Headers({
"Content-Type": 'application/json' "Content-Type": 'application/json'
@ -22,22 +25,41 @@ export class SessionService {
/** /**
* Get the related information of current signed in user from backend * Get the related information of current signed in user from backend
* *
* @returns {Promise<any>} * @returns {Promise<SessionUser>}
* *
* @memberOf SessionService * @memberOf SessionService
*/ */
retrieveUser(): Promise<any> { retrieveUser(): Promise<SessionUser> {
return this.http.get(currentUserEndpint, { headers: this.headers }).toPromise() 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 => { .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 * For getting info
*/ */
getCurrentUser(): any { getCurrentUser(): SessionUser {
return this.currentUser; 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);
})
}
} }

View File

@ -1,5 +1,6 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CoreModule } from '../core/core.module'; import { CoreModule } from '../core/core.module';
import { AccountModule } from '../account/account.module';
import { SessionService } from '../shared/session.service'; import { SessionService } from '../shared/session.service';
import { MessageComponent } from '../global-message/message.component'; import { MessageComponent } from '../global-message/message.component';
@ -7,13 +8,15 @@ import { MessageService } from '../global-message/message.service';
@NgModule({ @NgModule({
imports: [ imports: [
CoreModule CoreModule,
AccountModule
], ],
declarations: [ declarations: [
MessageComponent MessageComponent
], ],
exports: [ exports: [
CoreModule, CoreModule,
AccountModule,
MessageComponent MessageComponent
], ],
providers: [SessionService, MessageService] providers: [SessionService, MessageService]