mirror of https://github.com/goharbor/harbor.git
Refactor swagger ui (#17428)
Signed-off-by: AllForNothing <sshijun@vmware.com> Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
861ca553df
commit
83bce02e61
|
@ -21,6 +21,8 @@ src/portal/coverage/
|
||||||
src/portal/dist/
|
src/portal/dist/
|
||||||
src/portal/html-report/
|
src/portal/html-report/
|
||||||
src/portal/node_modules/
|
src/portal/node_modules/
|
||||||
|
src/portal/app-swagger-ui/node_modules/
|
||||||
|
src/portal/app-swagger-ui/webpack.dev.js
|
||||||
src/portal/typings/
|
src/portal/typings/
|
||||||
**/*npm-debug.log.*
|
**/*npm-debug.log.*
|
||||||
**/*yarn-error.log.*
|
**/*yarn-error.log.*
|
||||||
|
|
|
@ -29,9 +29,13 @@ RUN python -c 'import sys, yaml, json; y=yaml.load(sys.stdin.read()); print json
|
||||||
RUN cp swagger.yaml dist
|
RUN cp swagger.yaml dist
|
||||||
COPY ./LICENSE /build_dir/dist
|
COPY ./LICENSE /build_dir/dist
|
||||||
|
|
||||||
|
RUN cd app-swagger-ui && npm install --unsafe-perm
|
||||||
|
RUN cd app-swagger-ui && npm run build
|
||||||
|
|
||||||
FROM ${harbor_base_namespace}/harbor-portal-base:${harbor_base_image_version}
|
FROM ${harbor_base_namespace}/harbor-portal-base:${harbor_base_image_version}
|
||||||
|
|
||||||
COPY --from=nodeportal /build_dir/dist /usr/share/nginx/html
|
COPY --from=nodeportal /build_dir/dist /usr/share/nginx/html
|
||||||
|
COPY --from=nodeportal /build_dir/app-swagger-ui/dist /usr/share/nginx/html
|
||||||
COPY --from=nodeportal /build_dir/package*.json /usr/share/nginx/
|
COPY --from=nodeportal /build_dir/package*.json /usr/share/nginx/
|
||||||
|
|
||||||
VOLUME /var/cache/nginx /var/log/nginx /run
|
VOLUME /var/cache/nginx /var/log/nginx /run
|
||||||
|
|
|
@ -40,6 +40,10 @@ http {
|
||||||
gzip_proxied expired no-cache no-store private auth;
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
|
location /devcenter-api-2.0 {
|
||||||
|
try_files $uri $uri/ /swagger-ui-index.html;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
@ -48,4 +52,4 @@ http {
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"ignorePatterns": [
|
"ignorePatterns": [
|
||||||
"projects/**/*"
|
"projects/**/*",
|
||||||
|
"**/*.js"
|
||||||
],
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
"no-empty-source": null
|
"no-empty-source": null
|
||||||
},
|
},
|
||||||
"ignoreFiles": [
|
"ignoreFiles": [
|
||||||
"src/**/*.html"
|
"**/*.md",
|
||||||
|
"src/**/*.html",
|
||||||
|
"app-swagger-ui/**/*.html"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
```text
|
```text
|
||||||
{
|
{
|
||||||
"name": "harbor",
|
"name": "harbor",
|
||||||
"version": "2.5.0",
|
"version": "2.7.0",
|
||||||
"description": "Harbor UI with Clarity",
|
"description": "Harbor UI with Clarity",
|
||||||
"angular-cli": {},
|
"angular-cli": {},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -54,16 +54,16 @@
|
||||||
"@angular/localize": "~14.1.0",
|
"@angular/localize": "~14.1.0",
|
||||||
"@angular/platform-browser": "~14.1.0",
|
"@angular/platform-browser": "~14.1.0",
|
||||||
"@angular/platform-browser-dynamic": "~14.1.0",
|
"@angular/platform-browser-dynamic": "~14.1.0",
|
||||||
"@angular/router": "~13.2.2",
|
"@angular/router": "~14.1.0",
|
||||||
"rxjs": "^7.4.0",
|
"rxjs": "^7.4.0",
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.2.0",
|
||||||
"zone.js": "~0.11.4",
|
"zone.js": "~0.11.4",
|
||||||
|
|
||||||
// Clarity UI. Required
|
// Clarity UI. Required
|
||||||
"@clr/angular": "13.0.2",
|
"@clr/angular": "13.7.0",
|
||||||
"@cds/core": "5.6.4",
|
"@cds/core": "6.0.0",
|
||||||
"@clr/icons": "13.0.2",
|
"@clr/icons": "13.0.2",
|
||||||
"@clr/ui": "13.0.2",
|
"@clr/ui": "13.7.0",
|
||||||
|
|
||||||
// For Harbor i18n functionality. Required
|
// For Harbor i18n functionality. Required
|
||||||
"@ngx-translate/core": "^13.0.0",
|
"@ngx-translate/core": "^13.0.0",
|
||||||
|
@ -81,11 +81,6 @@
|
||||||
// To render markdown data. Required
|
// To render markdown data. Required
|
||||||
"ngx-markdown": "~13.0.0",
|
"ngx-markdown": "~13.0.0",
|
||||||
|
|
||||||
// For swagger API center. Required
|
|
||||||
"swagger-ui": "~4.9.0",
|
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"stream-browserify": "^3.0.0",
|
|
||||||
|
|
||||||
// To convert yaml to json. Required
|
// To convert yaml to json. Required
|
||||||
"js-yaml": "^4.1.0"
|
"js-yaml": "^4.1.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -76,6 +76,16 @@ Start
|
||||||
"secure": false,
|
"secure": false,
|
||||||
"logLevel": "debug"
|
"logLevel": "debug"
|
||||||
},
|
},
|
||||||
|
"/chartrepo/*": {
|
||||||
|
"target": "https://hostname",
|
||||||
|
"secure": false,
|
||||||
|
"logLevel": "debug"
|
||||||
|
},
|
||||||
|
"/LICENSE": {
|
||||||
|
"target": "https://hostname",
|
||||||
|
"secure": false,
|
||||||
|
"logLevel": "debug"
|
||||||
|
},
|
||||||
"/swagger.json": {
|
"/swagger.json": {
|
||||||
"target": "https://hostname",
|
"target": "https://hostname",
|
||||||
"secure": false,
|
"secure": false,
|
||||||
|
@ -86,12 +96,12 @@ Start
|
||||||
"secure": false,
|
"secure": false,
|
||||||
"logLevel": "debug"
|
"logLevel": "debug"
|
||||||
},
|
},
|
||||||
"/chartrepo/*": {
|
"/devcenter-api-2.0": {
|
||||||
"target": "https://hostname",
|
"target": "https://hostname",
|
||||||
"secure": false,
|
"secure": false,
|
||||||
"logLevel": "debug"
|
"logLevel": "debug"
|
||||||
},
|
},
|
||||||
"/LICENSE": {
|
"/swagger-ui.bundle.js": {
|
||||||
"target": "https://hostname",
|
"target": "https://hostname",
|
||||||
"secure": false,
|
"secure": false,
|
||||||
"logLevel": "debug"
|
"logLevel": "debug"
|
||||||
|
|
|
@ -12,20 +12,8 @@
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
"options": {
|
"options": {
|
||||||
"allowedCommonJsDependencies": [
|
"allowedCommonJsDependencies": [
|
||||||
"swagger-ui",
|
"cron-validator",
|
||||||
"buffer",
|
"js-yaml"
|
||||||
"js-yaml",
|
|
||||||
"hoist-non-react-statics",
|
|
||||||
"lodash",
|
|
||||||
"core-js-pure",
|
|
||||||
"prop-types",
|
|
||||||
"randexp",
|
|
||||||
"react-copy-to-clipboard",
|
|
||||||
"react-debounce-input",
|
|
||||||
"react-immutable-proptypes",
|
|
||||||
"redux-immutable",
|
|
||||||
"url-parse",
|
|
||||||
"xml-but-prettier"
|
|
||||||
],
|
],
|
||||||
"outputPath": "dist",
|
"outputPath": "dist",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
|
@ -41,7 +29,6 @@
|
||||||
"styles": [
|
"styles": [
|
||||||
"node_modules/@clr/icons/clr-icons.min.css",
|
"node_modules/@clr/icons/clr-icons.min.css",
|
||||||
"node_modules/@clr/ui/clr-ui.min.css",
|
"node_modules/@clr/ui/clr-ui.min.css",
|
||||||
"node_modules/swagger-ui/dist/swagger-ui.css",
|
|
||||||
"node_modules/prismjs/themes/prism-solarizedlight.css",
|
"node_modules/prismjs/themes/prism-solarizedlight.css",
|
||||||
"src/global.scss",
|
"src/global.scss",
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
Swagger UI
|
||||||
|
============
|
||||||
|
This is the project based on Swagger UI and Webpack.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Start
|
||||||
|
============
|
||||||
|
1. npm install
|
||||||
|
2. change `webpack.dev.js.temp` to `webpack.dev.js`,
|
||||||
|
```shell
|
||||||
|
mv webpack.dev.js.temp webpack.dev.js
|
||||||
|
```
|
||||||
|
3. modify `webpack.dev.js`, replace `https://example.com` with an available Harbor server
|
||||||
|
4. npm run start
|
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Harbor</title>
|
||||||
|
<base href="/" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2" />
|
||||||
|
<style>
|
||||||
|
.spinner {
|
||||||
|
position: absolute;
|
||||||
|
margin: auto;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: inline-block;
|
||||||
|
width: 108px;
|
||||||
|
height: 108px;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
background: url(data:image/svg+xml;charset=utf8,%3Csvg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%0A%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20.cls-1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20.cls-2%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fill%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stroke-miterlimit%3A%2010%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stroke-width%3A%205px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.cls-1%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stroke%3A%20%23000000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stroke-opacity%3A%200.15%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.cls-2%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stroke%3A%20%230072a3%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%3C%2Fstyle%3E%0A%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%3Ctitle%3EPreloader_72x2%3C%2Ftitle%3E%0A%20%20%20%20%3Ccircle%20class%3D%22cls-1%22%20cx%3D%2236%22%20cy%3D%2236%22%20r%3D%2233%22%2F%3E%0A%20%20%20%20%3Cpath%20class%3D%22cls-2%22%20d%3D%22M14.3%2C60.9A33%2C33%2C0%2C0%2C1%2C36%2C3%22%3E%0A%20%20%20%20%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A);
|
||||||
|
text-indent: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotateZ(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui-container" class="spinner">Loading...</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "swagger-ui",
|
||||||
|
"version": "2.7.0",
|
||||||
|
"description": "Swagger UI for Harbor APIs",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.prod.js && mv dist/index.html dist/swagger-ui-index.html",
|
||||||
|
"start": "webpack serve --open --config webpack.dev.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"swagger-ui": "4.13.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"webpack": "^5.74.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-dev-server": "^4.10.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
import SwaggerUI from 'swagger-ui'
|
||||||
|
import 'swagger-ui/dist/swagger-ui.css';
|
||||||
|
|
||||||
|
const helpInfo =
|
||||||
|
' If you want to enable basic authorization,' +
|
||||||
|
' please logout Harbor first or manually delete the cookies under the current domain.';
|
||||||
|
const SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS', 'TRACE'];
|
||||||
|
|
||||||
|
// get swagger.json and swagger2.json from portal container then render swagger ui
|
||||||
|
// before rendering, the ui shows a loading style
|
||||||
|
Promise.all([
|
||||||
|
fetch('/swagger.json').then(value => value.json()),
|
||||||
|
fetch('/swagger2.json').then(value => value.json())
|
||||||
|
])
|
||||||
|
.then(value => {
|
||||||
|
// merger swagger.json and swagger2.json
|
||||||
|
const json = {};
|
||||||
|
mergeDeep(json, value[0], value[1]);
|
||||||
|
json['host'] = window.location.host;
|
||||||
|
const protocal = window.location.protocol;
|
||||||
|
json['schemes'] = [protocal.replace(':', '')];
|
||||||
|
json.info.description = json.info.description + helpInfo;
|
||||||
|
// start to render
|
||||||
|
SwaggerUI({
|
||||||
|
spec: json,
|
||||||
|
dom_id: '#swagger-ui-container',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [SwaggerUI.presets.apis],
|
||||||
|
requestInterceptor: request => {
|
||||||
|
// Get the csrf token from localstorage
|
||||||
|
const token = localStorage.getItem('__csrf');
|
||||||
|
const headers = request.headers || {};
|
||||||
|
if (token) {
|
||||||
|
if (
|
||||||
|
request.method &&
|
||||||
|
SAFE_METHODS.indexOf(
|
||||||
|
request.method.toUpperCase()
|
||||||
|
) === -1
|
||||||
|
) {
|
||||||
|
headers['X-Harbor-CSRF-Token'] = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
},
|
||||||
|
responseInterceptor: response => {
|
||||||
|
const headers = response.headers || {};
|
||||||
|
const responseToken =
|
||||||
|
headers['X-Harbor-CSRF-Token'];
|
||||||
|
if (responseToken) {
|
||||||
|
// Set the csrf token to localstorage
|
||||||
|
localStorage.setItem('__csrf', responseToken);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// remove loading style
|
||||||
|
document.getElementById('swagger-ui-container').removeAttribute('class');
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function mergeDeep(target, ...sources) {
|
||||||
|
if (!sources.length) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
const source = sources.shift();
|
||||||
|
|
||||||
|
if (isObject(target) && isObject(source)) {
|
||||||
|
for (const key in source) {
|
||||||
|
if (isObject(source[key])) {
|
||||||
|
if (!target[key]) {
|
||||||
|
Object.assign(target, { [key]: {} });
|
||||||
|
}
|
||||||
|
mergeDeep(target[key], source[key]);
|
||||||
|
} else {
|
||||||
|
Object.assign(target, { [key]: source[key] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergeDeep(target, ...sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isObject(item) {
|
||||||
|
return item && typeof item === 'object' && !Array.isArray(item);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
const path = require('path');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
|
|
||||||
|
|
||||||
|
const outputPath = path.resolve(__dirname, 'dist');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
devServer: {
|
||||||
|
proxy: {
|
||||||
|
'/swagger.json': {
|
||||||
|
target: 'https://example.com',
|
||||||
|
"secure": false,
|
||||||
|
},
|
||||||
|
'/swagger2.json': {
|
||||||
|
target: 'https://example.com',
|
||||||
|
"secure": false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: 'development',
|
||||||
|
entry: {
|
||||||
|
app: require.resolve('./src/index'),
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
{ loader: 'style-loader' },
|
||||||
|
{ loader: 'css-loader' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
`favicon.ico`
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'index.html'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
filename: 'swagger-ui.bundle.js',
|
||||||
|
path: outputPath,
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
const path = require('path');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
|
const outputPath = path.resolve(__dirname, 'dist');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: 'production',
|
||||||
|
entry: {
|
||||||
|
app: require.resolve('./src/index'),
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
{ loader: 'style-loader' },
|
||||||
|
{ loader: 'css-loader' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'index.html'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
filename: 'swagger-ui.bundle.js',
|
||||||
|
path: outputPath,
|
||||||
|
}
|
||||||
|
};
|
|
@ -19,10 +19,15 @@ RUN python -c 'import sys, yaml, json; y=yaml.load(sys.stdin.read()); print json
|
||||||
|
|
||||||
COPY LICENSE /build_dir/dist
|
COPY LICENSE /build_dir/dist
|
||||||
|
|
||||||
|
RUN cd app-swagger-ui && npm install --unsafe-perm
|
||||||
|
RUN cd app-swagger-ui && npm run build
|
||||||
|
|
||||||
FROM nginx:1.17
|
FROM nginx:1.17
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
COPY --from=builder /build_dir/dist /usr/share/nginx/html
|
COPY --from=builder /build_dir/dist /usr/share/nginx/html
|
||||||
|
COPY --from=builder /build_dir/app-swagger-ui/dist /usr/share/nginx/html
|
||||||
COPY src/portal/docker-build/nginx.conf /etc/nginx/nginx.conf
|
COPY src/portal/docker-build/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
|
@ -26,6 +26,10 @@ http {
|
||||||
gzip_proxied expired no-cache no-store private auth;
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
|
location /devcenter-api-2.0 {
|
||||||
|
try_files $uri $uri/ /swagger-ui-index.html;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "harbor",
|
"name": "harbor",
|
||||||
"version": "2.6.0",
|
"version": "2.7.0",
|
||||||
"description": "Harbor UI with Clarity",
|
"description": "Harbor UI with Clarity",
|
||||||
"angular-cli": {},
|
"angular-cli": {},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -42,15 +42,12 @@
|
||||||
"@clr/ui": "13.7.0",
|
"@clr/ui": "13.7.0",
|
||||||
"@ngx-translate/core": "^13.0.0",
|
"@ngx-translate/core": "^13.0.0",
|
||||||
"@ngx-translate/http-loader": "^6.0.0",
|
"@ngx-translate/http-loader": "^6.0.0",
|
||||||
"buffer": "^6.0.3",
|
"cron-validator": "^1.3.1",
|
||||||
"cron-validator": "^1.2.1",
|
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"ngx-clipboard": "^12.3.1",
|
"ngx-clipboard": "^15.1.0",
|
||||||
"ngx-cookie": "^5.0.2",
|
"ngx-cookie": "^6.0.1",
|
||||||
"ngx-markdown": "14.0.1",
|
"ngx-markdown": "14.0.1",
|
||||||
"rxjs": "^7.4.0",
|
"rxjs": "^7.4.0",
|
||||||
"stream-browserify": "^3.0.0",
|
|
||||||
"swagger-ui": "~4.10.3",
|
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.2.0",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { AfterViewInit, Component, Directive, OnInit } from '@angular/core';
|
|
||||||
import { Title } from '@angular/platform-browser';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { CookieService } from 'ngx-cookie';
|
|
||||||
|
|
||||||
@Directive()
|
|
||||||
export abstract class DevCenterBaseDirective implements OnInit, AfterViewInit {
|
|
||||||
protected constructor(
|
|
||||||
public translate: TranslateService,
|
|
||||||
public cookieService: CookieService,
|
|
||||||
public titleService: Title
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.setTitle('APP_TITLE.HARBOR_SWAGGER');
|
|
||||||
}
|
|
||||||
|
|
||||||
private setTitle(key: string) {
|
|
||||||
this.translate.get(key).subscribe((res: string) => {
|
|
||||||
this.titleService.setTitle(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
abstract getSwaggerUI();
|
|
||||||
abstract ngAfterViewInit();
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
<div class="swagger-container"></div>
|
|
|
@ -1,8 +0,0 @@
|
||||||
.swagger-container {
|
|
||||||
overflow: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import { ComponentFixture, TestBed, getTestBed } from '@angular/core/testing';
|
|
||||||
import { HttpTestingController } from '@angular/common/http/testing';
|
|
||||||
import { DevCenterComponent } from './dev-center.component';
|
|
||||||
import { CookieService } from 'ngx-cookie';
|
|
||||||
import { SharedTestingModule } from '../shared/shared.module';
|
|
||||||
|
|
||||||
describe('DevCenterComponent', () => {
|
|
||||||
let component: DevCenterComponent;
|
|
||||||
let fixture: ComponentFixture<DevCenterComponent>;
|
|
||||||
const mockCookieService = {
|
|
||||||
get: () => {
|
|
||||||
return 'xsrf';
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let cookie = 'fdsa|ds';
|
|
||||||
let injector: TestBed;
|
|
||||||
let httpMock: HttpTestingController;
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [DevCenterComponent],
|
|
||||||
imports: [SharedTestingModule],
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: CookieService,
|
|
||||||
useValue: mockCookieService,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
injector = getTestBed();
|
|
||||||
httpMock = injector.inject(HttpTestingController);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DevCenterComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.autoDetectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
it('get swagger should return data', () => {
|
|
||||||
const req = httpMock.expectOne('/swagger.json');
|
|
||||||
expect(req.request.method).toBe('GET');
|
|
||||||
req.flush({
|
|
||||||
host: '122.33',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,96 +0,0 @@
|
||||||
import { AfterViewInit, Component, ElementRef, OnInit } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { throwError as observableThrowError, forkJoin } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { Title } from '@angular/platform-browser';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { CookieService } from 'ngx-cookie';
|
|
||||||
import SwaggerUI from 'swagger-ui';
|
|
||||||
import { mergeDeep } from '../shared/units/utils';
|
|
||||||
import { DevCenterBaseDirective } from './dev-center-base';
|
|
||||||
import { SAFE_METHODS } from '../services/intercept-http.service';
|
|
||||||
|
|
||||||
enum SwaggerJsonUrls {
|
|
||||||
SWAGGER1 = '/swagger.json',
|
|
||||||
SWAGGER2 = '/swagger2.json',
|
|
||||||
}
|
|
||||||
|
|
||||||
const helpInfo: string =
|
|
||||||
' If you want to enable basic authorization,' +
|
|
||||||
' please logout Harbor first or manually delete the cookies under the current domain.';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'dev-center',
|
|
||||||
templateUrl: 'dev-center.component.html',
|
|
||||||
viewProviders: [Title],
|
|
||||||
styleUrls: ['dev-center.component.scss'],
|
|
||||||
})
|
|
||||||
export class DevCenterComponent
|
|
||||||
extends DevCenterBaseDirective
|
|
||||||
implements AfterViewInit, OnInit
|
|
||||||
{
|
|
||||||
private ui: any;
|
|
||||||
constructor(
|
|
||||||
private el: ElementRef,
|
|
||||||
private http: HttpClient,
|
|
||||||
public translate: TranslateService,
|
|
||||||
public cookieService: CookieService,
|
|
||||||
public titleService: Title
|
|
||||||
) {
|
|
||||||
super(translate, cookieService, titleService);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterViewInit() {
|
|
||||||
this.getSwaggerUI();
|
|
||||||
}
|
|
||||||
getSwaggerUI() {
|
|
||||||
forkJoin([
|
|
||||||
this.http.get(SwaggerJsonUrls.SWAGGER1),
|
|
||||||
this.http.get(SwaggerJsonUrls.SWAGGER2),
|
|
||||||
])
|
|
||||||
.pipe(catchError(error => observableThrowError(error)))
|
|
||||||
.subscribe(jsonArr => {
|
|
||||||
const json: any = {};
|
|
||||||
mergeDeep(json, jsonArr[0], jsonArr[1]);
|
|
||||||
json['host'] = window.location.host;
|
|
||||||
const protocal = window.location.protocol;
|
|
||||||
json['schemes'] = [protocal.replace(':', '')];
|
|
||||||
json.info.description = json.info.description + helpInfo;
|
|
||||||
this.ui = SwaggerUI({
|
|
||||||
spec: json,
|
|
||||||
domNode:
|
|
||||||
this.el.nativeElement.querySelector(
|
|
||||||
'.swagger-container'
|
|
||||||
),
|
|
||||||
deepLinking: true,
|
|
||||||
presets: [SwaggerUI.presets.apis],
|
|
||||||
requestInterceptor: request => {
|
|
||||||
// Get the csrf token from localstorage
|
|
||||||
const token = localStorage.getItem('__csrf');
|
|
||||||
const headers = request.headers || {};
|
|
||||||
if (token) {
|
|
||||||
if (
|
|
||||||
request.method &&
|
|
||||||
SAFE_METHODS.indexOf(
|
|
||||||
request.method.toUpperCase()
|
|
||||||
) === -1
|
|
||||||
) {
|
|
||||||
headers['X-Harbor-CSRF-Token'] = token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return request;
|
|
||||||
},
|
|
||||||
responseInterceptor: response => {
|
|
||||||
const headers = response.headers || {};
|
|
||||||
const responseToken: string =
|
|
||||||
headers['X-Harbor-CSRF-Token'];
|
|
||||||
if (responseToken) {
|
|
||||||
// Set the csrf token to localstorage
|
|
||||||
localStorage.setItem('__csrf', responseToken);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { DevCenterComponent } from './dev-center.component';
|
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: DevCenterComponent,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forChild(routes)],
|
|
||||||
declarations: [DevCenterComponent],
|
|
||||||
})
|
|
||||||
export class DeveloperCenterModule {}
|
|
|
@ -30,13 +30,6 @@ const harborRoutes: Routes = [
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('./account/account.module').then(m => m.AccountModule),
|
import('./account/account.module').then(m => m.AccountModule),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'devcenter-api-2.0',
|
|
||||||
loadChildren: () =>
|
|
||||||
import('./dev-center/dev-center.module').then(
|
|
||||||
m => m.DeveloperCenterModule
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'oidc-onboard',
|
path: 'oidc-onboard',
|
||||||
canActivate: [OidcGuard, SignInGuard],
|
canActivate: [OidcGuard, SignInGuard],
|
||||||
|
|
|
@ -79,6 +79,3 @@ import 'zone.js'; // Included with Angular CLI.
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* APPLICATION IMPORTS
|
* APPLICATION IMPORTS
|
||||||
*/
|
*/
|
||||||
(window as any).global = window; // this is for swagger UI
|
|
||||||
// @ts-ignore
|
|
||||||
window.Buffer = window.Buffer || require('buffer').Buffer; // this is for swagger UI
|
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{
|
{
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"paths": {
|
|
||||||
"stream": [ "./node_modules/stream-browserify" ]
|
|
||||||
},
|
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"outDir": "./dist/out-tsc",
|
"outDir": "./dist/out-tsc",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
|
Loading…
Reference in New Issue