mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 10:15:35 +01:00
Improve global search component (#15462)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
a8562b2934
commit
eca3d82d9c
@ -1,5 +1,5 @@
|
||||
<form class="search">
|
||||
<label for="search_input">
|
||||
<input #globalSearchBox name="globalSearchBox" [(ngModel)]="searchTerm" id="search_input" type="text" (keyup)="search(globalSearchBox.value)" placeholder='{{placeholderText | translate}}'>
|
||||
<input autocomplete="off" #globalSearchBox name="globalSearchBox" [(ngModel)]="searchTerm" id="search_input" type="text" (keyup)="search(globalSearchBox.value)" placeholder='{{placeholderText | translate}}'>
|
||||
</label>
|
||||
</form>
|
||||
</form>
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
@ -13,20 +12,17 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subject , Subscription } from "rxjs";
|
||||
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subject, Subscription } from "rxjs";
|
||||
import { SearchTriggerService } from './search-trigger.service';
|
||||
|
||||
import { AppConfigService } from '../../../services/app-config.service';
|
||||
|
||||
|
||||
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {SkinableConfig} from "../../../services/skinable-config.service";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { SkinableConfig } from "../../../services/skinable-config.service";
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
const deBounceTime = 500; // ms
|
||||
const SEARCH_KEY: string = 'globalSearch';
|
||||
|
||||
@Component({
|
||||
selector: 'global-search',
|
||||
@ -43,13 +39,13 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
// To indicate if the result panel is opened
|
||||
isResPanelOpened: boolean = false;
|
||||
searchTerm: string = "";
|
||||
|
||||
placeholderText: string;
|
||||
|
||||
private _searchTerm = "";
|
||||
constructor(
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private location: Location,
|
||||
private appConfigService: AppConfigService,
|
||||
private translate: TranslateService,
|
||||
private skinableConfig: SkinableConfig) {
|
||||
@ -71,8 +67,7 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.searchSub = this.searchTerms.pipe(
|
||||
debounceTime(deBounceTime),
|
||||
distinctUntilChanged())
|
||||
debounceTime(deBounceTime))
|
||||
.subscribe(term => {
|
||||
this.searchTrigger.triggerSearch(term);
|
||||
});
|
||||
@ -83,6 +78,11 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
if (this.appConfigService.isIntegrationMode()) {
|
||||
this.placeholderText = "GLOBAL_SEARCH.PLACEHOLDER_VIC";
|
||||
}
|
||||
// init _searchTerm from queryParams
|
||||
this._searchTerm = this.activatedRoute.snapshot.queryParams[SEARCH_KEY];
|
||||
if (this._searchTerm) {
|
||||
this.searchTerms.next(this._searchTerm);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@ -98,7 +98,24 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
// Handle the term inputting event
|
||||
search(term: string): void {
|
||||
// Send event even term is empty
|
||||
|
||||
this.searchTerms.next(term.trim());
|
||||
}
|
||||
get searchTerm(): string {
|
||||
return this._searchTerm;
|
||||
}
|
||||
set searchTerm(s) {
|
||||
let url: string;
|
||||
if (s) {
|
||||
url = this.router.createUrlTree([], {
|
||||
relativeTo: this.activatedRoute,
|
||||
queryParams: {[SEARCH_KEY]: s}
|
||||
}).toString();
|
||||
} else {
|
||||
url = this.router.createUrlTree([], {
|
||||
relativeTo: this.activatedRoute,
|
||||
}).toString();
|
||||
}
|
||||
this.location.replaceState(url);
|
||||
this._searchTerm = s;
|
||||
}
|
||||
}
|
||||
|
@ -22,4 +22,4 @@
|
||||
{{charts?.length}} {{'HELM_CHART.ITEMS'| translate}}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</clr-datagrid>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Router } from '@angular/router';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { HelmChartSearchResultItem, HelmChartVersion, HelmChartMaintainer } from '../../../base/project/helm-chart/helm-chart-detail/helm-chart.interface.service';
|
||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||
import { ProjectService } from "../../services";
|
||||
@ -7,11 +7,10 @@ import { ProjectService } from "../../services";
|
||||
|
||||
@Component({
|
||||
selector: 'list-chart-version-ro',
|
||||
templateUrl: './list-chart-version-ro.component.html'
|
||||
templateUrl: './list-chart-version-ro.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ListChartVersionRoComponent implements OnInit {
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() charts: HelmChartSearchResultItem[];
|
||||
|
||||
constructor(
|
||||
|
@ -1,16 +1,17 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||
<clr-datagrid>
|
||||
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *clrDgItems="let p of projects" [clrDgItem]="p">
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="goToLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell><a href="javascript:void(0)" [routerLink]="getLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{ (p.metadata.public === 'true' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.creation_time | harborDatetime: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-placeholder>{{'PROJECT.NO_PROJECT' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="projects?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'PROJECT.OF' | translate}} </span> {{projects?.length}} {{'PROJECT.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</clr-datagrid>
|
||||
|
@ -1,47 +1,50 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { ListProjectROComponent } from './list-project-ro.component';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ClarityModule } from '@clr/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||
import { SharedTestingModule } from "../../shared.module";
|
||||
import { Project } from "../../../../../ng-swagger-gen/models/project";
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// mock a TestHostComponent for ListProjectROComponent
|
||||
@Component({
|
||||
template: `
|
||||
<list-project-ro [projects]="projects">
|
||||
</list-project-ro>`
|
||||
})
|
||||
class TestHostComponent {
|
||||
projects: Project[] = [];
|
||||
}
|
||||
|
||||
describe('ListProjectROComponent', () => {
|
||||
let component: ListProjectROComponent;
|
||||
let fixture: ComponentFixture<ListProjectROComponent>;
|
||||
const mockSearchTriggerService = {
|
||||
closeSearch: () => { }
|
||||
};
|
||||
let component: TestHostComponent;
|
||||
let fixture: ComponentFixture<TestHostComponent>;
|
||||
const mockedProjects: Project[] = [
|
||||
{
|
||||
chart_count: 0,
|
||||
name: "test1",
|
||||
metadata: {},
|
||||
project_id: 1,
|
||||
repo_count: 1
|
||||
},
|
||||
{
|
||||
chart_count: 0,
|
||||
name: "test2",
|
||||
metadata: {},
|
||||
project_id: 2,
|
||||
repo_count: 1
|
||||
}
|
||||
];
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
ClarityModule,
|
||||
TranslateModule.forRoot(),
|
||||
FormsModule,
|
||||
RouterTestingModule,
|
||||
NoopAnimationsModule,
|
||||
HttpClientTestingModule
|
||||
SharedTestingModule
|
||||
],
|
||||
declarations: [ListProjectROComponent],
|
||||
providers: [
|
||||
TranslateService,
|
||||
{ provide: SearchTriggerService, useValue: mockSearchTriggerService }
|
||||
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
declarations: [ListProjectROComponent,
|
||||
TestHostComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListProjectROComponent);
|
||||
fixture = TestBed.createComponent(TestHostComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@ -49,4 +52,12 @@ describe('ListProjectROComponent', () => {
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render project list', async () => {
|
||||
component.projects = mockedProjects;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
|
||||
expect(rows.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
@ -11,35 +11,21 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, EventEmitter, Output, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { State } from '../../services/interface';
|
||||
|
||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||
import { Project } from '../../../base/project/project';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Project } from "../../../../../ng-swagger-gen/models/project";
|
||||
|
||||
@Component({
|
||||
selector: 'list-project-ro',
|
||||
templateUrl: 'list-project-ro.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
selector: 'list-project-ro',
|
||||
templateUrl: 'list-project-ro.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ListProjectROComponent {
|
||||
@Input() projects: Project[];
|
||||
@Output() paginate = new EventEmitter<State>();
|
||||
@Input() projects: Project[];
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
constructor(
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private router: Router) {}
|
||||
|
||||
goToLink(proId: number): void {
|
||||
this.searchTrigger.closeSearch(true);
|
||||
|
||||
let linkUrl = ['harbor', 'projects', proId, 'repositories'];
|
||||
this.router.navigate(linkUrl);
|
||||
}
|
||||
|
||||
refresh(state: State) {
|
||||
this.paginate.emit(state);
|
||||
}
|
||||
getLink(proId: number) {
|
||||
return `/harbor/projects/${proId}/repositories`;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||
<clr-datagrid>
|
||||
<clr-dg-column>{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *clrDgItems="let r of repositories" [clrDgItem]='r'>
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="gotoLink(projectId || r.project_id, r.name || r.repository_name)">{{r.name || r.repository_name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell><a href="javascript:void(0)" [routerLink]="getLink(r.project_id, r.name || r.repository_name)" [queryParams]="getQueryParams()">{{r.name || r.repository_name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{r.artifact_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="repositories?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPOSITORY.OF' | translate}} </span> {{repositories?.length }} {{'REPOSITORY.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</clr-datagrid>
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
import {filter} from 'rxjs/operators';
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -13,72 +11,39 @@ import {filter} from 'rxjs/operators';
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, Input, Output, OnDestroy, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core';
|
||||
import { Router, NavigationEnd } from '@angular/router';
|
||||
import { State } from '../../services/interface';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Repository } from '../../../../../ng-swagger-gen/models/repository';
|
||||
|
||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||
import {Subscription} from "rxjs";
|
||||
import { SessionService } from "../../services/session.service";
|
||||
import { UN_LOGGED_PARAM } from "../../../account/sign-in/sign-in.service";
|
||||
|
||||
const YES: string = 'yes';
|
||||
|
||||
@Component({
|
||||
selector: 'list-repository-ro',
|
||||
templateUrl: 'list-repository-ro.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ListRepositoryROComponent implements OnInit, OnDestroy {
|
||||
export class ListRepositoryROComponent {
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() repositories: Repository[];
|
||||
|
||||
@Output() paginate = new EventEmitter<State>();
|
||||
|
||||
routerSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private sessionService: SessionService) {
|
||||
this.router.routeReuseStrategy.shouldReuseRoute = function() {
|
||||
return false;
|
||||
};
|
||||
this.routerSubscription = this.router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe((event) => {
|
||||
// trick the Router into believing it's last link wasn't previously loaded
|
||||
this.router.navigated = false;
|
||||
// if you need to scroll back to top, here is the right place
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
private router: Router,
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private sessionService: SessionService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.routerSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
refresh(state: State) {
|
||||
if (this.repositories) {
|
||||
this.paginate.emit(state);
|
||||
}
|
||||
}
|
||||
|
||||
public gotoLink(projectId: number, repoName: string): void {
|
||||
this.searchTrigger.closeSearch(true);
|
||||
getLink(projectId: number, repoName: string) {
|
||||
let projectName = repoName.split('/')[0];
|
||||
let repositorieName = projectName ? repoName.substr(projectName.length + 1) : repoName;
|
||||
let linkUrl = ['harbor', 'projects', projectId, 'repositories', repositorieName ];
|
||||
if (this.sessionService.getCurrentUser()) {
|
||||
this.router.navigate(linkUrl);
|
||||
} else {// if not logged in and it's a public project, add param 'publicAndNotLogged'
|
||||
this.router.navigate(linkUrl, {queryParams: {[UN_LOGGED_PARAM]: YES}});
|
||||
}
|
||||
return `/harbor/projects/${projectId}/repositories/${repositorieName}`;
|
||||
}
|
||||
getQueryParams() {
|
||||
if (this.sessionService.getCurrentUser()) {
|
||||
return null;
|
||||
}
|
||||
return {[UN_LOGGED_PARAM]: YES};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -254,7 +254,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Die Aktivierung der Funktion erlaubt es dem Projekt, als Cache für eine andere Registry Instanz zu dienen. Harbor unterstützt die Proxy Funktion nur für DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay und Google GCR.",
|
||||
"ENDPOINT": "Endpunkt",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpunkt"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpunkt",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "Zusammenfassung",
|
||||
|
@ -254,7 +254,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "Summary",
|
||||
|
@ -255,7 +255,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "Summary",
|
||||
|
@ -248,7 +248,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "Summary",
|
||||
|
@ -252,7 +252,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "Summary",
|
||||
|
@ -254,7 +254,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "Özet",
|
||||
|
@ -253,7 +253,8 @@
|
||||
"PROXY_CACHE": "镜像代理",
|
||||
"PROXY_CACHE_TOOLTIP": "开启此项,以使得该项目成为目标仓库的镜像代理.仅支持 DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay 和 Google GCR 类型的仓库",
|
||||
"ENDPOINT": "地址",
|
||||
"PROXY_CACHE_ENDPOINT": "镜像代理地址"
|
||||
"PROXY_CACHE_ENDPOINT": "镜像代理地址",
|
||||
"NO_PROJECT": "未发现任何项目"
|
||||
},
|
||||
"PROJECT_DETAIL": {
|
||||
"SUMMARY": "概要",
|
||||
|
@ -251,7 +251,8 @@
|
||||
"PROXY_CACHE": "Proxy Cache",
|
||||
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint"
|
||||
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
|
||||
"NO_PROJECT": "We couldn't find any projects"
|
||||
},
|
||||
"PROJECT_DETAIL":{
|
||||
"SUMMARY": "概要",
|
||||
|
Loading…
Reference in New Issue
Block a user