mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-23 15:21:35 +01:00
Merge pull request #1592 from vmware/feature/merge_latest_ui_code
merge latest ui code to fix block issues
This commit is contained in:
commit
e17a919e5f
@ -118,7 +118,7 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
|
|||||||
this.session.updateAccountSettings(this.account)
|
this.session.updateAccountSettings(this.account)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isOnCalling = false;
|
this.isOnCalling = false;
|
||||||
this.close();
|
this.opened = false;
|
||||||
this.msgService.announceMessage(200, "PROFILE.SAVE_SUCCESS", AlertType.SUCCESS);
|
this.msgService.announceMessage(200, "PROFILE.SAVE_SUCCESS", AlertType.SUCCESS);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -117,7 +117,7 @@ export class PasswordSettingComponent implements AfterViewChecked {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.onCalling = false;
|
this.onCalling = false;
|
||||||
this.close();
|
this.opened = false;
|
||||||
this.msgService.announceMessage(200, "CHANGE_PWD.SAVE_SUCCESS", AlertType.SUCCESS);
|
this.msgService.announceMessage(200, "CHANGE_PWD.SAVE_SUCCESS", AlertType.SUCCESS);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Headers, Http, RequestOptions } from '@angular/http';
|
import { Headers, Http, RequestOptions, URLSearchParams } from '@angular/http';
|
||||||
import 'rxjs/add/operator/toPromise';
|
import 'rxjs/add/operator/toPromise';
|
||||||
|
|
||||||
import { PasswordSetting } from './password-setting';
|
import { PasswordSetting } from './password-setting';
|
||||||
@ -52,10 +52,18 @@ export class PasswordSettingService {
|
|||||||
return Promise.reject("Invalid reset uuid or password");
|
return Promise.reject("Invalid reset uuid or password");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.http.post(resetPasswordEndpoint, JSON.stringify({
|
let formHeaders = new Headers({
|
||||||
"reset_uuid": uuid,
|
"Content-Type": 'application/x-www-form-urlencoded'
|
||||||
"password": newPassword
|
});
|
||||||
}), this.options)
|
let formOptions: RequestOptions = new RequestOptions({
|
||||||
|
headers: formHeaders
|
||||||
|
});
|
||||||
|
|
||||||
|
let body: URLSearchParams = new URLSearchParams();
|
||||||
|
body.set("reset_uuid", uuid);
|
||||||
|
body.set("password", newPassword);
|
||||||
|
|
||||||
|
return this.http.post(resetPasswordEndpoint, body.toString(), formOptions)
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(response => response)
|
.then(response => response)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -44,7 +44,9 @@ export class ResetPasswordComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getValidationState(key: string): boolean {
|
public getValidationState(key: string): boolean {
|
||||||
return this.validationState && this.validationState[key];
|
return this.validationState &&
|
||||||
|
this.validationState[key] &&
|
||||||
|
key === 'reNewPassword'?this.samePassword():true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public open(): void {
|
public open(): void {
|
||||||
@ -76,10 +78,12 @@ export class ResetPasswordComponent implements OnInit{
|
|||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.pwdService.resetPassword(this.resetUuid, this.password)
|
this.pwdService.resetPassword(this.resetUuid, this.password)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
this.onGoing = false;
|
||||||
this.resetOk = true;
|
this.resetOk = true;
|
||||||
this.inlineAlert.showInlineSuccess({message:'RESET_PWD.RESET_OK'});
|
this.inlineAlert.showInlineSuccess({message:'RESET_PWD.RESET_OK'});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
this.onGoing = false;
|
||||||
if(accessErrorHandler(error, this.msgService)){
|
if(accessErrorHandler(error, this.msgService)){
|
||||||
this.close();
|
this.close();
|
||||||
}else{
|
}else{
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
{{ 'SIGN_IN.INVALID_MSG' | translate }}
|
{{ 'SIGN_IN.INVALID_MSG' | translate }}
|
||||||
</div>
|
</div>
|
||||||
<button [disabled]="isOnGoing || !isValid" type="submit" class="btn btn-primary" (click)="signIn()">{{ 'BUTTON.LOG_IN' | translate }}</button>
|
<button [disabled]="isOnGoing || !isValid" type="submit" class="btn btn-primary" (click)="signIn()">{{ 'BUTTON.LOG_IN' | translate }}</button>
|
||||||
<a href="javascript:void(0)" class="signup" (click)="signUp()">{{ 'BUTTON.SIGN_UP_LINK' | translate }}</a>
|
<a href="javascript:void(0)" class="signup" (click)="signUp()" *ngIf="selfSignUp">{{ 'BUTTON.SIGN_UP_LINK' | translate }}</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<sign-up #signupDialog></sign-up>>
|
<sign-up #signupDialog></sign-up>
|
||||||
<forgot-password #forgotPwdDialog></forgot-password>
|
<forgot-password #forgotPwdDialog></forgot-password>
|
@ -10,6 +10,9 @@ import { SignUpComponent } from '../sign-up/sign-up.component';
|
|||||||
import { harborRootRoute } from '../../shared/shared.const';
|
import { harborRootRoute } from '../../shared/shared.const';
|
||||||
import { ForgotPasswordComponent } from '../password/forgot-password.component';
|
import { ForgotPasswordComponent } from '../password/forgot-password.component';
|
||||||
|
|
||||||
|
import { AppConfigService } from '../../app-config.service';
|
||||||
|
import { AppConfig } from '../../app-config';
|
||||||
|
|
||||||
//Define status flags for signing in states
|
//Define status flags for signing in states
|
||||||
export const signInStatusNormal = 0;
|
export const signInStatusNormal = 0;
|
||||||
export const signInStatusOnGoing = 1;
|
export const signInStatusOnGoing = 1;
|
||||||
@ -23,6 +26,7 @@ export const signInStatusError = -1;
|
|||||||
|
|
||||||
export class SignInComponent implements AfterViewChecked, OnInit {
|
export class SignInComponent implements AfterViewChecked, OnInit {
|
||||||
private redirectUrl: string = "";
|
private redirectUrl: string = "";
|
||||||
|
private appConfig: AppConfig = new AppConfig();
|
||||||
//Form reference
|
//Form reference
|
||||||
signInForm: NgForm;
|
signInForm: NgForm;
|
||||||
@ViewChild('signInForm') currentForm: NgForm;
|
@ViewChild('signInForm') currentForm: NgForm;
|
||||||
@ -41,13 +45,19 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute,
|
||||||
|
private appConfigService: AppConfigService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.appConfig = this.appConfigService.getConfig();
|
||||||
this.route.queryParams
|
this.route.queryParams
|
||||||
.subscribe(params => {
|
.subscribe(params => {
|
||||||
this.redirectUrl = params["redirect_url"] || "";
|
this.redirectUrl = params["redirect_url"] || "";
|
||||||
|
let isSignUp = params["sign_up"] || "";
|
||||||
|
if (isSignUp != "") {
|
||||||
|
this.signUp();//Open sign up
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +75,12 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
|||||||
return this.currentForm.form.valid;
|
return this.currentForm.form.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Whether show the 'sign up' link
|
||||||
|
public get selfSignUp(): boolean {
|
||||||
|
return this.appConfig.auth_mode === 'db_auth'
|
||||||
|
&& this.appConfig.self_registration;
|
||||||
|
}
|
||||||
|
|
||||||
//General error handler
|
//General error handler
|
||||||
private handleError(error) {
|
private handleError(error) {
|
||||||
//Set error status
|
//Set error status
|
||||||
@ -124,7 +140,7 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
|||||||
if (this.redirectUrl === "") {
|
if (this.redirectUrl === "") {
|
||||||
//Routing to the default location
|
//Routing to the default location
|
||||||
this.router.navigateByUrl(harborRootRoute);
|
this.router.navigateByUrl(harborRootRoute);
|
||||||
}else{
|
} else {
|
||||||
this.router.navigateByUrl(this.redirectUrl);
|
this.router.navigateByUrl(this.redirectUrl);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -9,6 +9,8 @@ import { UserService } from '../../user/user.service';
|
|||||||
import { errorHandler } from '../../shared/shared.utils';
|
import { errorHandler } from '../../shared/shared.utils';
|
||||||
import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component';
|
import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component';
|
||||||
|
|
||||||
|
import { Modal } from 'clarity-angular';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'sign-up',
|
selector: 'sign-up',
|
||||||
templateUrl: "sign-up.component.html"
|
templateUrl: "sign-up.component.html"
|
||||||
@ -30,6 +32,9 @@ export class SignUpComponent {
|
|||||||
@ViewChild(InlineAlertComponent)
|
@ViewChild(InlineAlertComponent)
|
||||||
private inlienAlert: InlineAlertComponent;
|
private inlienAlert: InlineAlertComponent;
|
||||||
|
|
||||||
|
@ViewChild(Modal)
|
||||||
|
private modal: Modal;
|
||||||
|
|
||||||
private getNewUser(): User {
|
private getNewUser(): User {
|
||||||
return this.newUserForm.getData();
|
return this.newUserForm.getData();
|
||||||
}
|
}
|
||||||
@ -55,7 +60,7 @@ export class SignUpComponent {
|
|||||||
open(): void {
|
open(): void {
|
||||||
this.newUserForm.reset();//Reset form
|
this.newUserForm.reset();//Reset form
|
||||||
this.formValueChanged = false;
|
this.formValueChanged = false;
|
||||||
this.opened = true;
|
this.modal.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
@ -74,7 +79,7 @@ export class SignUpComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
confirmCancel(): void {
|
confirmCancel(): void {
|
||||||
this.opened = false;
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create new user
|
//Create new user
|
||||||
@ -97,7 +102,7 @@ export class SignUpComponent {
|
|||||||
this.userService.addUser(u)
|
this.userService.addUser(u)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
this.close();
|
this.modal.close();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
|
41
src/ui_ng/src/app/app-config.service.ts
Normal file
41
src/ui_ng/src/app/app-config.service.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Headers, Http, RequestOptions } from '@angular/http';
|
||||||
|
import 'rxjs/add/operator/toPromise';
|
||||||
|
|
||||||
|
import { AppConfig } from './app-config';
|
||||||
|
|
||||||
|
export const systemInfoEndpoint = "/api/systeminfo";
|
||||||
|
/**
|
||||||
|
* Declare service to handle the bootstrap options
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class GlobalSearchService
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AppConfigService {
|
||||||
|
private headers = new Headers({
|
||||||
|
"Content-Type": 'application/json'
|
||||||
|
});
|
||||||
|
private options = new RequestOptions({
|
||||||
|
headers: this.headers
|
||||||
|
});
|
||||||
|
|
||||||
|
//Store the application configuration
|
||||||
|
private configurations: AppConfig = new AppConfig();
|
||||||
|
|
||||||
|
constructor(private http: Http) { }
|
||||||
|
|
||||||
|
public load(): Promise<AppConfig> {
|
||||||
|
return this.http.get(systemInfoEndpoint, this.options).toPromise()
|
||||||
|
.then(response => this.configurations = response.json() as AppConfig)
|
||||||
|
.catch(error => {
|
||||||
|
//Catch the error
|
||||||
|
console.error("Failed to load bootstrap options with error: ", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfig(): AppConfig {
|
||||||
|
return this.configurations;
|
||||||
|
}
|
||||||
|
}
|
20
src/ui_ng/src/app/app-config.ts
Normal file
20
src/ui_ng/src/app/app-config.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export class AppConfig {
|
||||||
|
constructor(){
|
||||||
|
//Set default value
|
||||||
|
this.with_notary = false;
|
||||||
|
this.with_admiral = false;
|
||||||
|
this.admiral_endpoint = "";
|
||||||
|
this.auth_mode = "db_auth";
|
||||||
|
this.registry_url = "";
|
||||||
|
this.project_creation_restriction = "everyone";
|
||||||
|
this.self_registration = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
with_notary: boolean;
|
||||||
|
with_admiral: boolean;
|
||||||
|
admiral_endpoint: string;
|
||||||
|
auth_mode: string;
|
||||||
|
registry_url: string;
|
||||||
|
project_creation_restriction: string;
|
||||||
|
self_registration: boolean;
|
||||||
|
}
|
@ -16,17 +16,14 @@ import { MyMissingTranslationHandler } from './i18n/missing-trans.handler';
|
|||||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||||
import { Http } from '@angular/http';
|
import { Http } from '@angular/http';
|
||||||
|
|
||||||
import { SessionService } from './shared/session.service';
|
import { AppConfigService } from './app-config.service';
|
||||||
|
|
||||||
export function HttpLoaderFactory(http: Http) {
|
export function HttpLoaderFactory(http: Http) {
|
||||||
return new TranslateHttpLoader(http, 'ng/i18n/lang/', '-lang.json');
|
return new TranslateHttpLoader(http, 'ng/i18n/lang/', '-lang.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initConfig(session: SessionService) {
|
export function initConfig(configService: AppConfigService) {
|
||||||
return () => {
|
return () => configService.load();
|
||||||
console.info("app init here");
|
|
||||||
return Promise.resolve(true);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -51,10 +48,12 @@ export function initConfig(session: SessionService) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
providers: [{
|
providers: [
|
||||||
|
AppConfigService,
|
||||||
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initConfig,
|
useFactory: initConfig,
|
||||||
deps: [SessionService],
|
deps: [AppConfigService],
|
||||||
multi: true
|
multi: true
|
||||||
}],
|
}],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
@ -17,4 +17,13 @@
|
|||||||
|
|
||||||
.lang-selected {
|
.lang-selected {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-divider {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
position: relative;
|
||||||
|
top: 10px;
|
||||||
}
|
}
|
@ -5,12 +5,19 @@
|
|||||||
<span class="title">Harbor</span>
|
<span class="title">Harbor</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="header-nav">
|
||||||
|
<a href="{{admiralLink}}" class="nav-link" *ngIf="isIntegrationMode"><span class="nav-text">Management</span></a>
|
||||||
|
<a href="javascript:void(0)" routerLink="/harbor/dashboard" class="active nav-link" *ngIf="isIntegrationMode"><span class="nav-text">Registry</span></a>
|
||||||
|
</div>
|
||||||
<global-search></global-search>
|
<global-search></global-search>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
|
<a href="javascript:void(0)" class="nav-link nav-text" routerLink="/sign-in" routerLinkActive="active" *ngIf="isSessionValid === false">{{'SIGN_IN.HEADER_LINK' | translate}}</a>
|
||||||
|
<div class="nav-divider" *ngIf="!isSessionValid"></div>
|
||||||
|
<a href="javascript:void(0)" class="nav-link nav-text" (click)="openSignUp()" *ngIf="isSessionValid === false">{{'SIGN_UP.TITLE' | translate}}</a>
|
||||||
<clr-dropdown class="dropdown bottom-left">
|
<clr-dropdown class="dropdown bottom-left">
|
||||||
<button class="nav-icon" clrDropdownToggle style="width: 90px;">
|
<button class="nav-icon" clrDropdownToggle style="width: 98px;">
|
||||||
<clr-icon shape="world" style="left:-8px;"></clr-icon>
|
<clr-icon shape="world" style="left:-8px;"></clr-icon>
|
||||||
<span>{{currentLang}}</span>
|
<span style="padding-right: 8px;">{{currentLang}}</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">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Component, Output, EventEmitter, OnInit } from '@angular/core';
|
import { Component, Output, EventEmitter, OnInit, Inject } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router, NavigationExtras } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ModalEvent } from '../modal-event';
|
import { ModalEvent } from '../modal-event';
|
||||||
@ -9,7 +9,10 @@ import { SessionUser } from '../../shared/session-user';
|
|||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { CookieService } from 'angular2-cookie/core';
|
import { CookieService } from 'angular2-cookie/core';
|
||||||
|
|
||||||
import { supportedLangs, enLang, languageNames } from '../../shared/shared.const';
|
import { supportedLangs, enLang, languageNames, signInRoute } from '../../shared/shared.const';
|
||||||
|
|
||||||
|
import { AppConfigService } from '../../app-config.service';
|
||||||
|
import { AppConfig } from '../../app-config';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'navigator',
|
selector: 'navigator',
|
||||||
@ -24,12 +27,14 @@ export class NavigatorComponent implements OnInit {
|
|||||||
|
|
||||||
private sessionUser: SessionUser = null;
|
private sessionUser: SessionUser = null;
|
||||||
private selectedLang: string = enLang;
|
private selectedLang: string = enLang;
|
||||||
|
private appConfig: AppConfig = new AppConfig();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private cookie: CookieService) { }
|
private cookie: CookieService,
|
||||||
|
private appConfigService: AppConfigService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.sessionUser = this.session.getCurrentUser();
|
this.sessionUser = this.session.getCurrentUser();
|
||||||
@ -39,6 +44,8 @@ export class NavigatorComponent implements OnInit {
|
|||||||
//Keep in cookie for next use
|
//Keep in cookie for next use
|
||||||
this.cookie.put("harbor-lang", langChange.lang);
|
this.cookie.put("harbor-lang", langChange.lang);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.appConfig = this.appConfigService.getConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isSessionValid(): boolean {
|
public get isSessionValid(): boolean {
|
||||||
@ -53,6 +60,19 @@ export class NavigatorComponent implements OnInit {
|
|||||||
return languageNames[this.selectedLang];
|
return languageNames[this.selectedLang];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get isIntegrationMode(): boolean {
|
||||||
|
return this.appConfig.with_admiral && this.appConfig.admiral_endpoint.trim() != "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public get admiralLink(): string {
|
||||||
|
let routeSegments = [this.appConfig.admiral_endpoint,
|
||||||
|
"?registry_url=",
|
||||||
|
encodeURIComponent(window.location.href)
|
||||||
|
];
|
||||||
|
|
||||||
|
return routeSegments.join("");
|
||||||
|
}
|
||||||
|
|
||||||
matchLang(lang: string): boolean {
|
matchLang(lang: string): boolean {
|
||||||
return lang.trim() === this.selectedLang;
|
return lang.trim() === this.selectedLang;
|
||||||
}
|
}
|
||||||
@ -94,12 +114,12 @@ export class NavigatorComponent implements OnInit {
|
|||||||
|
|
||||||
//Switch languages
|
//Switch languages
|
||||||
switchLanguage(lang: string): void {
|
switchLanguage(lang: string): void {
|
||||||
if (supportedLangs.find(supportedLang => supportedLang === lang.trim())){
|
if (supportedLangs.find(supportedLang => supportedLang === lang.trim())) {
|
||||||
this.translate.use(lang);
|
this.translate.use(lang);
|
||||||
}else{
|
} else {
|
||||||
this.translate.use(enLang);//Use default
|
this.translate.use(enLang);//Use default
|
||||||
//TODO:
|
//TODO:
|
||||||
console.error('Language '+lang.trim()+' is not suppoted');
|
console.error('Language ' + lang.trim() + ' is not suppoted');
|
||||||
}
|
}
|
||||||
//Try to switch backend lang
|
//Try to switch backend lang
|
||||||
//this.session.switchLanguage(lang).catch(error => console.error(error));
|
//this.session.switchLanguage(lang).catch(error => console.error(error));
|
||||||
@ -107,12 +127,20 @@ export class NavigatorComponent implements OnInit {
|
|||||||
|
|
||||||
//Handle the home action
|
//Handle the home action
|
||||||
homeAction(): void {
|
homeAction(): void {
|
||||||
if(this.sessionUser != null){
|
if (this.sessionUser != null) {
|
||||||
//Navigate to default page
|
//Navigate to default page
|
||||||
this.router.navigate(['harbor']);
|
this.router.navigate(['harbor']);
|
||||||
}else{
|
} else {
|
||||||
//Naviagte to signin page
|
//Naviagte to signin page
|
||||||
this.router.navigate(['sign-in']);
|
this.router.navigate(['sign-in']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openSignUp(): void {
|
||||||
|
let navigatorExtra: NavigationExtras = {
|
||||||
|
queryParams: { "sign_up": true }
|
||||||
|
};
|
||||||
|
|
||||||
|
this.router.navigate([signInRoute], navigatorExtra);
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,10 +3,9 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="authMode">{{'CONFIG.AUTH_MODE' | translate }}</label>
|
<label for="authMode">{{'CONFIG.AUTH_MODE' | translate }}</label>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select id="authMode" name="authMode" [disabled]="disabled(currentConfig.auth_mode)
|
<select id="authMode" name="authMode" [disabled]="disabled(currentConfig.auth_mode)" [(ngModel)]="currentConfig.auth_mode.value">
|
||||||
" [(ngModel)]="currentConfig.auth_mode.value">
|
|
||||||
<option value="db_auth">{{'CONFIG.AUTH_MODE_DB' | translate }}</option>
|
<option value="db_auth">{{'CONFIG.AUTH_MODE_DB' | translate }}</option>
|
||||||
<option value="ldap">{{'CONFIG.AUTH_MODE_LDAP' | translate }}</option>
|
<option value="ldap_auth">{{'CONFIG.AUTH_MODE_LDAP' | translate }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||||
|
@ -20,7 +20,7 @@ export class ConfigurationAuthComponent {
|
|||||||
public get showLdap(): boolean {
|
public get showLdap(): boolean {
|
||||||
return this.currentConfig &&
|
return this.currentConfig &&
|
||||||
this.currentConfig.auth_mode &&
|
this.currentConfig.auth_mode &&
|
||||||
this.currentConfig.auth_mode.value === 'ldap';
|
this.currentConfig.auth_mode.value === 'ldap_auth';
|
||||||
}
|
}
|
||||||
|
|
||||||
private disabled(prop: any): boolean {
|
private disabled(prop: any): boolean {
|
||||||
|
@ -55,4 +55,6 @@
|
|||||||
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>
|
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>
|
||||||
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL' | translate}}</button>
|
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
<button type="button" class="btn btn-outline" (click)="testMailServer()" *ngIf="showTestServerBtn" [disabled]="!isMailConfigValid()">{{'BUTTON.TEST_MAIL' | translate}}</button>
|
<button type="button" class="btn btn-outline" (click)="testMailServer()" *ngIf="showTestServerBtn" [disabled]="!isMailConfigValid()">{{'BUTTON.TEST_MAIL' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-outline" (click)="testLDAPServer()" *ngIf="showLdapServerBtn" [disabled]="!isLDAPConfigValid()">{{'BUTTON.TEST_LDAP' | translate}}</button>
|
||||||
|
<span class="spinner spinner-inline" [hidden]="!testingInProgress"></span>
|
||||||
</div>
|
</div>
|
@ -15,6 +15,8 @@ import { DeletionMessage } from '../shared/deletion-dialog/deletion-message'
|
|||||||
import { ConfigurationAuthComponent } from './auth/config-auth.component';
|
import { ConfigurationAuthComponent } from './auth/config-auth.component';
|
||||||
import { ConfigurationEmailComponent } from './email/config-email.component';
|
import { ConfigurationEmailComponent } from './email/config-email.component';
|
||||||
|
|
||||||
|
import { AppConfigService } from '../app-config.service';
|
||||||
|
|
||||||
const fakePass = "fakepassword";
|
const fakePass = "fakepassword";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -28,6 +30,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
private currentTabId: string = "";
|
private currentTabId: string = "";
|
||||||
private originalCopy: Configuration;
|
private originalCopy: Configuration;
|
||||||
private confirmSub: Subscription;
|
private confirmSub: Subscription;
|
||||||
|
private testingOnGoing: boolean = false;
|
||||||
|
|
||||||
@ViewChild("repoConfigFrom") repoConfigForm: NgForm;
|
@ViewChild("repoConfigFrom") repoConfigForm: NgForm;
|
||||||
@ViewChild("systemConfigFrom") systemConfigForm: NgForm;
|
@ViewChild("systemConfigFrom") systemConfigForm: NgForm;
|
||||||
@ -37,7 +40,8 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private msgService: MessageService,
|
private msgService: MessageService,
|
||||||
private configService: ConfigurationService,
|
private configService: ConfigurationService,
|
||||||
private confirmService: DeletionDialogService) { }
|
private confirmService: DeletionDialogService,
|
||||||
|
private appConfigService: AppConfigService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
//First load
|
//First load
|
||||||
@ -58,6 +62,10 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
return this.onGoing;
|
return this.onGoing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get testingInProgress(): boolean {
|
||||||
|
return this.testingOnGoing;
|
||||||
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
public isValid(): boolean {
|
||||||
return this.repoConfigForm &&
|
return this.repoConfigForm &&
|
||||||
this.repoConfigForm.valid &&
|
this.repoConfigForm.valid &&
|
||||||
@ -82,6 +90,16 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
return this.currentTabId === 'config-email';
|
return this.currentTabId === 'config-email';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get showLdapServerBtn(): boolean {
|
||||||
|
return this.currentTabId === 'config-auth' &&
|
||||||
|
this.allConfig.auth_mode &&
|
||||||
|
this.allConfig.auth_mode.value === "ldap_auth";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLDAPConfigValid(): boolean {
|
||||||
|
return this.authConfig && this.authConfig.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
public tabLinkChanged(tabLink: any) {
|
public tabLinkChanged(tabLink: any) {
|
||||||
this.currentTabId = tabLink.id;
|
this.currentTabId = tabLink.id;
|
||||||
}
|
}
|
||||||
@ -105,6 +123,10 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
//or force refresh by calling service.
|
//or force refresh by calling service.
|
||||||
//HERE we choose force way
|
//HERE we choose force way
|
||||||
this.retrieveConfig();
|
this.retrieveConfig();
|
||||||
|
|
||||||
|
//Reload bootstrap option
|
||||||
|
this.appConfigService.load().catch(error=> console.error("Failed to reload bootstrap option with error: ", error));
|
||||||
|
|
||||||
this.msgService.announceMessage(response.status, "CONFIG.SAVE_SUCCESS", AlertType.SUCCESS);
|
this.msgService.announceMessage(response.status, "CONFIG.SAVE_SUCCESS", AlertType.SUCCESS);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -150,7 +172,46 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
* @memberOf ConfigurationComponent
|
* @memberOf ConfigurationComponent
|
||||||
*/
|
*/
|
||||||
public testMailServer(): void {
|
public testMailServer(): void {
|
||||||
|
let mailSettings = {};
|
||||||
|
let allChanges = this.getChanges();
|
||||||
|
for (let prop in allChanges) {
|
||||||
|
if (prop.startsWith("email_")) {
|
||||||
|
mailSettings[prop] = allChanges[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.testingOnGoing = true;
|
||||||
|
this.configService.testMailServer(mailSettings)
|
||||||
|
.then(response => {
|
||||||
|
this.testingOnGoing = false;
|
||||||
|
this.msgService.announceMessage(200, "CONFIG.TEST_MAIL_SUCCESS", AlertType.SUCCESS);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.testingOnGoing = false;
|
||||||
|
this.msgService.announceMessage(error.status, errorHandler(error), AlertType.WARNING);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public testLDAPServer(): void {
|
||||||
|
let ldapSettings = {};
|
||||||
|
let allChanges = this.getChanges();
|
||||||
|
for (let prop in allChanges) {
|
||||||
|
if (prop.startsWith("ldap_")) {
|
||||||
|
ldapSettings[prop] = allChanges[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info(ldapSettings);
|
||||||
|
this.testingOnGoing = true;
|
||||||
|
this.configService.testLDAPServer(ldapSettings)
|
||||||
|
.then(respone => {
|
||||||
|
this.testingOnGoing = false;
|
||||||
|
this.msgService.announceMessage(200, "CONFIG.TEST_LDAP_SUCCESS", AlertType.SUCCESS);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.testingOnGoing = false;
|
||||||
|
this.msgService.announceMessage(error.status, errorHandler(error), AlertType.WARNING);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private retrieveConfig(): void {
|
private retrieveConfig(): void {
|
||||||
|
@ -5,6 +5,8 @@ import 'rxjs/add/operator/toPromise';
|
|||||||
import { Configuration } from './config';
|
import { Configuration } from './config';
|
||||||
|
|
||||||
const configEndpoint = "/api/configurations";
|
const configEndpoint = "/api/configurations";
|
||||||
|
const emailEndpoint = "/api/email/ping";
|
||||||
|
const ldapEndpoint = "/api/ldap/ping";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConfigurationService {
|
export class ConfigurationService {
|
||||||
@ -30,4 +32,18 @@ export class ConfigurationService {
|
|||||||
.then(response => response)
|
.then(response => response)
|
||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public testMailServer(mailSettings: any): Promise<any> {
|
||||||
|
return this.http.post(emailEndpoint, JSON.stringify(mailSettings), this.options)
|
||||||
|
.toPromise()
|
||||||
|
.then(response => response)
|
||||||
|
.catch(error => Promise.reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
public testLDAPServer(ldapSettings: any): Promise<any> {
|
||||||
|
return this.http.post(ldapEndpoint, JSON.stringify(ldapSettings), this.options)
|
||||||
|
.toPromise()
|
||||||
|
.then(response => response)
|
||||||
|
.catch(error => Promise.reject(error));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
<label for="emailUsername">{{'CONFIG.MAIL_USERNAME' | translate}}</label>
|
<label for="emailUsername">{{'CONFIG.MAIL_USERNAME' | translate}}</label>
|
||||||
<label for="emailUsername" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="false">
|
<label for="emailUsername" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="false">
|
||||||
<input name="emailUsername" type="text" #emailUsernameInput="ngModel" [(ngModel)]="currentConfig.email_username.value"
|
<input name="emailUsername" type="text" #emailUsernameInput="ngModel" [(ngModel)]="currentConfig.email_username.value"
|
||||||
required
|
|
||||||
id="emailUsername"
|
id="emailUsername"
|
||||||
size="40" [disabled]="disabled(currentConfig.email_username)">
|
size="40" [disabled]="disabled(currentConfig.email_username)">
|
||||||
<span class="tooltip-content">
|
<span class="tooltip-content">
|
||||||
@ -41,7 +40,6 @@
|
|||||||
<label for="emailPassword">{{'CONFIG.MAIL_PASSWORD' | translate}}</label>
|
<label for="emailPassword">{{'CONFIG.MAIL_PASSWORD' | translate}}</label>
|
||||||
<label for="emailPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="false">
|
<label for="emailPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="false">
|
||||||
<input name="emailPassword" type="password" #emailPasswordInput="ngModel" [(ngModel)]="currentConfig.email_password.value"
|
<input name="emailPassword" type="password" #emailPasswordInput="ngModel" [(ngModel)]="currentConfig.email_password.value"
|
||||||
required
|
|
||||||
id="emailPassword"
|
id="emailPassword"
|
||||||
size="40" [disabled]="disabled(currentConfig.email_password)">
|
size="40" [disabled]="disabled(currentConfig.email_password)">
|
||||||
<span class="tooltip-content">
|
<span class="tooltip-content">
|
||||||
|
@ -36,35 +36,35 @@ const harborRoutes: Routes = [
|
|||||||
{ path: '', redirectTo: '/harbor/dashboard', pathMatch: 'full' },
|
{ path: '', redirectTo: '/harbor/dashboard', pathMatch: 'full' },
|
||||||
{ path: 'harbor', redirectTo: '/harbor/dashboard', pathMatch: 'full' },
|
{ path: 'harbor', redirectTo: '/harbor/dashboard', pathMatch: 'full' },
|
||||||
{ path: 'sign-in', component: SignInComponent, canActivate: [SignInGuard] },
|
{ path: 'sign-in', component: SignInComponent, canActivate: [SignInGuard] },
|
||||||
{ path: 'sign-up', component: SignUpComponent},
|
{ path: 'sign-up', component: SignUpComponent },
|
||||||
{ path: 'reset_password', component: ResetPasswordComponent},
|
{ path: 'password-reset', component: ResetPasswordComponent },
|
||||||
{
|
{
|
||||||
path: 'harbor',
|
path: 'harbor',
|
||||||
component: HarborShellComponent,
|
component: HarborShellComponent,
|
||||||
canActivateChild: [AuthCheckGuard],
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{ path: 'sign-in', component: SignInComponent, canActivate: [SignInGuard] },
|
||||||
path: 'dashboard',
|
{ path: 'sign-up', component: SignUpComponent },
|
||||||
component: StartPageComponent
|
{ path: 'dashboard', component: StartPageComponent },
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'projects',
|
path: 'projects',
|
||||||
component: ProjectComponent
|
component: ProjectComponent,
|
||||||
|
canActivate: [AuthCheckGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'logs',
|
path: 'logs',
|
||||||
component: RecentLogComponent
|
component: RecentLogComponent,
|
||||||
|
canActivate: [AuthCheckGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'users',
|
path: 'users',
|
||||||
component: UserComponent,
|
component: UserComponent,
|
||||||
canActivate: [SystemAdminGuard]
|
canActivate: [AuthCheckGuard, SystemAdminGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'replications',
|
path: 'replications',
|
||||||
component: ReplicationManagementComponent,
|
component: ReplicationManagementComponent,
|
||||||
canActivate: [SystemAdminGuard],
|
canActivate: [AuthCheckGuard, SystemAdminGuard],
|
||||||
canActivateChild: [SystemAdminGuard],
|
canActivateChild: [AuthCheckGuard, SystemAdminGuard],
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'rules',
|
path: 'rules',
|
||||||
@ -78,11 +78,14 @@ const harborRoutes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/:id/:repo',
|
path: 'tags/:id/:repo',
|
||||||
component: TagRepositoryComponent
|
component: TagRepositoryComponent,
|
||||||
|
canActivate: [AuthCheckGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'projects/:id',
|
path: 'projects/:id',
|
||||||
component: ProjectDetailComponent,
|
component: ProjectDetailComponent,
|
||||||
|
canActivate: [AuthCheckGuard],
|
||||||
|
canActivateChild: [AuthCheckGuard],
|
||||||
resolve: {
|
resolve: {
|
||||||
projectResolver: ProjectRoutingResolver
|
projectResolver: ProjectRoutingResolver
|
||||||
},
|
},
|
||||||
@ -108,11 +111,11 @@ const harborRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'configs',
|
path: 'configs',
|
||||||
component: ConfigurationComponent,
|
component: ConfigurationComponent,
|
||||||
canActivate: [SystemAdminGuard],
|
canActivate: [AuthCheckGuard, SystemAdminGuard],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ path: "**", component: PageNotFoundComponent}
|
{ path: "**", component: PageNotFoundComponent }
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -53,7 +53,7 @@ export class AuditLogComponent implements OnInit {
|
|||||||
];
|
];
|
||||||
|
|
||||||
pageOffset: number = 1;
|
pageOffset: number = 1;
|
||||||
pageSize: number = 2;
|
pageSize: number = 15;
|
||||||
totalRecordCount: number;
|
totalRecordCount: number;
|
||||||
totalPage: number;
|
totalPage: number;
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export class ProjectComponent implements OnInit {
|
|||||||
isPublic: number;
|
isPublic: number;
|
||||||
|
|
||||||
page: number = 1;
|
page: number = 1;
|
||||||
pageSize: number = 3;
|
pageSize: number = 15;
|
||||||
|
|
||||||
totalPage: number;
|
totalPage: number;
|
||||||
totalRecordCount: number;
|
totalRecordCount: number;
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import { VerifiedSignature } from './verified-signature';
|
|
||||||
|
|
||||||
export const verifiedSignatures: VerifiedSignature[] = [
|
|
||||||
{
|
|
||||||
"tag": "latest",
|
|
||||||
"hashes": {
|
|
||||||
"sha256": "E1lggRW5RZnlZBY4usWu8d36p5u5YFfr9B68jTOs+Kc="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
@ -5,8 +5,6 @@ import { Repository } from './repository';
|
|||||||
import { Tag } from './tag';
|
import { Tag } from './tag';
|
||||||
import { VerifiedSignature } from './verified-signature';
|
import { VerifiedSignature } from './verified-signature';
|
||||||
|
|
||||||
import { verifiedSignatures } from './mock-verfied-signature';
|
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable'
|
import { Observable } from 'rxjs/Observable'
|
||||||
import 'rxjs/add/observable/of';
|
import 'rxjs/add/observable/of';
|
||||||
import 'rxjs/add/operator/mergeMap';
|
import 'rxjs/add/operator/mergeMap';
|
||||||
@ -43,25 +41,23 @@ export class RepositoryService {
|
|||||||
|
|
||||||
listTagsWithVerifiedSignatures(repoName: string): Observable<Tag[]> {
|
listTagsWithVerifiedSignatures(repoName: string): Observable<Tag[]> {
|
||||||
return this.http
|
return this.http
|
||||||
.get(`/api/repositories/tags?repo_name=${repoName}&detail=1`)
|
.get(`/api/repositories/signatures?repo_name=${repoName}`)
|
||||||
.map(response=>response.json())
|
.map(response=>response)
|
||||||
.catch(error=>Observable.throw(error))
|
.flatMap(res=>
|
||||||
.flatMap((tags: Tag[])=>
|
this.listTags(repoName)
|
||||||
this.http
|
.map((tags: Tag[])=>{
|
||||||
.get(`/api/repositories/signatures?repo_name=${repoName}`)
|
let signatures = res.json();
|
||||||
.map(res=>{
|
tags.forEach(t=>{
|
||||||
let signatures = res.json();
|
for(let i = 0; i < signatures.length; i++) {
|
||||||
tags.forEach(t=>{
|
if(signatures[i].tag === t.tag) {
|
||||||
for(let i = 0; i < signatures.length; i++) {
|
t.verified = true;
|
||||||
if(signatures[i].tag === t.tag) {
|
break;
|
||||||
t.verified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return tags;
|
});
|
||||||
})
|
return tags;
|
||||||
.catch(error=>Observable.throw(error))
|
})
|
||||||
|
.catch(error=>Observable.throw(error))
|
||||||
)
|
)
|
||||||
.catch(error=>Observable.throw(error));
|
.catch(error=>Observable.throw(error));
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.os}}
|
<clr-dg-cell>{{t.os}}
|
||||||
<harbor-action-overflow>
|
<harbor-action-overflow>
|
||||||
<a href="javascript:void(0)" class="dropdown-item" (click)="deleteTag(t.tag)">{{'REPOSITORY.DELETE' | translate}}</a>
|
<a href="javascript:void(0)" class="dropdown-item" (click)="deleteTag(t)">{{'REPOSITORY.DELETE' | translate}}</a>
|
||||||
</harbor-action-overflow>
|
</harbor-action-overflow>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
|
@ -32,16 +32,24 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
private repositoryService: RepositoryService) {
|
private repositoryService: RepositoryService) {
|
||||||
this.subscription = this.deletionDialogService.deletionConfirm$.subscribe(
|
this.subscription = this.deletionDialogService.deletionConfirm$.subscribe(
|
||||||
message=>{
|
message=>{
|
||||||
let tagName = message.data;
|
let tag = message.data;
|
||||||
this.repositoryService
|
if(tag) {
|
||||||
.deleteRepoByTag(this.repoName, tagName)
|
if(tag.verified) {
|
||||||
.subscribe(
|
return;
|
||||||
response=>{
|
} else {
|
||||||
this.retrieve();
|
let tagName = tag.tag;
|
||||||
console.log('Deleted repo:' + this.repoName + ' with tag:' + tagName);
|
this.repositoryService
|
||||||
},
|
.deleteRepoByTag(this.repoName, tagName)
|
||||||
error=>this.messageService.announceMessage(error.status, 'Failed to delete tag:' + tagName + ' under repo:' + this.repoName, AlertType.DANGER)
|
.subscribe(
|
||||||
);
|
response=>{
|
||||||
|
this.retrieve();
|
||||||
|
console.log('Deleted repo:' + this.repoName + ' with tag:' + tagName);
|
||||||
|
},
|
||||||
|
error=>this.messageService.announceMessage(error.status, 'Failed to delete tag:' + tagName + ' under repo:' + this.repoName, AlertType.DANGER)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -58,8 +66,9 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieve() {
|
retrieve() {
|
||||||
|
this.tags = [];
|
||||||
this.repositoryService
|
this.repositoryService
|
||||||
.listTagsWithVerifiedSignatures(this.repoName)
|
.listTagsWithVerifiedSignatures(this.repoName)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
@ -81,11 +90,19 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
error=>this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
error=>this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTag(tagName: string) {
|
deleteTag(tag: TagView) {
|
||||||
let message = new DeletionMessage(
|
if(tag) {
|
||||||
'REPOSITORY.DELETION_TITLE_TAG', 'REPOSITORY.DELETION_SUMMARY_TAG',
|
let titleKey: string, summaryKey: string;
|
||||||
tagName, tagName, DeletionTargets.TAG);
|
if (tag.verified) {
|
||||||
this.deletionDialogService.openComfirmDialog(message);
|
titleKey = 'REPOSITORY.DELETION_TITLE_TAG_DENIED';
|
||||||
|
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG_DENIED';
|
||||||
|
} else {
|
||||||
|
titleKey = 'REPOSITORY.DELETION_TITLE_TAG';
|
||||||
|
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG';
|
||||||
|
}
|
||||||
|
let message = new DeletionMessage(titleKey, summaryKey, tag.tag, tag, DeletionTargets.TAG);
|
||||||
|
this.deletionDialogService.openComfirmDialog(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2 class="custom-h2">{{'SIDE_NAV.SYSTEM_MGMT.USERS' | translate}}</h2>
|
<h2 class="custom-h2">{{'SIDE_NAV.SYSTEM_MGMT.USER' | translate}}</h2>
|
||||||
<div class="action-panel-pos">
|
<div class="action-panel-pos">
|
||||||
<span>
|
<span>
|
||||||
<clr-icon shape="plus" class="is-highlight" size="24"></clr-icon>
|
<clr-icon shape="plus" class="is-highlight" size="24"></clr-icon>
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Clarity Seed App</title>
|
<title>Harbor</title>
|
||||||
<base href="/ng">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2">
|
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<harbor-app>Loading...</harbor-app>
|
<harbor-app>Loading...</harbor-app>
|
||||||
<script type="text/javascript" src="/ng/inline.bundle.js"></script><script type="text/javascript" src="/ng/scripts.bundle.js"></script><script type="text/javascript" src="/ng/styles.bundle.js"></script><script type="text/javascript" src="/ng/vendor.bundle.js"></script><script type="text/javascript" src="/ng/main.bundle.js"></script></body>
|
</body>
|
||||||
</html>
|
|
||||||
|
</html>
|
@ -2,7 +2,8 @@
|
|||||||
"SIGN_IN": {
|
"SIGN_IN": {
|
||||||
"REMEMBER": "Remember me",
|
"REMEMBER": "Remember me",
|
||||||
"INVALID_MSG": "Invalid user name or password",
|
"INVALID_MSG": "Invalid user name or password",
|
||||||
"FORGOT_PWD": "Forgot password"
|
"FORGOT_PWD": "Forgot password",
|
||||||
|
"HEADER_LINK": "Sign In"
|
||||||
},
|
},
|
||||||
"SIGN_UP": {
|
"SIGN_UP": {
|
||||||
"TITLE": "Sign Up"
|
"TITLE": "Sign Up"
|
||||||
@ -18,7 +19,8 @@
|
|||||||
"SEND": "SEND",
|
"SEND": "SEND",
|
||||||
"SAVE": "SAVE",
|
"SAVE": "SAVE",
|
||||||
"TEST_MAIL": "TEST MAIL SERVER",
|
"TEST_MAIL": "TEST MAIL SERVER",
|
||||||
"CLOSE": "CLOSE"
|
"CLOSE": "CLOSE",
|
||||||
|
"TEST_LDAP": "TEST LDAP SERVER"
|
||||||
},
|
},
|
||||||
"TOOLTIP": {
|
"TOOLTIP": {
|
||||||
"EMAIL": "Email should be a valid email address like name@example.com",
|
"EMAIL": "Email should be a valid email address like name@example.com",
|
||||||
@ -256,6 +258,8 @@
|
|||||||
"DELETION_SUMMARY_REPO": "Do you want to delete repository {{param}}?",
|
"DELETION_SUMMARY_REPO": "Do you want to delete repository {{param}}?",
|
||||||
"DELETION_TITLE_TAG": "Confirm Tag Deletion",
|
"DELETION_TITLE_TAG": "Confirm Tag Deletion",
|
||||||
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
|
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
|
||||||
|
"DELETION_TITLE_TAG_DENIED": "Signed Tag can't be deleted",
|
||||||
|
"DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from Notary before they can be deleted.",
|
||||||
"FILTER_FOR_REPOSITORIES": "Filter for repositories",
|
"FILTER_FOR_REPOSITORIES": "Filter for repositories",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"VERIFIED": "Verified",
|
"VERIFIED": "Verified",
|
||||||
@ -330,7 +334,9 @@
|
|||||||
"FILTER": "LDAP Filter",
|
"FILTER": "LDAP Filter",
|
||||||
"UID": "LDAP UID",
|
"UID": "LDAP UID",
|
||||||
"SCOPE": "lDAP Scope"
|
"SCOPE": "lDAP Scope"
|
||||||
}
|
},
|
||||||
|
"TEST_MAIL_SUCCESS": "Connection to mail server is verified",
|
||||||
|
"TEST_LDAP_SUCCESS": "Connection to ldap server is verified"
|
||||||
},
|
},
|
||||||
"PAGE_NOT_FOUND": {
|
"PAGE_NOT_FOUND": {
|
||||||
"MAIN_TITLE": "Page not found",
|
"MAIN_TITLE": "Page not found",
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
"SIGN_IN": {
|
"SIGN_IN": {
|
||||||
"REMEMBER": "记住我",
|
"REMEMBER": "记住我",
|
||||||
"INVALID_MSG": "用户名或者密码不正确",
|
"INVALID_MSG": "用户名或者密码不正确",
|
||||||
"FORGOT_PWD": "忘记密码"
|
"FORGOT_PWD": "忘记密码",
|
||||||
|
"HEADER_LINK": "登录"
|
||||||
},
|
},
|
||||||
"SIGN_UP": {
|
"SIGN_UP": {
|
||||||
"TITLE": "注册"
|
"TITLE": "注册"
|
||||||
@ -18,7 +19,8 @@
|
|||||||
"SEND": "发送",
|
"SEND": "发送",
|
||||||
"SAVE": "保存",
|
"SAVE": "保存",
|
||||||
"TEST_MAIL": "测试邮件服务器",
|
"TEST_MAIL": "测试邮件服务器",
|
||||||
"CLOSE": "关闭"
|
"CLOSE": "关闭",
|
||||||
|
"TEST_LDAP": "测试LDAP服务器"
|
||||||
},
|
},
|
||||||
"TOOLTIP": {
|
"TOOLTIP": {
|
||||||
"EMAIL": "请使用正确的邮箱地址,比如name@example.com",
|
"EMAIL": "请使用正确的邮箱地址,比如name@example.com",
|
||||||
@ -256,6 +258,8 @@
|
|||||||
"DELETION_SUMMARY_REPO": "确认删除镜像仓库 {{param}}?",
|
"DELETION_SUMMARY_REPO": "确认删除镜像仓库 {{param}}?",
|
||||||
"DELETION_TITLE_TAG": "删除镜像标签确认",
|
"DELETION_TITLE_TAG": "删除镜像标签确认",
|
||||||
"DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?",
|
"DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?",
|
||||||
|
"DELETION_TITLE_TAG_DENIED": "已签名的镜像不能被删除",
|
||||||
|
"DELETION_SUMMARY_TAG_DENIED": "要删除此镜像标签必须首先从Notary中删除。",
|
||||||
"FILTER_FOR_REPOSITORIES": "过滤镜像仓库",
|
"FILTER_FOR_REPOSITORIES": "过滤镜像仓库",
|
||||||
"TAG": "标签",
|
"TAG": "标签",
|
||||||
"VERIFIED": "已验证",
|
"VERIFIED": "已验证",
|
||||||
@ -330,7 +334,9 @@
|
|||||||
"FILTER": "LDAP过滤器",
|
"FILTER": "LDAP过滤器",
|
||||||
"UID": "LDAP用户标识(UID)",
|
"UID": "LDAP用户标识(UID)",
|
||||||
"SCOPE": "lDAP范围"
|
"SCOPE": "lDAP范围"
|
||||||
}
|
},
|
||||||
|
"TEST_MAIL_SUCCESS": "邮件服务器的连通正常",
|
||||||
|
"TEST_LDAP_SUCCESS": "LDAP服务器的连通正常"
|
||||||
},
|
},
|
||||||
"PAGE_NOT_FOUND": {
|
"PAGE_NOT_FOUND": {
|
||||||
"MAIN_TITLE": "页面不存在",
|
"MAIN_TITLE": "页面不存在",
|
||||||
|
Loading…
Reference in New Issue
Block a user