mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-30 06:03:45 +01:00
Merge pull request #5423 from ninjadq/integrate_chart_view
Feature of helm chart UI
This commit is contained in:
commit
8b7706d191
2
.gitignore
vendored
2
.gitignore
vendored
@ -52,5 +52,3 @@ src/ui_ng/aot/**/*.json
|
|||||||
**/aot
|
**/aot
|
||||||
**/dist
|
**/dist
|
||||||
**/.bin
|
**/.bin
|
||||||
package-lock.json
|
|
||||||
src/ui_ng/package-lock.json
|
|
||||||
|
@ -79,7 +79,7 @@ script:
|
|||||||
- sudo mkdir -p /harbor
|
- sudo mkdir -p /harbor
|
||||||
- sudo mv ./VERSION /harbor/UIVERSION
|
- sudo mv ./VERSION /harbor/UIVERSION
|
||||||
- sudo service postgresql stop
|
- sudo service postgresql stop
|
||||||
- sudo make run_clarity_ut CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0
|
- sudo make run_clarity_ut CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.6.0
|
||||||
- cat ./src/ui_ng/npm-ut-test-results
|
- cat ./src/ui_ng/npm-ut-test-results
|
||||||
- sudo ./tests/testprepare.sh
|
- sudo ./tests/testprepare.sh
|
||||||
- sudo make -f make/photon/Makefile _build_db _build_registry -e VERSIONTAG=dev -e CLAIRDBVERSION=dev -e REGISTRYVERSION=v2.6.2
|
- sudo make -f make/photon/Makefile _build_db _build_registry -e VERSIONTAG=dev -e CLAIRDBVERSION=dev -e REGISTRYVERSION=v2.6.2
|
||||||
@ -105,7 +105,7 @@ script:
|
|||||||
- sudo rm -rf /data/config/*
|
- sudo rm -rf /data/config/*
|
||||||
- sudo rm -rf /data/database/*
|
- sudo rm -rf /data/database/*
|
||||||
- ls /data/cert
|
- ls /data/cert
|
||||||
- sudo make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0 NOTARYFLAG=true CLAIRFLAG=true
|
- sudo make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.6.0 NOTARYFLAG=true CLAIRFLAG=true
|
||||||
- sleep 10
|
- sleep 10
|
||||||
- docker ps
|
- docker ps
|
||||||
- ./tests/validatecontainers.sh
|
- ./tests/validatecontainers.sh
|
||||||
|
@ -50,19 +50,19 @@ You can compile the code by one of the three approaches:
|
|||||||
* Build, install and bring up Harbor without Notary:
|
* Build, install and bring up Harbor without Notary:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0
|
$ make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
* Build, install and bring up Harbor with Notary:
|
* Build, install and bring up Harbor with Notary:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0 NOTARYFLAG=true
|
$ make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.6.0 NOTARYFLAG=true
|
||||||
```
|
```
|
||||||
|
|
||||||
* Build, install and bring up Harbor with Clair:
|
* Build, install and bring up Harbor with Clair:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0 CLAIRFLAG=true
|
$ make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.6.0 CLAIRFLAG=true
|
||||||
```
|
```
|
||||||
|
|
||||||
#### II. Compile code with your own Golang environment, then build Harbor
|
#### II. Compile code with your own Golang environment, then build Harbor
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM node:7.5.0
|
FROM node:10.7.0
|
||||||
|
|
||||||
RUN mkdir -p /harbor_resources
|
RUN mkdir -p /harbor_resources
|
||||||
RUN mkdir -p /harbor_src
|
RUN mkdir -p /harbor_src
|
||||||
@ -7,14 +7,26 @@ COPY src/ui_ng/package.json /harbor_resources
|
|||||||
COPY make/dev/nodeclarity/entrypoint.sh /
|
COPY make/dev/nodeclarity/entrypoint.sh /
|
||||||
|
|
||||||
# Install Chrome
|
# Install Chrome
|
||||||
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
|
RUN apt-get update && apt-get install -y \
|
||||||
RUN echo "deb http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list
|
apt-transport-https \
|
||||||
RUN apt-get update && apt-get -y install google-chrome-stable
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
gnupg \
|
||||||
|
hicolor-icon-theme \
|
||||||
|
libcanberra-gtk* \
|
||||||
|
libgl1-mesa-dri \
|
||||||
|
libgl1-mesa-glx \
|
||||||
|
libpango1.0-0 \
|
||||||
|
libpulse0 \
|
||||||
|
libv4l-0 \
|
||||||
|
--no-install-recommends
|
||||||
|
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
|
RUN dpkg -i google-chrome-stable_current_amd64.deb; apt-get -fy install
|
||||||
|
RUN rm google-chrome-stable_current_amd64.deb
|
||||||
|
|
||||||
# Install npm package
|
# Install npm package
|
||||||
WORKDIR /harbor_resources
|
WORKDIR /harbor_resources
|
||||||
|
|
||||||
RUN npm __proxy__ install -g @angular/cli && \
|
RUN npm __proxy__ install && \
|
||||||
npm __proxy__ install && \
|
|
||||||
chmod u+x /entrypoint.sh
|
chmod u+x /entrypoint.sh
|
||||||
VOLUME ["/harbor_src"]
|
VOLUME ["/harbor_src"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"project": {
|
"project": {
|
||||||
"version": "1.2.0",
|
"version": "1.6.0",
|
||||||
"name": "Harbor"
|
"name": "Harbor"
|
||||||
},
|
},
|
||||||
"apps": [{
|
"apps": [{
|
||||||
@ -19,6 +19,7 @@
|
|||||||
"styles": [
|
"styles": [
|
||||||
"../node_modules/clarity-icons/clarity-icons.min.css",
|
"../node_modules/clarity-icons/clarity-icons.min.css",
|
||||||
"../node_modules/clarity-ui/clarity-ui.min.css",
|
"../node_modules/clarity-ui/clarity-ui.min.css",
|
||||||
|
"../node_modules/prismjs/themes/prism-solarizedlight.css",
|
||||||
"styles.css"
|
"styles.css"
|
||||||
],
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
@ -26,7 +27,10 @@
|
|||||||
"../node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
|
"../node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
|
||||||
"../node_modules/@webcomponents/custom-elements/custom-elements.min.js",
|
"../node_modules/@webcomponents/custom-elements/custom-elements.min.js",
|
||||||
"../node_modules/clarity-icons/clarity-icons.min.js",
|
"../node_modules/clarity-icons/clarity-icons.min.js",
|
||||||
"../node_modules/web-animations-js/web-animations.min.js"
|
"../node_modules/web-animations-js/web-animations.min.js",
|
||||||
|
"../node_modules/marked/lib/marked.js",
|
||||||
|
"../node_modules/prismjs/prism.js",
|
||||||
|
"../node_modules/prismjs/components/prism-yaml.min.js"
|
||||||
],
|
],
|
||||||
"environmentSource": "environments/environment.ts",
|
"environmentSource": "environments/environment.ts",
|
||||||
"environments": {
|
"environments": {
|
||||||
|
@ -26,6 +26,15 @@ fi
|
|||||||
cat ./package.json
|
cat ./package.json
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
## Build harbor-ui and link it
|
||||||
|
rm -rf /harbor_src/ui_ng/lib/dist
|
||||||
|
npm run build:lib
|
||||||
|
chmod -R +xr /harbor_src/ui_ng/lib/dist
|
||||||
|
cd /harbor_src/ui_ng/lib/dist
|
||||||
|
npm link
|
||||||
|
cd /harbor_src/ui_ng
|
||||||
|
npm link harbor-ui
|
||||||
|
|
||||||
./node_modules/.bin/ngc -p tsconfig-aot.json
|
./node_modules/.bin/ngc -p tsconfig-aot.json
|
||||||
sed -i 's/* as//g' src/app/shared/gauge/gauge.component.js
|
sed -i 's/* as//g' src/app/shared/gauge/gauge.component.js
|
||||||
./node_modules/.bin/rollup -c rollup-config.js
|
./node_modules/.bin/rollup -c rollup-config.js
|
||||||
@ -44,3 +53,8 @@ cp ./node_modules/@webcomponents/custom-elements/custom-elements.min.js ../ui/st
|
|||||||
cp ./node_modules/clarity-icons/clarity-icons.min.js ../ui/static/
|
cp ./node_modules/clarity-icons/clarity-icons.min.js ../ui/static/
|
||||||
cp ./node_modules/clarity-ui/clarity-ui.min.css ../ui/static/
|
cp ./node_modules/clarity-ui/clarity-ui.min.css ../ui/static/
|
||||||
cp -r ./node_modules/clarity-icons/shapes/ ../ui/static/
|
cp -r ./node_modules/clarity-icons/shapes/ ../ui/static/
|
||||||
|
|
||||||
|
cp ./node_modules/prismjs/themes/prism-solarizedlight.css ../ui/static/
|
||||||
|
cp ./node_modules/marked/lib/marked.js ../ui/static/
|
||||||
|
cp ./node_modules/prismjs/prism.js ../ui/static/
|
||||||
|
cp ./node_modules/prismjs/components/prism-yaml.min.js ../ui/static/
|
@ -15,13 +15,18 @@
|
|||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
</harbor-app>
|
</harbor-app>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/clarity-ui.min.css">
|
<link rel="stylesheet" href="/static/clarity-ui.min.css">
|
||||||
<link rel="stylesheet" href="/static/clarity-icons.min.css">
|
<link rel="stylesheet" href="/static/clarity-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="/static/prism-solarizedlight.css">
|
||||||
<link rel="stylesheet" href="/static/styles.css">
|
<link rel="stylesheet" href="/static/styles.css">
|
||||||
|
|
||||||
<script src="/static/mutationobserver.min.js"></script>
|
<script src="/static/mutationobserver.min.js"></script>
|
||||||
<script src="/static/custom-elements.min.js"></script>
|
<script src="/static/custom-elements.min.js"></script>
|
||||||
<script src="/static/clarity-icons.min.js"></script>
|
<script src="/static/clarity-icons.min.js"></script>
|
||||||
|
<script src="/static/marked.js"></script>
|
||||||
|
<script src="/static//prism.js"></script>
|
||||||
|
<script src="/static/prism-yaml.min.js"></script>
|
||||||
<script src="/static/build.min.js"></script>
|
<script src="/static/build.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"styles": [
|
"styles": [
|
||||||
"../node_modules/clarity-icons/clarity-icons.min.css",
|
"../node_modules/clarity-icons/clarity-icons.min.css",
|
||||||
"../node_modules/clarity-ui/clarity-ui.min.css",
|
"../node_modules/clarity-ui/clarity-ui.min.css",
|
||||||
|
"../node_modules/prismjs/themes/prism-solarizedlight.css",
|
||||||
"styles.css"
|
"styles.css"
|
||||||
],
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
@ -27,7 +28,10 @@
|
|||||||
"../node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
|
"../node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
|
||||||
"../node_modules/@webcomponents/custom-elements/custom-elements.min.js",
|
"../node_modules/@webcomponents/custom-elements/custom-elements.min.js",
|
||||||
"../node_modules/clarity-icons/clarity-icons.min.js",
|
"../node_modules/clarity-icons/clarity-icons.min.js",
|
||||||
"../node_modules/web-animations-js/web-animations.min.js"
|
"../node_modules/web-animations-js/web-animations.min.js",
|
||||||
|
"../node_modules/marked/lib/marked.js",
|
||||||
|
"../node_modules/prismjs/prism.js",
|
||||||
|
"../node_modules/prismjs/components/prism-yaml.min.js"
|
||||||
],
|
],
|
||||||
"environmentSource": "environments/environment.ts",
|
"environmentSource": "environments/environment.ts",
|
||||||
"environments": {
|
"environments": {
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
"entryFile": "index.ts",
|
"entryFile": "index.ts",
|
||||||
"externals": {
|
"externals": {
|
||||||
"@ngx-translate/core": "ngx-translate-core",
|
"@ngx-translate/core": "ngx-translate-core",
|
||||||
"@ngx-translate/core/index": "ngx-translate-core"
|
"@ngx-translate/core/index": "ngx-translate-core",
|
||||||
|
"ngx-markdown": "ngx-markdown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -38,6 +38,7 @@
|
|||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
"ngx-markdown": "^1.5.1",
|
||||||
"rxjs": "^5.0.1",
|
"rxjs": "^5.0.1",
|
||||||
"ts-helpers": "^1.1.1",
|
"ts-helpers": "^1.1.1",
|
||||||
"web-animations-js": "^2.2.1",
|
"web-animations-js": "^2.2.1",
|
||||||
|
@ -42,10 +42,11 @@ export class ConfirmationDialogComponent {
|
|||||||
|
|
||||||
open(msg: ConfirmationMessage): void {
|
open(msg: ConfirmationMessage): void {
|
||||||
this.dialogTitle = msg.title;
|
this.dialogTitle = msg.title;
|
||||||
this.dialogContent = msg.message;
|
|
||||||
this.message = msg;
|
this.message = msg;
|
||||||
this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res);
|
this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res);
|
||||||
this.translate.get(this.dialogContent, { 'param': msg.param }).subscribe((res: string) => this.dialogContent = res);
|
this.translate.get(msg.message, { 'param': msg.param }).subscribe((res: string) => {
|
||||||
|
this.dialogContent = res;
|
||||||
|
});
|
||||||
// Open dialog
|
// Open dialog
|
||||||
this.buttons = msg.buttons;
|
this.buttons = msg.buttons;
|
||||||
this.opened = true;
|
this.opened = true;
|
||||||
|
@ -25,10 +25,10 @@ import { PROJECT_POLICY_CONFIG_DIRECTIVES } from './project-policy-config/index'
|
|||||||
import { HBR_GRIDVIEW_DIRECTIVES } from './gridview/index';
|
import { HBR_GRIDVIEW_DIRECTIVES } from './gridview/index';
|
||||||
import { REPOSITORY_GRIDVIEW_DIRECTIVES } from './repository-gridview/index';
|
import { REPOSITORY_GRIDVIEW_DIRECTIVES } from './repository-gridview/index';
|
||||||
import { OPERATION_DIRECTIVES } from './operation/index';
|
import { OPERATION_DIRECTIVES } from './operation/index';
|
||||||
import {LABEL_DIRECTIVES} from "./label/index";
|
import { LABEL_DIRECTIVES } from "./label/index";
|
||||||
import {CREATE_EDIT_LABEL_DIRECTIVES} from "./create-edit-label/index";
|
import { CREATE_EDIT_LABEL_DIRECTIVES } from "./create-edit-label/index";
|
||||||
import {LABEL_PIECE_DIRECTIVES} from "./label-piece/index";
|
import { LABEL_PIECE_DIRECTIVES } from "./label-piece/index";
|
||||||
|
import { HELMCHART_DIRECTIVE } from "./helm-chart/index";
|
||||||
import {
|
import {
|
||||||
SystemInfoService,
|
SystemInfoService,
|
||||||
SystemInfoDefaultService,
|
SystemInfoDefaultService,
|
||||||
@ -52,6 +52,8 @@ import {
|
|||||||
ProjectDefaultService,
|
ProjectDefaultService,
|
||||||
LabelService,
|
LabelService,
|
||||||
LabelDefaultService,
|
LabelDefaultService,
|
||||||
|
HelmChartService,
|
||||||
|
HelmChartDefaultService
|
||||||
} from './service/index';
|
} from './service/index';
|
||||||
import {
|
import {
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
@ -90,7 +92,9 @@ export const DefaultServiceConfig: IServiceConfig = {
|
|||||||
localI18nMessageVariableMap: {},
|
localI18nMessageVariableMap: {},
|
||||||
configurationEndpoint: "/api/configurations",
|
configurationEndpoint: "/api/configurations",
|
||||||
scanJobEndpoint: "/api/jobs/scan",
|
scanJobEndpoint: "/api/jobs/scan",
|
||||||
labelEndpoint: "/api/labels"
|
labelEndpoint: "/api/labels",
|
||||||
|
helmChartEndpoint: "/api/chartrepo",
|
||||||
|
downloadChartEndpoint: "/chartrepo"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +142,9 @@ export interface HarborModuleConfig {
|
|||||||
|
|
||||||
// Service implementation for label
|
// Service implementation for label
|
||||||
labelService?: Provider;
|
labelService?: Provider;
|
||||||
|
|
||||||
|
// Service implementation for helmchart
|
||||||
|
helmChartService?: Provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,7 +191,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
|||||||
LABEL_PIECE_DIRECTIVES,
|
LABEL_PIECE_DIRECTIVES,
|
||||||
HBR_GRIDVIEW_DIRECTIVES,
|
HBR_GRIDVIEW_DIRECTIVES,
|
||||||
REPOSITORY_GRIDVIEW_DIRECTIVES,
|
REPOSITORY_GRIDVIEW_DIRECTIVES,
|
||||||
OPERATION_DIRECTIVES
|
OPERATION_DIRECTIVES,
|
||||||
|
HELMCHART_DIRECTIVE
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
LOG_DIRECTIVES,
|
LOG_DIRECTIVES,
|
||||||
@ -210,7 +218,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
|||||||
LABEL_PIECE_DIRECTIVES,
|
LABEL_PIECE_DIRECTIVES,
|
||||||
HBR_GRIDVIEW_DIRECTIVES,
|
HBR_GRIDVIEW_DIRECTIVES,
|
||||||
REPOSITORY_GRIDVIEW_DIRECTIVES,
|
REPOSITORY_GRIDVIEW_DIRECTIVES,
|
||||||
OPERATION_DIRECTIVES
|
OPERATION_DIRECTIVES,
|
||||||
|
HELMCHART_DIRECTIVE
|
||||||
],
|
],
|
||||||
providers: []
|
providers: []
|
||||||
})
|
})
|
||||||
@ -233,6 +242,7 @@ export class HarborLibraryModule {
|
|||||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||||
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
||||||
|
config.helmChartService || {provide: HelmChartService, useClass: HelmChartDefaultService},
|
||||||
// Do initializing
|
// Do initializing
|
||||||
TranslateServiceInitializer,
|
TranslateServiceInitializer,
|
||||||
{
|
{
|
||||||
@ -264,6 +274,7 @@ export class HarborLibraryModule {
|
|||||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||||
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
||||||
|
config.helmChartService || {provide: HelmChartService, useClass: HelmChartDefaultService},
|
||||||
ChannelService,
|
ChannelService,
|
||||||
OperationService
|
OperationService
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<div class="row flex-items-xs-center dep-container">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left">{{'HELM_CHART.NAME' | translate}}</th>
|
||||||
|
<th class="left">{{'HELM_CHART.VERSION' | translate}}</th>
|
||||||
|
<th class="left">{{'HELM_CHART.REPO' | translate}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let dep of dependencies">
|
||||||
|
<td class="left">{{dep.name}}</td>
|
||||||
|
<td class="left">{{dep.version}}</td>
|
||||||
|
<td class="left"><a href="{{dep.repository}}">{{dep.repository}}</a></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,3 @@
|
|||||||
|
.dep-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
ChangeDetectionStrategy
|
||||||
|
} from "@angular/core";
|
||||||
|
|
||||||
|
import { HelmChartDependency } from "./../../service/interface";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-chart-detail-dependency",
|
||||||
|
templateUrl: "./chart-detail-dependency.component.html",
|
||||||
|
styleUrls: ["./chart-detail-dependency.component.scss"],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChartDetailDependencyComponent implements OnInit {
|
||||||
|
@Input() dependencies: HelmChartDependency;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<p>{{summary.description}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 md-container">
|
||||||
|
<div *ngIf="readme" class="md-div" [innerHTML]="readme | markdown"></div>
|
||||||
|
<div *ngIf="!readme">{{'HELM_CHART.NO_README' | translate}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4 summary-container">
|
||||||
|
<div class="col-md-12 content-group">
|
||||||
|
<div>
|
||||||
|
<label>{{'HELM_CHART.OVERVIEW' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{'HELM_CHART.HOME' | translate }}</td>
|
||||||
|
<td class="left">
|
||||||
|
<a href="{{summary.home}}">{{summary.home}}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{'HELM_CHART.SRC_REPO' | translate }}</td>
|
||||||
|
<td class="left">
|
||||||
|
<a href="{{summary.sources}}">{{summary.sources}}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{'HELM_CHART.CREATED' | translate }}</td>
|
||||||
|
<td class="left">{{summary.created | date}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let maintainer of summary.maintainers; let i = index">
|
||||||
|
<td class="left" *ngIf="i === 0">{{'HELM_CHART.MAINTAINERS' | translate }}</td>
|
||||||
|
<td class="left" *ngIf="i !== 0"></td>
|
||||||
|
<td class="left">
|
||||||
|
<a href="mailto:{{maintainer.email}}">{{maintainer.name}}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{'HELM_CHART.VERSION' | translate }}</td>
|
||||||
|
<td class="left">{{ summary.appVersion }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 content-group">
|
||||||
|
<div>
|
||||||
|
<label>{{'HELM_CHART.ADD_REPO' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{addCMD}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 content-group">
|
||||||
|
<div>
|
||||||
|
<label>{{'HELM_CHART.INSTALL_CHART' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{installCMD}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 content-group">
|
||||||
|
<div>
|
||||||
|
<label>{{'HELM_CHART.SECURITY' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="left">{{'HELM_CHART.SIGNED' | translate }}</td>
|
||||||
|
<div *ngIf="security?.signature?.signed;then signed_content else unsignd_content"></div>
|
||||||
|
<ng-template #signed_content>
|
||||||
|
<td class="left">
|
||||||
|
<span class="content-icon">
|
||||||
|
<clr-icon shape="shield-check" class="is-success"></clr-icon>
|
||||||
|
</span> {{'HELM_CHART.SIGNED' | translate }}</td>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #unsignd_content>
|
||||||
|
<td class="left">
|
||||||
|
<span class="content-icon">
|
||||||
|
<clr-icon shape="shield-x" class="is-error"></clr-icon>
|
||||||
|
</span> {{'HELM_CHART.UNSIGNED' | translate }}</td>
|
||||||
|
</ng-template>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,22 @@
|
|||||||
|
.md-container {
|
||||||
|
margin-top: 15px;
|
||||||
|
border: solid 1px #DDDDDD;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-container {
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
table {
|
||||||
|
background-color: #F2F2F2;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-group {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input
|
||||||
|
} from "@angular/core";
|
||||||
|
|
||||||
|
import { HelmChartMetaData, HelmChartSecurity } from "./../../service/interface";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-chart-detail-summary",
|
||||||
|
templateUrl: "./chart-detail-summary.component.html",
|
||||||
|
styleUrls: ["./chart-detail-summary.component.scss"],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChartDetailSummaryComponent implements OnInit {
|
||||||
|
@Input() summary: HelmChartMetaData;
|
||||||
|
@Input() security: HelmChartSecurity;
|
||||||
|
@Input() repoURL: string;
|
||||||
|
@Input() projectName: string;
|
||||||
|
@Input() chartName: string;
|
||||||
|
@Input() chartVersion: string;
|
||||||
|
@Input() readme: string;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
public get addCMD() {
|
||||||
|
return `helm repo add REPO_NAME ${this.repoURL}/chartrepo/${this.projectName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get installCMD() {
|
||||||
|
return `helm install --version ${this.chartVersion} REPO_NAME/${this.chartName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
<div class="row flex-items-xs-right">
|
||||||
|
<div class="swichy-container">
|
||||||
|
<span class="card-btn" (click)="showYamlFile(false)" (mouseenter)="mouseEnter('value') " (mouseleave)="mouseLeave('value')">
|
||||||
|
<clr-icon size="36" shape="view-list" title='list values'
|
||||||
|
[ngClass]="{'is-highlight': isValueMode || isHovering('value') }" ></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="list-btn" (click)="showYamlFile(true)" (mouseenter)="mouseEnter('yaml') " (mouseleave)="mouseLeave('yaml')">
|
||||||
|
<clr-icon size="36" shape="file" title="yaml file"
|
||||||
|
[ngClass]="{'is-highlight': !isValueMode || isHovering('yaml') }"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row value-container">
|
||||||
|
<div class="col-xs-8" *ngIf="valueMode">
|
||||||
|
<div>
|
||||||
|
<label>{{'HELM_CHART.SHOW_KV' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let key of objKeys(values)">
|
||||||
|
<td class="left">{{key}}</td>
|
||||||
|
<td class="left">{{values[key]}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-8" *ngIf="!valueMode">
|
||||||
|
<div>
|
||||||
|
<label>{{'HELM_CHART.SHOW_YAML' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="yaml-container" [innerHTML]="yaml | language : 'yaml' | markdown"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,15 @@
|
|||||||
|
.value-container {
|
||||||
|
::ng-deep pre {
|
||||||
|
min-height: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.swichy-container {
|
||||||
|
margin-top: 3px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
max-height: max-content;
|
||||||
|
padding-left: 21px;
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy
|
||||||
|
} from "@angular/core";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-chart-detail-value",
|
||||||
|
templateUrl: "./chart-detail-value.component.html",
|
||||||
|
styleUrls: ["./chart-detail-value.component.scss"],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChartDetailValueComponent implements OnInit {
|
||||||
|
@Input() values;
|
||||||
|
@Input() yaml;
|
||||||
|
|
||||||
|
// Default set to yaml file
|
||||||
|
valueMode = false;
|
||||||
|
valueHover = false;
|
||||||
|
yamlHover = true;
|
||||||
|
|
||||||
|
objKeys = Object.keys;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isValueMode() {
|
||||||
|
return this.valueMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isHovering(view: string) {
|
||||||
|
if (view === 'value') {
|
||||||
|
return this.valueHover ? true : false;
|
||||||
|
} else {
|
||||||
|
return this.yamlHover ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showYamlFile(showYaml: boolean) {
|
||||||
|
this.valueMode = !showYaml;
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnter(mode: string) {
|
||||||
|
if (mode === "value") {
|
||||||
|
this.valueHover = true;
|
||||||
|
} else {
|
||||||
|
this.yamlHover = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeave(mode: string) {
|
||||||
|
if (mode === "value") {
|
||||||
|
this.valueHover = false;
|
||||||
|
} else {
|
||||||
|
this.yamlHover = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
<div>
|
||||||
|
<div class="row flex-items-xs-between">
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<div class="title-container">
|
||||||
|
<div class="chart-name">
|
||||||
|
{{chartNameWithVersion | translate}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{roleName | translate}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-1">
|
||||||
|
<button class="btn btn-sm btn-secondary"
|
||||||
|
(click)="downloadChart()">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-loading" *ngIf="loading">
|
||||||
|
<span class="spinner">
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!loading && isChartExist">
|
||||||
|
<clr-tabs>
|
||||||
|
<clr-tab>
|
||||||
|
<button clrTabLink id="summary-link">{{'HELM_CHART.SUMMARY' | translate}}</button>
|
||||||
|
<clr-tab-content id="summary-content" *clrIfActive>
|
||||||
|
<hbr-chart-detail-summary
|
||||||
|
[summary]="chartDetail.metadata"
|
||||||
|
[chartName]="chartName"
|
||||||
|
[repoURL]="repoURL"
|
||||||
|
[projectName]="project.name"
|
||||||
|
[chartVersion]="chartVersion"
|
||||||
|
[security]="chartDetail.security"
|
||||||
|
[readme]="chartDetail.files['README.md']"></hbr-chart-detail-summary>
|
||||||
|
</clr-tab-content>
|
||||||
|
</clr-tab>
|
||||||
|
<clr-tab>
|
||||||
|
<button clrTabLink id="depend-link">{{'HELM_CHART.DEPENDENCIES' | translate}}</button>
|
||||||
|
<clr-tab-content id="depend-content">
|
||||||
|
<hbr-chart-detail-dependency [dependencies]='chartDetail.dependencies'></hbr-chart-detail-dependency>
|
||||||
|
</clr-tab-content>
|
||||||
|
</clr-tab>
|
||||||
|
<clr-tab>
|
||||||
|
<button clrTabLink id="value-link">{{'HELM_CHART.VALUES' | translate}}</button>
|
||||||
|
<clr-tab-content id="value-content">
|
||||||
|
<hbr-chart-detail-value
|
||||||
|
[values]="chartDetail.values"
|
||||||
|
[yaml]="chartDetail.files['values.yaml']"></hbr-chart-detail-value>
|
||||||
|
</clr-tab-content>
|
||||||
|
</clr-tab>
|
||||||
|
</clr-tabs>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!loading && !isChartExist">
|
||||||
|
<h6>{{'HELM_CHART.NO_DETAIL' | translate }}</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,20 @@
|
|||||||
|
.title-container {
|
||||||
|
display: flex;
|
||||||
|
.chart-name {
|
||||||
|
border-right: 1px solid gray;
|
||||||
|
font-size: 27px;
|
||||||
|
font-weight: normal;
|
||||||
|
padding-right: 9px;
|
||||||
|
margin-right: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
width: 108px !important;
|
||||||
|
height: 108px !important;
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
import { Project } from "./../../project-policy-config/project";
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from "@angular/core";
|
||||||
|
|
||||||
|
import { downloadFile, toPromise } from "../../utils";
|
||||||
|
import { SystemInfoService, HelmChartService } from "../../service/index";
|
||||||
|
import { HelmChartDetail, SystemInfo } from "./../../service/interface";
|
||||||
|
import { ErrorHandler } from "./../../error-handler/error-handler";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-chart-detail",
|
||||||
|
templateUrl: "./chart-detail.component.html",
|
||||||
|
styleUrls: ["./chart-detail.component.scss"],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChartDetailComponent implements OnInit {
|
||||||
|
@Input() projectId: number;
|
||||||
|
@Input() project: Project;
|
||||||
|
@Input() chartName: string;
|
||||||
|
@Input() chartVersion: string;
|
||||||
|
@Input() roleName: string;
|
||||||
|
@Input() hasSignedIn: boolean;
|
||||||
|
@Input() hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
|
loading = true;
|
||||||
|
isMember = false;
|
||||||
|
chartDetail: HelmChartDetail;
|
||||||
|
systemInfo: SystemInfo;
|
||||||
|
|
||||||
|
repoURL = "";
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private systemInfoService: SystemInfoService,
|
||||||
|
private helmChartService: HelmChartService,
|
||||||
|
private cdr: ChangeDetectorRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||||
|
.then(systemInfo => {
|
||||||
|
let scheme = 'http://';
|
||||||
|
this.systemInfo = systemInfo;
|
||||||
|
if (this.systemInfo.has_ca_root) {
|
||||||
|
scheme = 'https://';
|
||||||
|
}
|
||||||
|
this.repoURL = `${scheme}${this.systemInfo.registry_url}`;
|
||||||
|
})
|
||||||
|
.catch(error => this.errorHandler.error(error));
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
public get chartNameWithVersion() {
|
||||||
|
return `${this.chartName}:${this.chartVersion}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isChartExist() {
|
||||||
|
return this.chartDetail ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.loading = true;
|
||||||
|
this.helmChartService
|
||||||
|
.getChartDetail(this.project.name, this.chartName, this.chartVersion)
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
|
setTimeout(() => clearInterval(hnd), 2000);
|
||||||
|
})
|
||||||
|
.subscribe(
|
||||||
|
chartDetail => {
|
||||||
|
this.chartDetail = chartDetail;
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
this.errorHandler.error(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadChart() {
|
||||||
|
if (!this.chartDetail ||
|
||||||
|
!this.chartDetail.metadata ||
|
||||||
|
!this.chartDetail.metadata.urls ||
|
||||||
|
this.chartDetail.metadata.urls.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let filename = this.chartDetail.metadata.urls[0];
|
||||||
|
|
||||||
|
this.helmChartService.downloadChart(this.project.name, filename).subscribe(
|
||||||
|
res => {
|
||||||
|
downloadFile(res);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
100
src/ui_ng/lib/src/helm-chart/helm-chart.component.html
Normal file
100
src/ui_ng/lib/src/helm-chart/helm-chart.component.html
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<div>
|
||||||
|
<div class="row chart-tool">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="row flex-items-xs-right option-right rightPos">
|
||||||
|
<div class="flex-xs-middle">
|
||||||
|
<hbr-filter [withDivider]="true" filterPlaceholder="{{'HELM_CHART.FILTER_FOR_CHARTS' | translate}}" [currentValue]="lastFilteredChartName"></hbr-filter>
|
||||||
|
<span class="card-btn" (click)="showCard(true)" (mouseenter)="mouseEnter('card') " (mouseleave)="mouseLeave('card')">
|
||||||
|
<clr-icon [ngClass]="{'is-highlight': isCardView || isHovering('card') }" shape="view-cards"></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="list-btn" (click)="showCard(false)" (mouseenter)="mouseEnter('list') " (mouseleave)="mouseLeave('list')">
|
||||||
|
<clr-icon [ngClass]="{'is-highlight': !isCardView || isHovering('list') }" shape="view-list"></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="filter-divider"></span>
|
||||||
|
<span class="refresh-btn" (click)="refresh()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<clr-datagrid (clrDgRefresh)="refresh($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
||||||
|
<clr-dg-action-bar>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasProjectAdminRole" (click)="onChartUpload($event)">
|
||||||
|
<clr-icon shape="upload" size="16"></clr-icon> {{'HELM_CHART.UPLOAD' | translate}}
|
||||||
|
</button>
|
||||||
|
</clr-dg-action-bar>
|
||||||
|
<clr-dg-column [clrDgField]="'name'">{{'HELM_CHART.NAME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'HELM_CHART.VERSIONS' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'HELM_CHART.CREATED' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-placeholder>{{'HELM_CHART.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||||
|
<clr-dg-row *ngFor="let chart of charts" [clrDgItem]="chart">
|
||||||
|
<clr-dg-cell>
|
||||||
|
<a href="javascript:void(0)" (click)="onChartClick(chart)">{{ chart.name }}</a>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{ chart.total_versions }}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{ chart.created | date }}</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize">
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'HELM_CHART.OF' | translate}} {{pagination.totalItems}} {{'HELM_CHART.ITEMS'
|
||||||
|
| translate}}
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isCardView" class="row card-container">
|
||||||
|
<div *ngFor="let item of charts;" class="col-lg-3 col-md-4 col-sm-6">
|
||||||
|
<a let i=index; class="card clickable" (click)="onChartClick(item)">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-media-block wrap">
|
||||||
|
<div class="card-media-description">
|
||||||
|
<span class="card-media-title">{{item.name}}</span>
|
||||||
|
<p class="card-media-text">{{item.home}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'HELM_CHART.VERSIONS' | translate}}</label>
|
||||||
|
<div>{{item.total_versions}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'HELM_CHART.CREATED' | translate}}</label>
|
||||||
|
<div>{{item.Created | date}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="loading" [ngClass]="{'central-block-loading': isFirstPage, 'central-block-loading-more': !isFirstPage}">
|
||||||
|
<span class="vertical-helper"></span>
|
||||||
|
<div class="spinner"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<clr-modal [(clrModalOpen)]="isUploadModalOpen" [clrModalStaticBackdrop]="true">
|
||||||
|
<h3 class="modal-title">{{'HELM_CHART.UPLOAD_TITLE' | translate}}</h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form #chartUploadForm="ngForm" enctype="multipart/form-data" (ngSubmit)="upload()">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="chart"> {{'HELM_CHART.CHART_FILE' | translate}} </label>
|
||||||
|
<input type="file" id="chart" name="chart" ngModel (change)="onChartFileChangeEvent($event)">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="prov"> {{'HELM_CHART.CHART_PROV' | translate}} </label>
|
||||||
|
<input type="file" id="prov" name="prov" ngModel (change)="onProvFileChangeEvent($event)">
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<button type="submit" class="btn btn-secondary" [disabled]="isUploading">
|
||||||
|
<span *ngIf="!isUploading">{{'HELM_CHART.UPLOAD' | translate}}</span>
|
||||||
|
<span *ngIf="isUploading" class="spinner spinner-inline">
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
</div>
|
68
src/ui_ng/lib/src/helm-chart/helm-chart.component.scss
Normal file
68
src/ui_ng/lib/src/helm-chart/helm-chart.component.scss
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
.chart-tool {
|
||||||
|
position: relative;
|
||||||
|
.toolbar {
|
||||||
|
overflow: hidden;
|
||||||
|
.rightPos {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
right: 35px;
|
||||||
|
margin-top: 4px;
|
||||||
|
.filter-divider {
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
width: 2px;
|
||||||
|
background-color: #cccccc;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
top: 9px;
|
||||||
|
margin-right: 6px;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
margin-top: 21px;
|
||||||
|
.card-header {
|
||||||
|
.card-media-block {
|
||||||
|
.card-media-description {
|
||||||
|
height: 45px;
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.card-media-title {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
.card-media-text {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 21px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-block {
|
||||||
|
margin-top: 24px;
|
||||||
|
min-height: 100px;
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
label {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-helper {
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
171
src/ui_ng/lib/src/helm-chart/helm-chart.component.ts
Normal file
171
src/ui_ng/lib/src/helm-chart/helm-chart.component.ts
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from "@angular/core";
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { State } from "clarity-angular";
|
||||||
|
|
||||||
|
import { SystemInfo, SystemInfoService, HelmChartItem } from "../service/index";
|
||||||
|
import { ErrorHandler } from "../error-handler/error-handler";
|
||||||
|
import { toPromise, DEFAULT_PAGE_SIZE } from "../utils";
|
||||||
|
import { HelmChartService } from "../service/helm-chart.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-helm-chart",
|
||||||
|
templateUrl: "./helm-chart.component.html",
|
||||||
|
styleUrls: ["./helm-chart.component.scss"],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class HelmChartComponent implements OnInit {
|
||||||
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
|
@Input() projectId: number;
|
||||||
|
@Input() projectName = "unknown";
|
||||||
|
@Input() urlPrefix: string;
|
||||||
|
@Input() hasSignedIn: boolean;
|
||||||
|
@Input() hasProjectAdminRole: boolean;
|
||||||
|
@Output() chartClickEvt = new EventEmitter<any>();
|
||||||
|
@Output() chartDownloadEve = new EventEmitter<string>();
|
||||||
|
|
||||||
|
lastFilteredChartName: string;
|
||||||
|
charts: HelmChartItem[] = [];
|
||||||
|
chartsCopy: HelmChartItem[] = [];
|
||||||
|
systemInfo: SystemInfo;
|
||||||
|
selectedRows: HelmChartItem[] = [];
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
// For Upload
|
||||||
|
isUploading = false;
|
||||||
|
isUploadModalOpen = false;
|
||||||
|
provFile: File;
|
||||||
|
chartFile: File;
|
||||||
|
|
||||||
|
// For View swtich
|
||||||
|
isCardView: boolean;
|
||||||
|
cardHover = false;
|
||||||
|
listHover = false;
|
||||||
|
|
||||||
|
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||||
|
currentPage = 1;
|
||||||
|
totalCount = 0;
|
||||||
|
currentState: State;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private systemInfoService: SystemInfoService,
|
||||||
|
private helmChartService: HelmChartService,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public get registryUrl(): string {
|
||||||
|
return this.systemInfo ? this.systemInfo.registry_url : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Get system info for tag views
|
||||||
|
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||||
|
.then(systemInfo => (this.systemInfo = systemInfo))
|
||||||
|
.catch(error => this.errorHandler.error(error));
|
||||||
|
this.lastFilteredChartName = "";
|
||||||
|
this.refresh();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.loading = true;
|
||||||
|
this.helmChartService
|
||||||
|
.getHelmCharts(this.projectName)
|
||||||
|
.finally(() => {
|
||||||
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
|
setTimeout(() => clearInterval(hnd), 3000);
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
.subscribe(
|
||||||
|
charts => {
|
||||||
|
this.charts = charts;
|
||||||
|
this.chartsCopy = charts.map(x => Object.assign({}, x));
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
this.errorHandler.error(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartClick(item: HelmChartItem) {
|
||||||
|
this.chartClickEvt.emit(item.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartUpload() {
|
||||||
|
this.isUploadModalOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload() {
|
||||||
|
if (!this.chartFile && !this.provFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.isUploading) { return; };
|
||||||
|
this.isUploading = true;
|
||||||
|
this.helmChartService
|
||||||
|
.uploadChart(this.projectName, this.chartFile, this.provFile)
|
||||||
|
.finally(() => {
|
||||||
|
this.isUploading = false;
|
||||||
|
this.isUploadModalOpen = false;
|
||||||
|
this.refresh();
|
||||||
|
})
|
||||||
|
.subscribe(() => {
|
||||||
|
this.translateService
|
||||||
|
.get("HELM_CHART.FILE_UPLOADED")
|
||||||
|
.subscribe(res => this.errorHandler.info(res));
|
||||||
|
},
|
||||||
|
err => this.errorHandler.error(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartFileChangeEvent(event) {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
this.chartFile = event.target.files[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onProvFileChangeEvent(event) {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
this.provFile = event.target.files[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
showCard(cardView: boolean) {
|
||||||
|
if (this.isCardView === cardView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isCardView = cardView;
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnter(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
this.cardHover = true;
|
||||||
|
} else {
|
||||||
|
this.listHover = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeave(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
this.cardHover = false;
|
||||||
|
} else {
|
||||||
|
this.listHover = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isHovering(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
return this.cardHover;
|
||||||
|
} else {
|
||||||
|
return this.listHover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/ui_ng/lib/src/helm-chart/index.ts
Normal file
23
src/ui_ng/lib/src/helm-chart/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
import { HelmChartComponent } from './helm-chart.component';
|
||||||
|
import { ChartVersionComponent } from './versions/helm-chart-version.component';
|
||||||
|
import { ChartDetailComponent } from './chart-detail/chart-detail.component';
|
||||||
|
import { ChartDetailSummaryComponent } from './chart-detail/chart-detail-summary.component';
|
||||||
|
import { ChartDetailDependencyComponent } from './chart-detail/chart-detail-dependency.component';
|
||||||
|
import { ChartDetailValueComponent } from './chart-detail/chart-detail-value.component';
|
||||||
|
|
||||||
|
export * from "./helm-chart.component";
|
||||||
|
export * from "./versions/helm-chart-version.component";
|
||||||
|
export * from "./chart-detail/chart-detail.component";
|
||||||
|
export * from "./chart-detail/chart-detail-summary.component";
|
||||||
|
export * from "./chart-detail/chart-detail-dependency.component";
|
||||||
|
export * from "./chart-detail/chart-detail-value.component";
|
||||||
|
|
||||||
|
export const HELMCHART_DIRECTIVE: Type<any>[] = [
|
||||||
|
HelmChartComponent,
|
||||||
|
ChartVersionComponent,
|
||||||
|
ChartDetailComponent,
|
||||||
|
ChartDetailSummaryComponent,
|
||||||
|
ChartDetailDependencyComponent,
|
||||||
|
ChartDetailValueComponent,
|
||||||
|
];
|
@ -0,0 +1,136 @@
|
|||||||
|
<div>
|
||||||
|
<div class="row flex-items-xs-between">
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<div class="title-container">
|
||||||
|
<div class="chart-name-span">
|
||||||
|
{{chartName | translate}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{roleName | translate}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row version-tool">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="row flex-items-xs-right option-right rightPos">
|
||||||
|
<div class="flex-xs-middle">
|
||||||
|
<hbr-filter [withDivider]="true" filterPlaceholder="{{'HELM_CHART.FILTER_FOR_CHARTS' | translate}}" [currentValue]="lastFilteredVersionName"></hbr-filter>
|
||||||
|
<span class="card-btn" (click)="showCard(true)" (mouseenter)="mouseEnter('card') " (mouseleave)="mouseLeave('card')">
|
||||||
|
<clr-icon [ngClass]="{'is-highlight': isCardView || isHovering('card') }" shape="view-cards"></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="list-btn" (click)="showCard(false)" (mouseenter)="mouseEnter('list') " (mouseleave)="mouseLeave('list')">
|
||||||
|
<clr-icon [ngClass]="{'is-highlight': !isCardView || isHovering('list') }" shape="view-list"></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="filter-divider"></span>
|
||||||
|
<span class="refresh-btn" (click)="refresh()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<clr-datagrid (clrDgRefresh)="refresh($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
||||||
|
<clr-dg-action-bar>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary"
|
||||||
|
[disabled]="!hasProjectAdminRole"
|
||||||
|
(click)="versionUpload($event)">
|
||||||
|
<clr-icon shape="upload" size="16"></clr-icon> {{'HELM_CHART.UPLOAD' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary"
|
||||||
|
[disabled]="!(selectedRows.length===1)"
|
||||||
|
(click)="versionDownload()">
|
||||||
|
<clr-icon shape="download" size="16"></clr-icon> {{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary"
|
||||||
|
[disabled]="selectedRows.length<=0 || !hasProjectAdminRole"
|
||||||
|
(click)="openVersionDeleteModal(selectedRows)">
|
||||||
|
<clr-icon shape="times" size="16"></clr-icon> {{'BUTTON.DELETE' | translate}}</button>
|
||||||
|
</clr-dg-action-bar>
|
||||||
|
<clr-dg-column [clrDgField]="'name'">{{'HELM_CHART.VERSION' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'HELM_CHART.ENGINE' | translate }}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'HELM_CHART.MAINTAINERS' | translate }}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'HELM_CHART.CREATED' | translate }}</clr-dg-column>
|
||||||
|
<clr-dg-row *ngFor="let v of chartVersions" [clrDgItem]="v">
|
||||||
|
<clr-dg-cell>
|
||||||
|
<span class="list-img"><img [src]="getImgLink(v)"/></span>
|
||||||
|
<a href="javascript:void(0)" (click)="onVersionClick(v)">{{ v.version }}</a>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{ v.engine }}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{ getMaintainerString(v.maintainers) }}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{ v.created | date}}</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize">
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'HELM_CHART.OF' | translate}} {{pagination.totalItems}} {{'HELM_CHART.VERSIONS'
|
||||||
|
| translate}}
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isCardView" class="row card-container">
|
||||||
|
<div *ngFor="let item of chartVersions;" class="col-lg-3 col-md-4 col-sm-6">
|
||||||
|
<a let i=index; class="card clickable" (click)="onVersionClick(item)">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-media-block">
|
||||||
|
<img [src]="getImgLink(item)"/>
|
||||||
|
<div class="card-media-description">
|
||||||
|
<span class="card-media-title">{{item.name}}</span>
|
||||||
|
<p class="card-media-text">{{item.home}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'HELM_CHART.ENGINE' | translate}}</label>
|
||||||
|
<div>{{item.engine}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'HELM_CHART.MAINTAINERS' | translate}}</label>
|
||||||
|
<div>{{getMaintainerString(item.maintainers)}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'HELM_CHART.VERSION' | translate}}</label>
|
||||||
|
<div>{{item.appVersion}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
||||||
|
<button type="button" class="btn btn-link" (click)="versionDownload($event, item)">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-link" (click)="openVersionDeleteModal([item])">{{'BUTTON.DELETE' | translate}}</button>
|
||||||
|
</clr-dropdown>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="loading" [ngClass]="{'central-block-loading': isFirstPage, 'central-block-loading-more': !isFirstPage}">
|
||||||
|
<span class="vertical-helper"></span>
|
||||||
|
<div class="spinner"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<clr-modal [(clrModalOpen)]="isUploadModalOpen" [clrModalStaticBackdrop]="true">
|
||||||
|
<h3 class="modal-title">{{'HELM_CHART.UPLOAD_TITLE' | translate}}</h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form #chartUploadForm="ngForm" enctype="multipart/form-data" (ngSubmit)="upload()">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="chart"> {{'HELM_CHART.CHART_FILE' | translate}} </label>
|
||||||
|
<input type="file" id="chart" name="chart" ngModel (change)="onChartFileChangeEvent($event)">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="prov"> {{'HELM_CHART.CHART_PROV' | translate}} </label>
|
||||||
|
<input type="file" id="prov" name="prov" ngModel (change)="onProvFileChangeEvent($event)">
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<button type="submit" class="btn btn-secondary" [disabled]="isUploading">
|
||||||
|
<span *ngIf="!isUploading">{{'HELM_CHART.UPLOAD' | translate}}</span>
|
||||||
|
<span *ngIf="isUploading" class="spinner spinner-inline">
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||||
|
</div>
|
@ -0,0 +1,96 @@
|
|||||||
|
.title-container {
|
||||||
|
display: flex;
|
||||||
|
.chart-name-span {
|
||||||
|
border-right: 1px solid gray;
|
||||||
|
font-size: 27px;
|
||||||
|
font-weight: normal;
|
||||||
|
padding-right: 9px;
|
||||||
|
margin-right: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-tool {
|
||||||
|
position: relative;
|
||||||
|
.toolbar {
|
||||||
|
overflow: hidden;
|
||||||
|
.rightPos {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
right: 35px;
|
||||||
|
margin-top: 4px;
|
||||||
|
.filter-divider {
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
width: 2px;
|
||||||
|
background-color: #cccccc;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
top: 9px;
|
||||||
|
margin-right: 6px;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
margin-top: 21px;
|
||||||
|
.card-header {
|
||||||
|
.card-media-block {
|
||||||
|
img {
|
||||||
|
height: 45px;
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
|
.card-media-description {
|
||||||
|
height: 45px;
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.card-media-title {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
.card-media-text {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 21px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-block {
|
||||||
|
margin-top: 24px;
|
||||||
|
min-height: 100px;
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
label {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
.card-footer {
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-img {
|
||||||
|
img {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-helper {
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
@ -0,0 +1,298 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
ViewChild,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Output,
|
||||||
|
EventEmitter
|
||||||
|
} from "@angular/core";
|
||||||
|
import { NgForm } from "@angular/forms";
|
||||||
|
import { Observable } from "rxjs/Observable";
|
||||||
|
import "rxjs/add/observable/forkJoin";
|
||||||
|
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { State } from "clarity-angular";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SystemInfo,
|
||||||
|
SystemInfoService,
|
||||||
|
HelmChartVersion,
|
||||||
|
HelmChartMaintainer
|
||||||
|
} from "./../../service/index";
|
||||||
|
import { ErrorHandler } from "./../../error-handler/error-handler";
|
||||||
|
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../../utils";
|
||||||
|
import { OperationService } from "./../../operation/operation.service";
|
||||||
|
import { HelmChartService } from "./../../service/helm-chart.service";
|
||||||
|
import { ConfirmationAcknowledgement, ConfirmationDialogComponent, ConfirmationMessage } from "./../../confirmation-dialog";
|
||||||
|
import {
|
||||||
|
OperateInfo,
|
||||||
|
OperationState,
|
||||||
|
operateChanges
|
||||||
|
} from "./../../operation/operate";
|
||||||
|
import {
|
||||||
|
ConfirmationButtons,
|
||||||
|
ConfirmationTargets,
|
||||||
|
ConfirmationState,
|
||||||
|
DefaultHelmIcon
|
||||||
|
} from "../../shared/shared.const";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-helm-chart-version",
|
||||||
|
templateUrl: "./helm-chart-version.component.html",
|
||||||
|
styleUrls: ["./helm-chart-version.component.scss"],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChartVersionComponent implements OnInit {
|
||||||
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
|
@Input() projectName: string;
|
||||||
|
@Input() chartName: string;
|
||||||
|
@Input() roleName: string;
|
||||||
|
@Input() hasSignedIn: boolean;
|
||||||
|
@Input() hasProjectAdminRole: boolean;
|
||||||
|
@Output() versionClickEvt = new EventEmitter<string>();
|
||||||
|
@Output() backEvt = new EventEmitter<any>();
|
||||||
|
|
||||||
|
lastFilteredVersionName: string;
|
||||||
|
chartVersions: HelmChartVersion[] = [];
|
||||||
|
versionsCopy: HelmChartVersion[] = [];
|
||||||
|
systemInfo: SystemInfo;
|
||||||
|
selectedRows: HelmChartVersion[] = [];
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
isCardView: boolean;
|
||||||
|
cardHover = false;
|
||||||
|
listHover = false;
|
||||||
|
|
||||||
|
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||||
|
currentPage = 1;
|
||||||
|
totalCount = 0;
|
||||||
|
currentState: State;
|
||||||
|
|
||||||
|
isUploading = false;
|
||||||
|
isUploadModalOpen = false;
|
||||||
|
chartFile: File;
|
||||||
|
provFile: File;
|
||||||
|
|
||||||
|
@ViewChild("confirmationDialog")
|
||||||
|
confirmationDialog: ConfirmationDialogComponent;
|
||||||
|
|
||||||
|
@ViewChild("chartUploadForm") form: NgForm;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private systemInfoService: SystemInfoService,
|
||||||
|
private helmChartService: HelmChartService,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private operationService: OperationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public get registryUrl(): string {
|
||||||
|
return this.systemInfo ? this.systemInfo.registry_url : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Get system info for tag views
|
||||||
|
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||||
|
.then(systemInfo => (this.systemInfo = systemInfo))
|
||||||
|
.catch(error => this.errorHandler.error(error));
|
||||||
|
this.refresh();
|
||||||
|
this.lastFilteredVersionName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.loading = true;
|
||||||
|
this.helmChartService
|
||||||
|
.getChartVersions(this.projectName, this.chartName)
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
|
setTimeout(() => clearInterval(hnd), 2000);
|
||||||
|
})
|
||||||
|
.subscribe(
|
||||||
|
versions => {
|
||||||
|
this.chartVersions = versions;
|
||||||
|
this.versionsCopy = versions.map(x => Object.assign({}, x));
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
if (err.status && err.status === 404) {
|
||||||
|
this.backEvt.emit();
|
||||||
|
}
|
||||||
|
this.errorHandler.error(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaintainerString(maintainers: HelmChartMaintainer[]) {
|
||||||
|
if (!maintainers || maintainers.length < 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let maintainer_string = maintainers[0].name;
|
||||||
|
if (maintainers.length > 1) {
|
||||||
|
maintainer_string = `${maintainer_string} (${maintainers.length - 1} others)`;
|
||||||
|
}
|
||||||
|
return maintainer_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
onVersionClick(version: HelmChartVersion) {
|
||||||
|
this.versionClickEvt.emit(version.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteVersion(version: HelmChartVersion): Observable<any> {
|
||||||
|
// init operation info
|
||||||
|
let operateMsg = new OperateInfo();
|
||||||
|
operateMsg.name = "OPERATION.DELETE_CHART_VERSION";
|
||||||
|
operateMsg.data.id = version.digest;
|
||||||
|
operateMsg.state = OperationState.progressing;
|
||||||
|
operateMsg.data.name = `${version.name}:${version.version}`;
|
||||||
|
this.operationService.publishInfo(operateMsg);
|
||||||
|
|
||||||
|
return this.helmChartService
|
||||||
|
.deleteChartVersion(this.projectName, this.chartName, version.version)
|
||||||
|
.map(
|
||||||
|
() => operateChanges(operateMsg, OperationState.success),
|
||||||
|
err => operateChanges(operateMsg, OperationState.failure, err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteVersions(versions: HelmChartVersion[]) {
|
||||||
|
if (versions && versions.length < 1) { return; }
|
||||||
|
let versionObs = versions.map(v => this.deleteVersion(v));
|
||||||
|
Observable.forkJoin(versionObs).finally(() => this.refresh()).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
versionDownload(item?: HelmChartVersion) {
|
||||||
|
let selectedVersion: HelmChartVersion;
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
selectedVersion = item;
|
||||||
|
} else {
|
||||||
|
// return if selected version less then 1
|
||||||
|
if (this.selectedRows.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedVersion = this.selectedRows[0];
|
||||||
|
}
|
||||||
|
if (!selectedVersion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = selectedVersion.urls[0];
|
||||||
|
this.helmChartService.downloadChart(this.projectName, filename).subscribe(
|
||||||
|
res => {
|
||||||
|
downloadFile(res);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
versionUpload() {
|
||||||
|
this.isUploadModalOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
showCard(cardView: boolean) {
|
||||||
|
if (this.isCardView === cardView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isCardView = cardView;
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnter(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
this.cardHover = true;
|
||||||
|
} else {
|
||||||
|
this.listHover = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeave(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
this.cardHover = false;
|
||||||
|
} else {
|
||||||
|
this.listHover = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isHovering(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
return this.cardHover;
|
||||||
|
} else {
|
||||||
|
return this.listHover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upload() {
|
||||||
|
if (!this.chartFile && !this.provFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.isUploading) { return; };
|
||||||
|
this.isUploading = true;
|
||||||
|
this.helmChartService
|
||||||
|
.uploadChart(this.projectName, this.chartFile, this.provFile)
|
||||||
|
.finally(() => {
|
||||||
|
this.isUploading = false;
|
||||||
|
this.isUploadModalOpen = false;
|
||||||
|
this.refresh();
|
||||||
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
|
setTimeout(() => clearInterval(hnd), 3000);
|
||||||
|
})
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
this.translateService.get("HELM_CHART.FILE_UPLOADED")
|
||||||
|
.subscribe(res => this.errorHandler.info(res));
|
||||||
|
},
|
||||||
|
err => this.errorHandler.error(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartFileChangeEvent(event) {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
this.chartFile = event.target.files[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onProvFileChangeEvent(event) {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
this.provFile = event.target.files[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openVersionDeleteModal(versions: HelmChartVersion[]) {
|
||||||
|
let versionNames = versions.map(v => v.name).join(",");
|
||||||
|
this.translateService.get("HELM_CHART.DELETE_CHART_VERSION").subscribe(key => {
|
||||||
|
let message = new ConfirmationMessage(
|
||||||
|
"HELM_CHART.DELETE_CHART_VERSION_TITLE",
|
||||||
|
key,
|
||||||
|
versionNames,
|
||||||
|
versions,
|
||||||
|
ConfirmationTargets.HELM_CHART,
|
||||||
|
ConfirmationButtons.DELETE_CANCEL
|
||||||
|
);
|
||||||
|
this.confirmationDialog.open(message);
|
||||||
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
|
setTimeout(() => clearInterval(hnd), 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||||
|
if (
|
||||||
|
message &&
|
||||||
|
message.source === ConfirmationTargets.HELM_CHART &&
|
||||||
|
message.state === ConfirmationState.CONFIRMED
|
||||||
|
) {
|
||||||
|
let versions = message.data;
|
||||||
|
this.deleteVersions(versions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getImgLink(v: HelmChartVersion) {
|
||||||
|
if (v.icon) {
|
||||||
|
return v.icon;
|
||||||
|
} else {
|
||||||
|
return DefaultHelmIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,3 +26,4 @@ export * from './gridview/index';
|
|||||||
export * from './repository-gridview/index';
|
export * from './repository-gridview/index';
|
||||||
export * from './operation/index';
|
export * from './operation/index';
|
||||||
export * from './_animations/index';
|
export * from './_animations/index';
|
||||||
|
export * from './helm-chart/index';
|
||||||
|
@ -47,8 +47,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hbr-gridview *ngIf="isCardView" #gridView style="position:relative;" [items]="repositories" [loading]="loading" [pageSize]="pageSize"
|
<hbr-gridview *ngIf="isCardView" #gridView style="position:relative;"
|
||||||
[currentPage]="currentPage" [totalCount]="totalCount" [expectScrollPercent]="90" [withAdmiral]="withAdmiral" (loadNextPageEvent)="loadNextPage()">
|
[items]="repositories"
|
||||||
|
[loading]="loading"
|
||||||
|
[pageSize]="pageSize"
|
||||||
|
[currentPage]="currentPage"
|
||||||
|
[totalCount]="totalCount"
|
||||||
|
[expectScrollPercent]="90"
|
||||||
|
[withAdmiral]="withAdmiral"
|
||||||
|
(loadNextPageEvent)="loadNextPage()">
|
||||||
<ng-template let-item="item">
|
<ng-template let-item="item">
|
||||||
<a class="card clickable" (click)="watchRepoClickEvt(item)">
|
<a class="card clickable" (click)="watchRepoClickEvt(item)">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -84,7 +91,7 @@
|
|||||||
<button *ngIf="withAdmiral" type="button" class="btn btn-link" clrDropdownItem (click)="itemAddInfoEvent($event, item)" [disabled]="!hasProjectAdminRole">
|
<button *ngIf="withAdmiral" type="button" class="btn btn-link" clrDropdownItem (click)="itemAddInfoEvent($event, item)" [disabled]="!hasProjectAdminRole">
|
||||||
{{'REPOSITORY.ADDITIONAL_INFO' | translate}}
|
{{'REPOSITORY.ADDITIONAL_INFO' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-link" clrDropdownItem (click)="deleteItemEvent($event, item)" [disabled]="!hasProjectAdminRole">
|
<button type="button" class="btn btn-link" clrDropdownItem (click)="deleteItemEvent($event, [item])" [disabled]="!hasProjectAdminRole">
|
||||||
{{'REPOSITORY.DELETE' | translate}}
|
{{'REPOSITORY.DELETE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
|
@ -220,4 +220,22 @@ export interface IServiceConfig {
|
|||||||
* @memberOf IServiceConfig
|
* @memberOf IServiceConfig
|
||||||
*/
|
*/
|
||||||
labelEndpoint?: string;
|
labelEndpoint?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base endpoint of the service used to handle the helm chart.
|
||||||
|
* helm charts related endpoints will be built based on this endpoint.
|
||||||
|
* E.g:
|
||||||
|
* If the base endpoint is '/api/helmcharts',
|
||||||
|
* the helm chart endpoint will be '/api/helmcharts/:id'.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberOf IServiceConfig
|
||||||
|
*/
|
||||||
|
helmChartEndpoint?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base endpoint of the chart download url
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
downloadChartEndpoint?: string;
|
||||||
}
|
}
|
||||||
|
236
src/ui_ng/lib/src/service/helm-chart.service.ts
Normal file
236
src/ui_ng/lib/src/service/helm-chart.service.ts
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
import { Injectable, Inject } from "@angular/core";
|
||||||
|
import { Http, Response, ResponseContentType } from "@angular/http";
|
||||||
|
|
||||||
|
import "rxjs/add/observable/of";
|
||||||
|
import { Observable } from "rxjs/Observable";
|
||||||
|
|
||||||
|
import { RequestQueryParams } from "./RequestQueryParams";
|
||||||
|
import { HelmChartItem, HelmChartVersion, HelmChartDetail } from "./interface";
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from "../service.config";
|
||||||
|
import { HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS } from "../utils";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define service methods for handling the helmchart related things.
|
||||||
|
* Loose couple with project module.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @abstract
|
||||||
|
* @class RepositoryService
|
||||||
|
*/
|
||||||
|
export abstract class HelmChartService {
|
||||||
|
/**
|
||||||
|
* Get all helm charts info
|
||||||
|
* @param projectName Id of the project
|
||||||
|
* @param queryParams options params for query data
|
||||||
|
*/
|
||||||
|
abstract getHelmCharts(
|
||||||
|
projectName: string,
|
||||||
|
queryParams?: RequestQueryParams
|
||||||
|
): Observable<HelmChartItem[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an helmchart
|
||||||
|
* @param projectId Id of the project
|
||||||
|
* @param chartId ID of helmChart in this specific project
|
||||||
|
*/
|
||||||
|
abstract deleteHelmChart(projectId: number | string, chartId: number): Observable<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the versions of helmchart
|
||||||
|
* @param projectName Id of the project
|
||||||
|
* @param chartName ID of the helm chart
|
||||||
|
* @param queryParams option params for query
|
||||||
|
*/
|
||||||
|
abstract getChartVersions(
|
||||||
|
projectName: string,
|
||||||
|
chartName: string,
|
||||||
|
): Observable<HelmChartVersion[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a version of helmchart
|
||||||
|
* @param projectName ID of the project
|
||||||
|
* @param chartName ID of the chart you want to delete
|
||||||
|
* @param version name of the version
|
||||||
|
*/
|
||||||
|
abstract deleteChartVersion(projectName: string, chartName: string, version: string): Observable<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the all details of an helmchart
|
||||||
|
* @param projectName ID of the project
|
||||||
|
* @param chartname ID of the chart
|
||||||
|
* @param version name of the chart's version
|
||||||
|
* @param queryParams options
|
||||||
|
*/
|
||||||
|
abstract getChartDetail(
|
||||||
|
projectName: string,
|
||||||
|
chartname: string,
|
||||||
|
version: string,
|
||||||
|
): Observable<HelmChartDetail>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download an specific verison
|
||||||
|
* @param projectName ID of the project
|
||||||
|
* @param filename ID of the helm chart
|
||||||
|
* @param version Name of version
|
||||||
|
* @param queryParams options
|
||||||
|
*/
|
||||||
|
abstract downloadChart(
|
||||||
|
projectName: string,
|
||||||
|
filename: string,
|
||||||
|
): Observable<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload chart and prov files to chartmuseam
|
||||||
|
* @param projectName Name of the project
|
||||||
|
* @param chart chart file
|
||||||
|
* @param prov prov file
|
||||||
|
*/
|
||||||
|
abstract uploadChart (
|
||||||
|
projectName: string,
|
||||||
|
chart: File,
|
||||||
|
prov: File
|
||||||
|
): Observable<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement default service for helm chart.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class HelmChartDefaultService extends HelmChartService {
|
||||||
|
constructor(
|
||||||
|
private http: Http,
|
||||||
|
@Inject(SERVICE_CONFIG) private config: IServiceConfig
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractData(res: Response) {
|
||||||
|
if (res.text() === "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return res.json() || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractHelmItems(res: Response) {
|
||||||
|
if (res.text() === "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
let charts = res.json();
|
||||||
|
if (charts) {
|
||||||
|
return charts.map( chart => {
|
||||||
|
return {
|
||||||
|
name: chart.Name,
|
||||||
|
total_versions: chart.total_versions,
|
||||||
|
created: chart.Created,
|
||||||
|
icon: chart.Icon,
|
||||||
|
home: chart.Home};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleErrorObservable(error: Response | any) {
|
||||||
|
console.error(error.message || error);
|
||||||
|
return Observable.throw(error.message || error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getHelmCharts(
|
||||||
|
projectName: string,
|
||||||
|
): Observable<HelmChartItem[]> {
|
||||||
|
if (!projectName) {
|
||||||
|
return Observable.throw("Bad argument, No project id to get helm charts");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.get(`${this.config.helmChartEndpoint}/${projectName}/charts`, HTTP_GET_OPTIONS)
|
||||||
|
.map(response => {
|
||||||
|
return this.extractHelmItems(response);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
return this.handleErrorObservable(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteHelmChart(projectId: number | string, chartId: number): any {
|
||||||
|
if (!chartId) {
|
||||||
|
Observable.throw("Bad argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.delete(`${this.config.helmChartEndpoint}/${projectId}/${chartId}`)
|
||||||
|
.map(response => {
|
||||||
|
return this.extractData(response);
|
||||||
|
})
|
||||||
|
.catch(this.handleErrorObservable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getChartVersions(
|
||||||
|
projectName: string,
|
||||||
|
chartName: string,
|
||||||
|
): Observable<HelmChartVersion[]> {
|
||||||
|
return this.http.get(`${this.config.helmChartEndpoint}/${projectName}/charts/${chartName}`, HTTP_GET_OPTIONS)
|
||||||
|
.map(response => {
|
||||||
|
return this.extractData(response);
|
||||||
|
})
|
||||||
|
.catch(this.handleErrorObservable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteChartVersion(projectName: string, chartName: string, version: string): any {
|
||||||
|
return this.http.delete(`${this.config.helmChartEndpoint}/${projectName}/charts/${chartName}/${version}`, HTTP_JSON_OPTIONS)
|
||||||
|
.map(response => {
|
||||||
|
return this.extractData(response);
|
||||||
|
})
|
||||||
|
.catch(this.handleErrorObservable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getChartDetail (
|
||||||
|
projectName: string,
|
||||||
|
chartName: string,
|
||||||
|
version: string,
|
||||||
|
): Observable<HelmChartDetail> {
|
||||||
|
return this.http.get(`${this.config.helmChartEndpoint}/${projectName}/charts/${chartName}/${version}`)
|
||||||
|
.map(response => {
|
||||||
|
return this.extractData(response);
|
||||||
|
})
|
||||||
|
.catch(this.handleErrorObservable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public downloadChart(
|
||||||
|
projectName: string,
|
||||||
|
filename: string,
|
||||||
|
): Observable<any> {
|
||||||
|
return this.http.get(`${this.config.downloadChartEndpoint}/${projectName}/${filename}`, {
|
||||||
|
responseType: ResponseContentType.Blob,
|
||||||
|
})
|
||||||
|
.map(response => {
|
||||||
|
return {
|
||||||
|
filename: filename.split('/')[1],
|
||||||
|
data: response.blob()
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(this.handleErrorObservable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uploadChart(
|
||||||
|
projectName: string,
|
||||||
|
chart?: File,
|
||||||
|
prov?: File
|
||||||
|
): Observable<any> {
|
||||||
|
let formData = new FormData();
|
||||||
|
let uploadURL = `${this.config.helmChartEndpoint}/${projectName}/charts`;
|
||||||
|
if (chart) {
|
||||||
|
formData.append('chart', chart);
|
||||||
|
}
|
||||||
|
if (prov) {
|
||||||
|
formData.append('prov', prov);
|
||||||
|
if (!chart) {
|
||||||
|
uploadURL = `${this.config.helmChartEndpoint}/${projectName}/prov`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.http.post(uploadURL, formData)
|
||||||
|
.map(reponse => this.extractData(reponse))
|
||||||
|
.catch(this.handleErrorObservable);
|
||||||
|
}
|
||||||
|
}
|
@ -11,3 +11,4 @@ export * from './configuration.service';
|
|||||||
export * from './job-log.service';
|
export * from './job-log.service';
|
||||||
export * from './project.service';
|
export * from './project.service';
|
||||||
export * from './label.service';
|
export * from './label.service';
|
||||||
|
export * from './helm-chart.service';
|
||||||
|
@ -296,3 +296,80 @@ export interface ScrollPosition {
|
|||||||
sT: number;
|
sT: number;
|
||||||
cH: number;
|
cH: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HelmChartItem {
|
||||||
|
name: string;
|
||||||
|
total_versions: number;
|
||||||
|
created: string;
|
||||||
|
icon: string;
|
||||||
|
home: string;
|
||||||
|
status?: string;
|
||||||
|
pulls?: number;
|
||||||
|
maintainer?: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartVersion {
|
||||||
|
name: string;
|
||||||
|
home: string;
|
||||||
|
sources: string[];
|
||||||
|
version: string;
|
||||||
|
description: string;
|
||||||
|
keywords: string[];
|
||||||
|
maintainers: HelmChartMaintainer[];
|
||||||
|
engine: string;
|
||||||
|
icon: string;
|
||||||
|
appVersion: string;
|
||||||
|
urls: string[];
|
||||||
|
created: string;
|
||||||
|
digest: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartDetail {
|
||||||
|
metadata: HelmChartMetaData;
|
||||||
|
dependencies: HelmChartDependency[];
|
||||||
|
values: any;
|
||||||
|
files: HelmchartFile;
|
||||||
|
security: HelmChartSecurity;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartMetaData {
|
||||||
|
name: string;
|
||||||
|
home: string;
|
||||||
|
sources: string[];
|
||||||
|
version: string;
|
||||||
|
description: string;
|
||||||
|
keywords: string[];
|
||||||
|
maintainers: HelmChartMaintainer[];
|
||||||
|
engine: string;
|
||||||
|
icon: string;
|
||||||
|
appVersion: string;
|
||||||
|
urls: string[];
|
||||||
|
created?: string;
|
||||||
|
digest: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartMaintainer {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartDependency {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
repository: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmchartFile {
|
||||||
|
"README.MD": string;
|
||||||
|
"values.yaml": string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartSecurity {
|
||||||
|
signature: HelmChartSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelmChartSignature {
|
||||||
|
signed: boolean;
|
||||||
|
prov_file: string;
|
||||||
|
}
|
||||||
|
@ -40,7 +40,8 @@ export const enum ConfirmationTargets {
|
|||||||
TAG,
|
TAG,
|
||||||
CONFIG,
|
CONFIG,
|
||||||
CONFIG_ROUTE,
|
CONFIG_ROUTE,
|
||||||
CONFIG_TAB
|
CONFIG_TAB,
|
||||||
|
HELM_CHART
|
||||||
};
|
};
|
||||||
|
|
||||||
export const enum ActionType {
|
export const enum ActionType {
|
||||||
@ -87,3 +88,7 @@ export const LabelColor = [
|
|||||||
{ 'color': '#F52F52', 'textColor': 'black' }, { 'color': '#FF5501', 'textColor': 'black' },
|
{ 'color': '#F52F52', 'textColor': 'black' }, { 'color': '#FF5501', 'textColor': 'black' },
|
||||||
{ 'color': '#F57600', 'textColor': 'black' }, { 'color': '#FFDC0B', 'textColor': 'black' },
|
{ 'color': '#F57600', 'textColor': 'black' }, { 'color': '#FFDC0B', 'textColor': 'black' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
||||||
|
|
||||||
|
export const DefaultHelmIcon = '/static/images/helm-logo.svg';
|
||||||
|
@ -5,6 +5,7 @@ import { ClarityModule } from 'clarity-angular';
|
|||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { TranslateModule, TranslateLoader, MissingTranslationHandler } from '@ngx-translate/core';
|
import { TranslateModule, TranslateLoader, MissingTranslationHandler } from '@ngx-translate/core';
|
||||||
import { CookieService, CookieModule } from 'ngx-cookie';
|
import { CookieService, CookieModule } from 'ngx-cookie';
|
||||||
|
import { MarkdownModule } from 'ngx-markdown';
|
||||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||||
|
|
||||||
import { ClipboardModule } from '../third-party/ngx-clipboard/index';
|
import { ClipboardModule } from '../third-party/ngx-clipboard/index';
|
||||||
@ -46,6 +47,7 @@ export function GeneralTranslatorLoader(http: Http, config: IServiceConfig) {
|
|||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
CookieModule.forRoot(),
|
CookieModule.forRoot(),
|
||||||
ClarityModule.forRoot(),
|
ClarityModule.forRoot(),
|
||||||
|
MarkdownModule.forRoot(),
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
@ -63,9 +65,10 @@ export function GeneralTranslatorLoader(http: Http, config: IServiceConfig) {
|
|||||||
HttpModule,
|
HttpModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
CookieModule,
|
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
ClarityModule,
|
ClarityModule,
|
||||||
|
CookieModule,
|
||||||
|
MarkdownModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
],
|
],
|
||||||
providers: [CookieService]
|
providers: [CookieService]
|
||||||
|
@ -45,27 +45,3 @@ export const errorHandler = function (error: any): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CancelablePromise<T> {
|
|
||||||
|
|
||||||
constructor(promise: Promise<T>) {
|
|
||||||
this.wrappedPromise = new Promise((resolve, reject) => {
|
|
||||||
promise.then((val) =>
|
|
||||||
this.isCanceled ? reject({isCanceled: true}) : resolve(val)
|
|
||||||
);
|
|
||||||
promise.catch((error) =>
|
|
||||||
this.isCanceled ? reject({isCanceled: true}) : reject(error)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private wrappedPromise: Promise<T>;
|
|
||||||
private isCanceled: boolean;
|
|
||||||
getPromise(): Promise<T> {
|
|
||||||
return this.wrappedPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.isCanceled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -57,6 +57,12 @@ export const HTTP_GET_OPTIONS: RequestOptions = new RequestOptions({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const FILE_UPLOAD_OPTION: RequestOptions = new RequestOptions({
|
||||||
|
headers: new Headers({
|
||||||
|
"Content-Type": 'multipart/form-data',
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build http request options
|
* Build http request options
|
||||||
*
|
*
|
||||||
@ -288,3 +294,15 @@ export function clone(srcObj: any): any {
|
|||||||
if (!srcObj) { return null; };
|
if (!srcObj) { return null; };
|
||||||
return JSON.parse(JSON.stringify(srcObj));
|
return JSON.parse(JSON.stringify(srcObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function downloadFile(fileData) {
|
||||||
|
let url = window.URL.createObjectURL(fileData.data);
|
||||||
|
let a = document.createElement("a");
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.setAttribute("style", "display: none");
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileData.filename;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
a.remove();
|
||||||
|
};
|
||||||
|
13292
src/ui_ng/package-lock.json
generated
Normal file
13292
src/ui_ng/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "ng serve --ssl 1 --ssl-key ssl/server.key --ssl-cert ssl/server.crt --host 0.0.0.0 --proxy-config proxy.config.json",
|
"start": "ng serve --ssl 1 --ssl-key ssl/server.key --ssl-cert ssl/server.crt --host 0.0.0.0 --proxy-config proxy.config.json",
|
||||||
"lint": "tslint \"src/**/*.ts\"",
|
"lint": "tslint \"src/**/*.ts\"",
|
||||||
"lint:lib": "tslint \"lib/**/*.ts\"",
|
"lint:lib": "tslint \"lib/**/*.ts\" -e \"lib/dist/**/*\" ",
|
||||||
"test": "ng test --single-run",
|
"test": "ng test --single-run",
|
||||||
"pree2e": "webdriver-manager update",
|
"pree2e": "webdriver-manager update",
|
||||||
"e2e": "protractor",
|
"e2e": "protractor",
|
||||||
@ -35,6 +35,7 @@
|
|||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
"ngx-markdown": "1.5.2",
|
||||||
"rxjs": "^5.0.1",
|
"rxjs": "^5.0.1",
|
||||||
"ts-helpers": "^1.1.1",
|
"ts-helpers": "^1.1.1",
|
||||||
"web-animations-js": "^2.2.1",
|
"web-animations-js": "^2.2.1",
|
||||||
|
@ -21,7 +21,11 @@ export default {
|
|||||||
plugins: [
|
plugins: [
|
||||||
nodeResolve({jsnext: true, module: true, browser: true}),
|
nodeResolve({jsnext: true, module: true, browser: true}),
|
||||||
commonjs({
|
commonjs({
|
||||||
include: ['node_modules/**'],
|
namedExports: {
|
||||||
|
'node_modules/ngx-markdown/dist/lib/index.js': ['MarkdownModule']
|
||||||
|
},
|
||||||
|
include: ['node_modules/**',
|
||||||
|
'node_modules/ngx-markdown/**'],
|
||||||
}),
|
}),
|
||||||
uglify()
|
uglify()
|
||||||
]
|
]
|
||||||
|
@ -18,6 +18,7 @@ import { HttpModule } from '@angular/http';
|
|||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ClarityModule } from 'clarity-angular';
|
import { ClarityModule } from 'clarity-angular';
|
||||||
import { CookieModule } from 'ngx-cookie';
|
import { CookieModule } from 'ngx-cookie';
|
||||||
|
import { MarkdownModule } from 'ngx-markdown';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -26,6 +27,7 @@ import { CookieModule } from 'ngx-cookie';
|
|||||||
HttpModule,
|
HttpModule,
|
||||||
ClarityModule.forRoot(),
|
ClarityModule.forRoot(),
|
||||||
CookieModule.forRoot(),
|
CookieModule.forRoot(),
|
||||||
|
MarkdownModule.forRoot(),
|
||||||
BrowserAnimationsModule
|
BrowserAnimationsModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
@ -33,7 +35,8 @@ import { CookieModule } from 'ngx-cookie';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
HttpModule,
|
HttpModule,
|
||||||
ClarityModule,
|
ClarityModule,
|
||||||
BrowserAnimationsModule
|
BrowserAnimationsModule,
|
||||||
|
MarkdownModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreModule {
|
export class CoreModule {
|
||||||
|
@ -47,6 +47,9 @@ import { MemberComponent } from './project/member/member.component';
|
|||||||
import {ProjectLabelComponent} from "./project/project-label/project-label.component";
|
import {ProjectLabelComponent} from "./project/project-label/project-label.component";
|
||||||
import { ProjectConfigComponent } from './project/project-config/project-config.component';
|
import { ProjectConfigComponent } from './project/project-config/project-config.component';
|
||||||
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
||||||
|
import { ListChartsComponent } from './project/list-charts/list-charts.component';
|
||||||
|
import { ListChartVersionsComponent } from './project/list-chart-versions/list-chart-versions.component';
|
||||||
|
import { ChartDetailComponent } from './project/chart-detail/chart-detail.component';
|
||||||
|
|
||||||
const harborRoutes: Routes = [
|
const harborRoutes: Routes = [
|
||||||
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
||||||
@ -116,6 +119,22 @@ const harborRoutes: Routes = [
|
|||||||
projectResolver: ProjectRoutingResolver
|
projectResolver: ProjectRoutingResolver
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/helm-charts/:chart/versions',
|
||||||
|
component: ListChartVersionsComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/helm-charts/:chart/versions/:version',
|
||||||
|
component: ChartDetailComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'projects/:id',
|
path: 'projects/:id',
|
||||||
component: ProjectDetailComponent,
|
component: ProjectDetailComponent,
|
||||||
@ -128,6 +147,10 @@ const harborRoutes: Routes = [
|
|||||||
path: 'repositories',
|
path: 'repositories',
|
||||||
component: RepositoryPageComponent
|
component: RepositoryPageComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'helm-charts',
|
||||||
|
component: ListChartsComponent
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'repositories/:repo/tags',
|
path: 'repositories/:repo/tags',
|
||||||
component: TagRepositoryComponent,
|
component: TagRepositoryComponent,
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
<div>
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a (click)="gotoProjectList()"> {{ 'SIDE_NAV.PROJECTS'| translate}} </a>
|
||||||
|
<
|
||||||
|
<a (click)="gotoChartList()">{{ 'HELM_CHART.HELMCHARTS'| translate}}</a>
|
||||||
|
<
|
||||||
|
<a (click)="gotoChartVersion()">{{ 'HELM_CHART.CHARTVERSIONS'| translate}}</a>
|
||||||
|
</div>
|
||||||
|
<hbr-chart-detail
|
||||||
|
[projectId]="projectId"
|
||||||
|
[project]="project"
|
||||||
|
[chartName]="chartName"
|
||||||
|
[chartVersion]="chartVersion"
|
||||||
|
[roleName]="roleName"
|
||||||
|
></hbr-chart-detail>
|
||||||
|
</div>
|
@ -0,0 +1,6 @@
|
|||||||
|
.breadcrumb a {
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #007cbb;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ChartDetailComponent } from './chart-detail.component';
|
||||||
|
|
||||||
|
describe('ChartDetailComponent', () => {
|
||||||
|
let component: ChartDetailComponent;
|
||||||
|
let fixture: ComponentFixture<ChartDetailComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ChartDetailComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ChartDetailComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,55 @@
|
|||||||
|
import { RoleMapping } from './../../shared/shared.const';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { Project } from '../project';
|
||||||
|
import { SessionService } from './../../shared/session.service';
|
||||||
|
import { SessionUser } from './../../shared/session-user';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "project-chart-detail",
|
||||||
|
templateUrl: "./chart-detail.component.html",
|
||||||
|
styleUrls: ["./chart-detail.component.scss"]
|
||||||
|
})
|
||||||
|
export class ChartDetailComponent implements OnInit {
|
||||||
|
|
||||||
|
projectId: number | string;
|
||||||
|
project: Project;
|
||||||
|
chartName: string;
|
||||||
|
chartVersion: string;
|
||||||
|
currentUser: SessionUser;
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
roleName: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private session: SessionService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
// Get projectId from route params snapshot.
|
||||||
|
this.projectId = +this.route.snapshot.params['id'];
|
||||||
|
this.chartName = this.route.snapshot.params['chart'];
|
||||||
|
this.chartVersion = this.route.snapshot.params['version'];
|
||||||
|
// Get current user from registered resolver.
|
||||||
|
this.currentUser = this.session.getCurrentUser();
|
||||||
|
let resolverData = this.route.snapshot.data;
|
||||||
|
if (resolverData) {
|
||||||
|
this.project = <Project>(resolverData["projectResolver"]);
|
||||||
|
this.roleName = RoleMapping[this.project.role_name];
|
||||||
|
this.hasProjectAdminRole = this.project.has_project_admin_role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoProjectList() {
|
||||||
|
this.router.navigateByUrl("/harbor/projects");
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoChartList() {
|
||||||
|
this.router.navigateByUrl(`/harbor/projects/${this.projectId}/helm-charts`);
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoChartVersion() {
|
||||||
|
this.router.navigateByUrl(`/harbor/projects/${this.projectId}/helm-charts/${this.chartName}/versions`);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<div>
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<a href="javascript:void(0)" (click)="gotoProjectList()"> {{ 'SIDE_NAV.PROJECTS'| translate}} </a>
|
||||||
|
<
|
||||||
|
<a href="javascript:void(0)" (click)="gotoChartList()">{{ 'HELM_CHART.HELMCHARTS'| translate}}</a>
|
||||||
|
</div>
|
||||||
|
<hbr-helm-chart-version
|
||||||
|
[projectName]='projectName'
|
||||||
|
[chartName]='chartName'
|
||||||
|
[roleName]='roleName'
|
||||||
|
[hasSignedIn]='hasSignedIn'
|
||||||
|
[hasProjectAdminRole]='hasProjectAdminRole'
|
||||||
|
(versionClickEvt)='onVersionClick($event)'
|
||||||
|
(backEvt)='gotoChartList()'>
|
||||||
|
</hbr-helm-chart-version>
|
||||||
|
</div>
|
@ -0,0 +1,6 @@
|
|||||||
|
.breadcrumb a {
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #007cbb;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ListChartVersionsComponent } from './list-chart-versions.component';
|
||||||
|
|
||||||
|
describe('ListChartVersionsComponent', () => {
|
||||||
|
let component: ListChartVersionsComponent;
|
||||||
|
let fixture: ComponentFixture<ListChartVersionsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ListChartVersionsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ListChartVersionsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,60 @@
|
|||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { Project } from './../project';
|
||||||
|
import { SessionUser } from './../../shared/session-user';
|
||||||
|
import { SessionService } from './../../shared/session.service';
|
||||||
|
import { RoleMapping } from '../../shared/shared.const';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'list-chart-version',
|
||||||
|
templateUrl: './list-chart-versions.component.html',
|
||||||
|
styleUrls: ['./list-chart-versions.component.scss']
|
||||||
|
})
|
||||||
|
export class ListChartVersionsComponent implements OnInit {
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
projectId: number;
|
||||||
|
projectName: string;
|
||||||
|
chartName: string;
|
||||||
|
roleName: string;
|
||||||
|
|
||||||
|
hasSignedIn: boolean;
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
currentUser: SessionUser;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private session: SessionService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
// Get projectId from route params snapshot.
|
||||||
|
this.projectId = +this.route.snapshot.params['id'];
|
||||||
|
this.chartName = this.route.snapshot.params['chart'];
|
||||||
|
// Get current user from registered resolver.
|
||||||
|
this.currentUser = this.session.getCurrentUser();
|
||||||
|
let resolverData = this.route.snapshot.data;
|
||||||
|
if (resolverData) {
|
||||||
|
let project = <Project>(resolverData["projectResolver"]);
|
||||||
|
this.hasProjectAdminRole = project.has_project_admin_role;
|
||||||
|
this.roleName = RoleMapping[project.role_name];
|
||||||
|
this.projectName = project.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onVersionClick(version: string) {
|
||||||
|
this.router.navigateByUrl(`${this.router.url}/${version}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoProjectList() {
|
||||||
|
this.router.navigateByUrl('/harbor/projects');
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoChartList() {
|
||||||
|
this.router.navigateByUrl(`/harbor/projects/${this.projectId}/helm-charts`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<hbr-helm-chart
|
||||||
|
[projectId]='projectId'
|
||||||
|
[projectName]='projectName'
|
||||||
|
[urlPrefix]='urlPrefix'
|
||||||
|
[hasSignedIn]='hasSignedIn'
|
||||||
|
[hasProjectAdminRole]='hasProjectAdminRole'
|
||||||
|
(chartClickEvt)='onChartClick($event)'>
|
||||||
|
</hbr-helm-chart>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ListChartsComponent } from './list-charts.component';
|
||||||
|
|
||||||
|
describe('ListChartsComponent', () => {
|
||||||
|
let component: ListChartsComponent;
|
||||||
|
let fixture: ComponentFixture<ListChartsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ListChartsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ListChartsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,44 @@
|
|||||||
|
import { Project } from '../../project/project';
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { SessionService } from './../../shared/session.service';
|
||||||
|
import { SessionUser } from './../../shared/session-user';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "project-list-charts",
|
||||||
|
templateUrl: "./list-charts.component.html",
|
||||||
|
styleUrls: ["./list-charts.component.scss"]
|
||||||
|
})
|
||||||
|
export class ListChartsComponent implements OnInit {
|
||||||
|
projectId: number;
|
||||||
|
|
||||||
|
projectName: string;
|
||||||
|
urlPrefix: string;
|
||||||
|
hasSignedIn: boolean;
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
currentUser: SessionUser;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private session: SessionService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
// Get projectId from route params snapshot.
|
||||||
|
this.projectId = +this.route.snapshot.parent.params["id"];
|
||||||
|
// Get current user from registered resolver.
|
||||||
|
this.currentUser = this.session.getCurrentUser();
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if (resolverData) {
|
||||||
|
let project = <Project>(resolverData["projectResolver"]);
|
||||||
|
this.projectName = project.name;
|
||||||
|
this.hasProjectAdminRole = project.has_project_admin_role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartClick(chartName: string) {
|
||||||
|
this.router.navigateByUrl(`${this.router.url}/${chartName}/versions`);
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,28 @@
|
|||||||
<clr-datagrid (clrDgRefresh)="clrLoad($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
<clr-datagrid (clrDgRefresh)="clrLoad($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" (click)="addNewProject()" *ngIf="projectCreationRestriction"><clr-icon shape="plus" size="16"></clr-icon> {{'PROJECT.NEW_PROJECT' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" (click)="addNewProject()" *ngIf="projectCreationRestriction">
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length && (isSystemAdmin || canDelete))" (click)="deleteProjects(selectedRow)" ><clr-icon shape="times" size="16"></clr-icon> {{'PROJECT.DELETE' | translate}}</button>
|
<clr-icon shape="plus" size="16"></clr-icon> {{'PROJECT.NEW_PROJECT' | translate}}</button>
|
||||||
</clr-dg-action-bar>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length && (isSystemAdmin || canDelete))"
|
||||||
|
(click)="deleteProjects(selectedRow)">
|
||||||
|
<clr-icon shape="times" size="16"></clr-icon> {{'PROJECT.DELETE' | translate}}</button>
|
||||||
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'name'">{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'name'">{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="accessLevelComparator">{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="accessLevelComparator">{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column *ngIf="showRoleInfo" [clrDgSortBy]="roleComparator">{{'PROJECT.ROLE' | translate}}</clr-dg-column>
|
<clr-dg-column *ngIf="showRoleInfo" [clrDgSortBy]="roleComparator">{{'PROJECT.ROLE' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="repoCountComparator">{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="repoCountComparator">{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="timeComparator">{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="timeComparator">{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
<clr-dg-row *ngFor="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)" (click)="goToLink(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.metadata.public === 'true' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
||||||
<clr-dg-cell *ngIf="showRoleInfo">{{roleInfo[p.current_user_role_id] | translate}}</clr-dg-cell>
|
<clr-dg-cell *ngIf="showRoleInfo">{{roleInfo[p.current_user_role_id] | translate}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{p.creation_time | date: 'short'}}</clr-dg-cell>
|
<clr-dg-cell>{{p.creation_time | date: 'short'}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'PROJECT.OF' | translate}} </span> {{pagination.totalItems }} {{'PROJECT.ITEMS' | translate}}
|
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'PROJECT.OF' | translate}} </span> {{pagination.totalItems
|
||||||
|
}} {{'PROJECT.ITEMS' | translate}}
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize" [(clrDgPage)]="currentPage" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize" [(clrDgPage)]="currentPage" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
@ -7,6 +7,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" routerLink="repositories" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
<a class="nav-link" routerLink="repositories" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" routerLink="helm-charts" routerLinkActive="active">{{'PROJECT_DETAIL.HELMCHART' | translate}}</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||||
<a class="nav-link" routerLink="members" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
<a class="nav-link" routerLink="members" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -34,6 +34,9 @@ import { ProjectRoutingResolver } from './project-routing-resolver.service';
|
|||||||
|
|
||||||
import { TargetExistsValidatorDirective } from '../shared/target-exists-directive';
|
import { TargetExistsValidatorDirective } from '../shared/target-exists-directive';
|
||||||
import { ProjectLabelComponent } from "../project/project-label/project-label.component";
|
import { ProjectLabelComponent } from "../project/project-label/project-label.component";
|
||||||
|
import { ListChartsComponent } from './list-charts/list-charts.component';
|
||||||
|
import { ListChartVersionsComponent } from './list-chart-versions/list-chart-versions.component';
|
||||||
|
import { ChartDetailComponent } from './chart-detail/chart-detail.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -52,7 +55,10 @@ import { ProjectLabelComponent } from "../project/project-label/project-label.co
|
|||||||
AddMemberComponent,
|
AddMemberComponent,
|
||||||
TargetExistsValidatorDirective,
|
TargetExistsValidatorDirective,
|
||||||
ProjectLabelComponent,
|
ProjectLabelComponent,
|
||||||
AddGroupComponent
|
AddGroupComponent,
|
||||||
|
ListChartsComponent,
|
||||||
|
ListChartVersionsComponent,
|
||||||
|
ChartDetailComponent
|
||||||
],
|
],
|
||||||
exports: [ProjectComponent, ListProjectComponent],
|
exports: [ProjectComponent, ListProjectComponent],
|
||||||
providers: [ProjectRoutingResolver, ProjectService, MemberService]
|
providers: [ProjectRoutingResolver, ProjectService, MemberService]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="arrow-block" *ngIf="!withAdmiral">
|
<div class="breadcrumb" *ngIf="!withAdmiral">
|
||||||
<a (click)="goProBack()">< {{'SIDE_NAV.PROJECTS'| translate}}</a>
|
<a (click)="goProBack()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
|
||||||
<a (click)="watchGoBackEvt(projectId)">< {{'REPOSITORY.REPOSITORIES'| translate}}</a>
|
<a (click)="watchGoBackEvt(projectId)">< {{'REPOSITORY.REPOSITORIES'| translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
<hbr-repository [repoName]="repoName" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isGuest]="isGuest"
|
<hbr-repository [repoName]="repoName" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isGuest]="isGuest"
|
||||||
(tagClickEvent)="watchTagClickEvt($event)" (backEvt)="watchGoBackEvt($event)"></hbr-repository>
|
(tagClickEvent)="watchTagClickEvt($event)" (backEvt)="watchGoBackEvt($event)"></hbr-repository>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
.sub-header-title {
|
.breadcrumb a {
|
||||||
margin-top: 12px;
|
text-decoration: none;
|
||||||
}
|
cursor: pointer;
|
||||||
.arrow-block a{text-decoration: none; cursor: pointer; cursor: pointer; color: #007cbb; font-size: 12px;}
|
color: #007cbb;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
@ -58,7 +58,23 @@ const uiLibConfig: IServiceConfig = {
|
|||||||
langCookieKey: "harbor-lang",
|
langCookieKey: "harbor-lang",
|
||||||
langMessageLoader: "http",
|
langMessageLoader: "http",
|
||||||
langMessagePathForHttpLoader: "i18n/lang/",
|
langMessagePathForHttpLoader: "i18n/lang/",
|
||||||
langMessageFileSuffixForHttpLoader: "-lang.json"
|
langMessageFileSuffixForHttpLoader: "-lang.json",
|
||||||
|
systemInfoEndpoint: "/api/systeminfo",
|
||||||
|
repositoryBaseEndpoint: "/api/repositories",
|
||||||
|
logBaseEndpoint: "/api/logs",
|
||||||
|
targetBaseEndpoint: "/api/targets",
|
||||||
|
replicationBaseEndpoint: "/api/replications",
|
||||||
|
replicationRuleEndpoint: "/api/policies/replication",
|
||||||
|
replicationJobEndpoint: "/api/jobs/replication",
|
||||||
|
vulnerabilityScanningBaseEndpoint: "/api/repositories",
|
||||||
|
projectPolicyEndpoint: "/api/projects/configs",
|
||||||
|
projectBaseEndpoint: "/api/projects",
|
||||||
|
localI18nMessageVariableMap: {},
|
||||||
|
configurationEndpoint: "/api/configurations",
|
||||||
|
scanJobEndpoint: "/api/jobs/scan",
|
||||||
|
labelEndpoint: "/api/labels",
|
||||||
|
helmChartEndpoint: "/api/chartrepo",
|
||||||
|
downloadChartEndpoint: "/chartrepo"
|
||||||
};
|
};
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -185,7 +185,8 @@
|
|||||||
"LOGS": "Logs",
|
"LOGS": "Logs",
|
||||||
"LABELS": "Labels",
|
"LABELS": "Labels",
|
||||||
"PROJECTS": "Projects",
|
"PROJECTS": "Projects",
|
||||||
"CONFIG": "Configuration"
|
"CONFIG": "Configuration",
|
||||||
|
"HELMCHART": "Helm Charts"
|
||||||
},
|
},
|
||||||
"PROJECT_CONFIG": {
|
"PROJECT_CONFIG": {
|
||||||
"REGISTRY": "Project registry",
|
"REGISTRY": "Project registry",
|
||||||
@ -311,7 +312,6 @@
|
|||||||
"TEST_CONNECTION_SUCCESS": "Connection tested successfully.",
|
"TEST_CONNECTION_SUCCESS": "Connection tested successfully.",
|
||||||
"TEST_CONNECTION_FAILURE": "Failed to ping endpoint.",
|
"TEST_CONNECTION_FAILURE": "Failed to ping endpoint.",
|
||||||
"NAME": "Name",
|
"NAME": "Name",
|
||||||
"STATUS": "Status",
|
|
||||||
"PROJECT": "Project",
|
"PROJECT": "Project",
|
||||||
"NAME_IS_REQUIRED": "Name is required.",
|
"NAME_IS_REQUIRED": "Name is required.",
|
||||||
"DESCRIPTION": "Description",
|
"DESCRIPTION": "Description",
|
||||||
@ -470,6 +470,50 @@
|
|||||||
"DEPLOY": "DEPLOY",
|
"DEPLOY": "DEPLOY",
|
||||||
"ADDITIONAL_INFO": "Add Additional Info"
|
"ADDITIONAL_INFO": "Add Additional Info"
|
||||||
},
|
},
|
||||||
|
"HELM_CHART": {
|
||||||
|
"HELMCHARTS": "Charts",
|
||||||
|
"CHARTVERSIONS": "Versions",
|
||||||
|
"UPLOAD_TITLE": "Upload chart files",
|
||||||
|
"CHART_FILE": "Chart File",
|
||||||
|
"CHART_PROV": "Prov File",
|
||||||
|
"DOWNLOAD": "Download",
|
||||||
|
"SUMMARY": "Summary",
|
||||||
|
"DEPENDENCIES": "Dependencies",
|
||||||
|
"VALUES": "Values",
|
||||||
|
"OVERVIEW": "Overview",
|
||||||
|
"HOME": "Home",
|
||||||
|
"SRC_REPO": "Srouce Repository",
|
||||||
|
"CREATED": "Created Time",
|
||||||
|
"MAINTAINERS": "Maintainers",
|
||||||
|
"PULLS": "Pull Count",
|
||||||
|
"VERSION": "Version",
|
||||||
|
"INSTALL": "Install",
|
||||||
|
"INSTALL_CHART": "Install Chart",
|
||||||
|
"NAME": "Name",
|
||||||
|
"REPO": "Repository",
|
||||||
|
"FILTER_FOR_CHARTS": "Filter for charts",
|
||||||
|
"DELETE": "Delete",
|
||||||
|
"OF": "of",
|
||||||
|
"VERSIONS": "versions",
|
||||||
|
"IMAGES": "Images",
|
||||||
|
"ENGINE": "Engine",
|
||||||
|
"ACTION": "Action",
|
||||||
|
"UPLOAD": "Upload",
|
||||||
|
"DELETE_CHART_VERSION_TITLE": "Delete Chart Versions",
|
||||||
|
"DELETE_CHART_VERSION": "Do you want to delete version {{param}}?",
|
||||||
|
"IMPORT": "Import",
|
||||||
|
"EXPORT": "Export",
|
||||||
|
"ADD_REPO": "Add Repo",
|
||||||
|
"SHOW_KV": "Key Value Pairs",
|
||||||
|
"SHOW_YAML": "YAML File",
|
||||||
|
"PLACEHOLDER": "No Item",
|
||||||
|
"FILE_UPLOADED": "File upload successfully",
|
||||||
|
"SIGNED": "Signed",
|
||||||
|
"UNSIGNED": "Unsigned",
|
||||||
|
"ITEMS": "charts",
|
||||||
|
"NO_README": "No readme file provided by this charts.",
|
||||||
|
"SECURITY": "Security"
|
||||||
|
},
|
||||||
"ALERT": {
|
"ALERT": {
|
||||||
"FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet. Do you want to cancel?"
|
"FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet. Do you want to cancel?"
|
||||||
},
|
},
|
||||||
@ -725,6 +769,8 @@
|
|||||||
"DELETE_REPLICATION": "Delete replication",
|
"DELETE_REPLICATION": "Delete replication",
|
||||||
"DELETE_MEMBER": "Delete user member",
|
"DELETE_MEMBER": "Delete user member",
|
||||||
"DELETE_GROUP": "Delete group member",
|
"DELETE_GROUP": "Delete group member",
|
||||||
|
"DELETE_CHART_VERSION": "Delete Chart Version",
|
||||||
|
"DELETE_CHART": "Delete Chart",
|
||||||
"SWITCH_ROLE": "Switch role",
|
"SWITCH_ROLE": "Switch role",
|
||||||
"ADD_GROUP": "Add group member",
|
"ADD_GROUP": "Add group member",
|
||||||
"ADD_USER": "Add user member",
|
"ADD_USER": "Add user member",
|
||||||
|
@ -311,7 +311,6 @@
|
|||||||
"TEST_CONNECTION_SUCCESS": "Conexión comprobada satisfactoriamente.",
|
"TEST_CONNECTION_SUCCESS": "Conexión comprobada satisfactoriamente.",
|
||||||
"TEST_CONNECTION_FAILURE": "Fallo al conectar con el endpoint.",
|
"TEST_CONNECTION_FAILURE": "Fallo al conectar con el endpoint.",
|
||||||
"NAME": "Nombre",
|
"NAME": "Nombre",
|
||||||
"STATUS": "Status",
|
|
||||||
"PROJECT": "Proyecto",
|
"PROJECT": "Proyecto",
|
||||||
"NAME_IS_REQUIRED": "El nombre es obligatorio.",
|
"NAME_IS_REQUIRED": "El nombre es obligatorio.",
|
||||||
"DESCRIPTION": "Descripción",
|
"DESCRIPTION": "Descripción",
|
||||||
|
@ -292,7 +292,6 @@
|
|||||||
"TEST_CONNECTION_SUCCESS": "Connexion testée avec succès.",
|
"TEST_CONNECTION_SUCCESS": "Connexion testée avec succès.",
|
||||||
"TEST_CONNECTION_FAILURE": "Echec du ping du point final.",
|
"TEST_CONNECTION_FAILURE": "Echec du ping du point final.",
|
||||||
"NAME": "Nom",
|
"NAME": "Nom",
|
||||||
"STATUS": "Status",
|
|
||||||
"PROJECT": "Projet",
|
"PROJECT": "Projet",
|
||||||
"NAME_IS_REQUIRED": "Le nom est obligatoire.",
|
"NAME_IS_REQUIRED": "Le nom est obligatoire.",
|
||||||
"DESCRIPTION": "Description",
|
"DESCRIPTION": "Description",
|
||||||
|
@ -311,7 +311,6 @@
|
|||||||
"TEST_CONNECTION_SUCCESS": "测试连接成功。",
|
"TEST_CONNECTION_SUCCESS": "测试连接成功。",
|
||||||
"TEST_CONNECTION_FAILURE": "测试连接失败。",
|
"TEST_CONNECTION_FAILURE": "测试连接失败。",
|
||||||
"NAME": "名称",
|
"NAME": "名称",
|
||||||
"STATUS": "状态",
|
|
||||||
"PROJECT": "项目",
|
"PROJECT": "项目",
|
||||||
"NAME_IS_REQUIRED": "名称为必填项。",
|
"NAME_IS_REQUIRED": "名称为必填项。",
|
||||||
"DESCRIPTION": "描述",
|
"DESCRIPTION": "描述",
|
||||||
|
1
src/ui_ng/src/images/helm-logo.svg
Normal file
1
src/ui_ng/src/images/helm-logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 33 KiB |
@ -19,7 +19,7 @@ Library OperatingSystem
|
|||||||
|
|
||||||
*** Variables ***
|
*** Variables ***
|
||||||
${HARBOR_VERSION} v1.1.1
|
${HARBOR_VERSION} v1.1.1
|
||||||
${CLAIR_BUILDER} 1.4.0
|
${CLAIR_BUILDER} 1.6.0
|
||||||
${GOLANG_VERSION} 1.9.2
|
${GOLANG_VERSION} 1.9.2
|
||||||
|
|
||||||
*** Keywords ***
|
*** Keywords ***
|
||||||
|
@ -7,7 +7,17 @@ cd /harbor_src
|
|||||||
mv /harbor_resources/node_modules ./
|
mv /harbor_resources/node_modules ./
|
||||||
|
|
||||||
npm install -q --no-progress
|
npm install -q --no-progress
|
||||||
npm run lint
|
## Build harbor-ui and link it
|
||||||
npm run lint:lib
|
npm run build:lib
|
||||||
|
|
||||||
|
## Link harbor-ui
|
||||||
|
chmod -R +xr /harbor_src/lib/dist
|
||||||
|
cd /harbor_src/lib/dist
|
||||||
|
npm link
|
||||||
|
cd /harbor_src
|
||||||
|
npm link harbor-ui
|
||||||
|
|
||||||
npm run build
|
npm run build
|
||||||
npm run test > ./npm-ut-test-results
|
npm run test > ./npm-ut-test-results
|
||||||
|
|
||||||
|
rm -rf /harbor_src/node_modules
|
Loading…
Reference in New Issue
Block a user