chore(webui): format code with prettier
This commit is contained in:
parent
115ddc6a4a
commit
c5c8382742
32 changed files with 1525 additions and 827 deletions
3
webui/.prettierrc
Normal file
3
webui/.prettierrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
|
@ -14,3 +14,4 @@ COPY . $WEBUI_DIR/
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
RUN yarn lint
|
RUN yarn lint
|
||||||
|
RUN yarn format --check
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"build": "ng build --prod --no-delete-output-path --output-path ../static/",
|
"build": "ng build --prod --no-delete-output-path --output-path ../static/",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
|
"format": "yarn prettier 'src/**/*.{js,ts,html}' '*.md'",
|
||||||
"e2e": "ng e2e"
|
"e2e": "ng e2e"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -34,9 +35,9 @@
|
||||||
"date-fns": "^1.29.0",
|
"date-fns": "^1.29.0",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.5",
|
||||||
"rxjs": "^6.4.0",
|
"rxjs": "^6.4.0",
|
||||||
|
"rxjs-compat": "^6.0.0-rc.0",
|
||||||
"tslib": "^1.9.0",
|
"tslib": "^1.9.0",
|
||||||
"zone.js": "^0.8.19",
|
"zone.js": "^0.8.19"
|
||||||
"rxjs-compat": "^6.0.0-rc.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "~0.13.0",
|
"@angular-devkit/build-angular": "~0.13.0",
|
||||||
|
@ -54,9 +55,11 @@
|
||||||
"karma-coverage-istanbul-reporter": "^2.0.4",
|
"karma-coverage-istanbul-reporter": "^2.0.4",
|
||||||
"karma-jasmine": "~2.0.1",
|
"karma-jasmine": "~2.0.1",
|
||||||
"karma-jasmine-html-reporter": "^1.4.0",
|
"karma-jasmine-html-reporter": "^1.4.0",
|
||||||
|
"prettier": "^1.16.4",
|
||||||
"protractor": "~5.4.2",
|
"protractor": "~5.4.2",
|
||||||
"ts-node": "~8.0.2",
|
"ts-node": "~8.0.2",
|
||||||
"tslint": "~5.12.1",
|
"tslint": "~5.12.1",
|
||||||
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"typescript": "~3.2.4"
|
"typescript": "~3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ Access to Traefik Web UI, ex: http://localhost:8080
|
||||||
## Interface
|
## Interface
|
||||||
|
|
||||||
Traefik Web UI provide 2 types of informations:
|
Traefik Web UI provide 2 types of informations:
|
||||||
|
|
||||||
- Providers with their backends and frontends information.
|
- Providers with their backends and frontends information.
|
||||||
- Health of the web server.
|
- Health of the web server.
|
||||||
|
|
||||||
|
@ -27,9 +28,11 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
|
||||||
- Go to the directory `webui`
|
- Go to the directory `webui`
|
||||||
|
|
||||||
- To install dependencies, execute the following commands:
|
- To install dependencies, execute the following commands:
|
||||||
|
|
||||||
- `yarn install`
|
- `yarn install`
|
||||||
|
|
||||||
- Build static Web UI, execute the following command:
|
- Build static Web UI, execute the following command:
|
||||||
|
|
||||||
- `yarn run build`
|
- `yarn run build`
|
||||||
|
|
||||||
- Static contents are build in the directory `static`
|
- Static contents are build in the directory `static`
|
||||||
|
@ -44,7 +47,6 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
|
||||||
- all images will be optimized at build
|
- all images will be optimized at build
|
||||||
- bundle JavaScript in one file
|
- bundle JavaScript in one file
|
||||||
|
|
||||||
|
|
||||||
## How to edit (only for frontends developer)
|
## How to edit (only for frontends developer)
|
||||||
|
|
||||||
**Don't change manually the files in the directory `static`**
|
**Don't change manually the files in the directory `static`**
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [
|
declarations: [AppComponent],
|
||||||
AppComponent
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
],
|
|
||||||
schemas: [
|
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -7,4 +7,4 @@ import { Component } from '@angular/core';
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class AppComponent { }
|
export class AppComponent {}
|
||||||
|
|
|
@ -38,14 +38,11 @@ import { WindowService } from './services/window.service';
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
RouterModule.forRoot([
|
RouterModule.forRoot([
|
||||||
{path: '', component: ProvidersComponent, pathMatch: 'full'},
|
{ path: '', component: ProvidersComponent, pathMatch: 'full' },
|
||||||
{path: 'status', component: HealthComponent}
|
{ path: 'status', component: HealthComponent }
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [ApiService, WindowService],
|
||||||
ApiService,
|
|
||||||
WindowService
|
|
||||||
],
|
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule {}
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
<div class="loading-text" [class.is-hidden]="!loading">
|
<div class="loading-text" [class.is-hidden]="!loading">
|
||||||
<span>
|
<span>
|
||||||
<span>Loading, please wait...</span>
|
<span>Loading, please wait...</span>
|
||||||
<img src="./assets/images/loader.svg" class="main-loader">
|
<img src="./assets/images/loader.svg" class="main-loader" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { BarChartComponent } from './bar-chart.component';
|
|
||||||
import { WindowService } from '../../services/window.service';
|
import { WindowService } from '../../services/window.service';
|
||||||
|
import { BarChartComponent } from './bar-chart.component';
|
||||||
|
|
||||||
describe('BarChartComponent', () => {
|
describe('BarChartComponent', () => {
|
||||||
let component: BarChartComponent;
|
let component: BarChartComponent;
|
||||||
|
@ -9,10 +9,9 @@ describe('BarChartComponent', () => {
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ BarChartComponent ],
|
declarations: [BarChartComponent],
|
||||||
providers: [{provide: WindowService, useInstance: {}}]
|
providers: [{ provide: WindowService, useInstance: {} }]
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -27,5 +26,4 @@ describe('BarChartComponent', () => {
|
||||||
it('should initially go to loading state', () => {
|
it('should initially go to loading state', () => {
|
||||||
expect(component.loading).toBeTruthy();
|
expect(component.loading).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
SimpleChanges
|
||||||
|
} from '@angular/core';
|
||||||
import { axisBottom, axisLeft, max, scaleBand, scaleLinear, select } from 'd3';
|
import { axisBottom, axisLeft, max, scaleBand, scaleLinear, select } from 'd3';
|
||||||
import { format } from 'd3-format';
|
import { format } from 'd3-format';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
@ -18,19 +25,22 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||||
g: any;
|
g: any;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
margin = {top: 40, right: 40, bottom: 40, left: 40};
|
margin = { top: 40, right: 40, bottom: 40, left: 40 };
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
data: any[];
|
data: any[];
|
||||||
previousData: any[];
|
previousData: any[];
|
||||||
|
|
||||||
constructor(public elementRef: ElementRef, public windowService: WindowService) {
|
constructor(
|
||||||
|
public elementRef: ElementRef,
|
||||||
|
public windowService: WindowService
|
||||||
|
) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.barChartEl = this.elementRef.nativeElement.querySelector('.bar-chart');
|
this.barChartEl = this.elementRef.nativeElement.querySelector('.bar-chart');
|
||||||
this.setup();
|
this.setup();
|
||||||
setTimeout(() => this.loading = false, 1000);
|
setTimeout(() => (this.loading = false), 1000);
|
||||||
|
|
||||||
this.windowService.resize.subscribe(w => this.draw());
|
this.windowService.resize.subscribe(w => this.draw());
|
||||||
}
|
}
|
||||||
|
@ -49,39 +59,44 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(): void {
|
setup(): void {
|
||||||
this.width = this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
this.width =
|
||||||
this.height = this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
||||||
|
this.height =
|
||||||
|
this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
||||||
|
|
||||||
this.svg = select(this.barChartEl)
|
this.svg = select(this.barChartEl)
|
||||||
.append('svg')
|
.append('svg')
|
||||||
.attr('width', this.width + this.margin.left + this.margin.right)
|
.attr('width', this.width + this.margin.left + this.margin.right)
|
||||||
.attr('height', this.height + this.margin.top + this.margin.bottom);
|
.attr('height', this.height + this.margin.top + this.margin.bottom);
|
||||||
|
|
||||||
this.g = this.svg.append('g')
|
this.g = this.svg
|
||||||
|
.append('g')
|
||||||
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
|
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
|
||||||
|
|
||||||
this.x = scaleBand().padding(0.05);
|
this.x = scaleBand().padding(0.05);
|
||||||
this.y = scaleLinear();
|
this.y = scaleLinear();
|
||||||
|
|
||||||
this.g.append('g')
|
this.g.append('g').attr('class', 'axis axis--x');
|
||||||
.attr('class', 'axis axis--x');
|
|
||||||
|
|
||||||
this.g.append('g')
|
this.g.append('g').attr('class', 'axis axis--y');
|
||||||
.attr('class', 'axis axis--y');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(): void {
|
draw(): void {
|
||||||
if (this.barChartEl.clientWidth === 0 || this.barChartEl.clientHeight === 0) {
|
if (
|
||||||
|
this.barChartEl.clientWidth === 0 ||
|
||||||
|
this.barChartEl.clientHeight === 0
|
||||||
|
) {
|
||||||
this.previousData = [];
|
this.previousData = [];
|
||||||
} else {
|
} else {
|
||||||
this.width = this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
this.width =
|
||||||
this.height = this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
||||||
|
this.height =
|
||||||
|
this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.x.domain(this.data.map((d: any) => d.code));
|
this.x.domain(this.data.map((d: any) => d.code));
|
||||||
this.y.domain([0, max(this.data, (d: any) => d.count)]);
|
this.y.domain([0, max(this.data, (d: any) => d.count)]);
|
||||||
|
|
||||||
|
|
||||||
this.svg
|
this.svg
|
||||||
.attr('width', this.width + this.margin.left + this.margin.right)
|
.attr('width', this.width + this.margin.left + this.margin.right)
|
||||||
.attr('height', this.height + this.margin.top + this.margin.bottom);
|
.attr('height', this.height + this.margin.top + this.margin.bottom);
|
||||||
|
@ -89,28 +104,38 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||||
this.x.rangeRound([0, this.width]);
|
this.x.rangeRound([0, this.width]);
|
||||||
this.y.rangeRound([this.height, 0]);
|
this.y.rangeRound([this.height, 0]);
|
||||||
|
|
||||||
this.g.select('.axis--x')
|
this.g
|
||||||
|
.select('.axis--x')
|
||||||
.attr('transform', `translate(0, ${this.height})`)
|
.attr('transform', `translate(0, ${this.height})`)
|
||||||
.call(axisBottom(this.x));
|
.call(axisBottom(this.x));
|
||||||
|
|
||||||
this.g.select('.axis--y')
|
this.g.select('.axis--y').call(
|
||||||
.call(axisLeft(this.y).tickFormat(format('~s')).tickSize(-this.width));
|
axisLeft(this.y)
|
||||||
|
.tickFormat(format('~s'))
|
||||||
|
.tickSize(-this.width)
|
||||||
|
);
|
||||||
|
|
||||||
// Clean previous graph
|
// Clean previous graph
|
||||||
this.g.selectAll('.bar').remove();
|
this.g.selectAll('.bar').remove();
|
||||||
|
|
||||||
const bars = this.g.selectAll('.bar').data(this.data);
|
const bars = this.g.selectAll('.bar').data(this.data);
|
||||||
|
|
||||||
bars.enter()
|
bars
|
||||||
|
.enter()
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'bar')
|
.attr('class', 'bar')
|
||||||
.style('fill', (d: any) => 'hsl(' + Math.floor(((d.code - 100) * 310 / 427) + 50) + ', 50%, 50%)')
|
.style(
|
||||||
|
'fill',
|
||||||
|
(d: any) =>
|
||||||
|
'hsl(' + Math.floor(((d.code - 100) * 310) / 427 + 50) + ', 50%, 50%)'
|
||||||
|
)
|
||||||
.attr('x', (d: any) => this.x(d.code))
|
.attr('x', (d: any) => this.x(d.code))
|
||||||
.attr('y', (d: any) => this.y(d.count))
|
.attr('y', (d: any) => this.y(d.count))
|
||||||
.attr('width', this.x.bandwidth())
|
.attr('width', this.x.bandwidth())
|
||||||
.attr('height', (d: any) => (this.height - this.y(d.count)) < 0 ? 0 : this.height - this.y(d.count));
|
.attr('height', (d: any) =>
|
||||||
|
this.height - this.y(d.count) < 0 ? 0 : this.height - this.y(d.count)
|
||||||
|
);
|
||||||
|
|
||||||
bars.exit().remove();
|
bars.exit().remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
<div class="loading-text line-chart-loading" [class.is-hidden]="!loading">
|
<div class="loading-text line-chart-loading" [class.is-hidden]="!loading">
|
||||||
<span>
|
<span>
|
||||||
<span>Loading, please wait...</span>
|
<span>Loading, please wait...</span>
|
||||||
<img src="./assets/images/loader.svg" class="main-loader">
|
<img src="./assets/images/loader.svg" class="main-loader" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
SimpleChanges
|
||||||
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
axisBottom,
|
axisBottom,
|
||||||
axisLeft,
|
axisLeft,
|
||||||
|
@ -21,7 +28,7 @@ import { WindowService } from '../../services/window.service';
|
||||||
templateUrl: 'line-chart.component.html'
|
templateUrl: 'line-chart.component.html'
|
||||||
})
|
})
|
||||||
export class LineChartComponent implements OnChanges, OnInit {
|
export class LineChartComponent implements OnChanges, OnInit {
|
||||||
@Input() value: { count: number, date: string };
|
@Input() value: { count: number; date: string };
|
||||||
|
|
||||||
firstDisplay: boolean;
|
firstDisplay: boolean;
|
||||||
dirty: boolean;
|
dirty: boolean;
|
||||||
|
@ -42,14 +49,21 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
yAxis: any;
|
yAxis: any;
|
||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
margin = {top: 40, right: 40, bottom: 60, left: 60};
|
margin = { top: 40, right: 40, bottom: 60, left: 60 };
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
constructor(private elementRef: ElementRef, public windowService: WindowService) { }
|
constructor(
|
||||||
|
private elementRef: ElementRef,
|
||||||
|
public windowService: WindowService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.lineChartEl = this.elementRef.nativeElement.querySelector('.line-chart');
|
this.lineChartEl = this.elementRef.nativeElement.querySelector(
|
||||||
this.loadingEl = this.elementRef.nativeElement.querySelector('.line-chart-loading');
|
'.line-chart'
|
||||||
|
);
|
||||||
|
this.loadingEl = this.elementRef.nativeElement.querySelector(
|
||||||
|
'.line-chart-loading'
|
||||||
|
);
|
||||||
this.limit = 40;
|
this.limit = 40;
|
||||||
|
|
||||||
// related to the Observable.timer(0, 3000) in health component
|
// related to the Observable.timer(0, 3000) in health component
|
||||||
|
@ -77,7 +91,10 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
render() {
|
render() {
|
||||||
// When the lineChartEl is not displayed (is-hidden), width and length are equal to 0.
|
// When the lineChartEl is not displayed (is-hidden), width and length are equal to 0.
|
||||||
let elt;
|
let elt;
|
||||||
if (this.lineChartEl.clientWidth === 0 || this.lineChartEl.clientHeight === 0) {
|
if (
|
||||||
|
this.lineChartEl.clientWidth === 0 ||
|
||||||
|
this.lineChartEl.clientHeight === 0
|
||||||
|
) {
|
||||||
elt = this.loadingEl;
|
elt = this.loadingEl;
|
||||||
} else {
|
} else {
|
||||||
elt = this.lineChartEl;
|
elt = this.lineChartEl;
|
||||||
|
@ -85,7 +102,6 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
this.width = elt.clientWidth - this.margin.left - this.margin.right;
|
this.width = elt.clientWidth - this.margin.left - this.margin.right;
|
||||||
this.height = elt.clientHeight - this.margin.top - this.margin.bottom;
|
this.height = elt.clientHeight - this.margin.top - this.margin.bottom;
|
||||||
|
|
||||||
|
|
||||||
const el = this.lineChartEl.querySelector('svg');
|
const el = this.lineChartEl.querySelector('svg');
|
||||||
if (el) {
|
if (el) {
|
||||||
el.parentNode.removeChild(el);
|
el.parentNode.removeChild(el);
|
||||||
|
@ -105,11 +121,16 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
this.x = scaleTime().range([0, this.width - 10]);
|
this.x = scaleTime().range([0, this.width - 10]);
|
||||||
this.y = scaleLinear().range([this.height, 0]);
|
this.y = scaleLinear().range([this.height, 0]);
|
||||||
|
|
||||||
this.x.domain([<any>this.now - (this.limit - 2), <any>this.now - this.duration]);
|
this.x.domain([
|
||||||
|
(this.now as any) - (this.limit - 2),
|
||||||
|
(this.now as any) - this.duration
|
||||||
|
]);
|
||||||
this.y.domain([0, max(this.data, (d: any) => d)]);
|
this.y.domain([0, max(this.data, (d: any) => d)]);
|
||||||
|
|
||||||
this.line = line()
|
this.line = line()
|
||||||
.x((d: any, i: number) => this.x(<any>this.now - (this.limit - 1 - i) * this.duration))
|
.x((d: any, i: number) =>
|
||||||
|
this.x((this.now as any) - (this.limit - 1 - i) * this.duration)
|
||||||
|
)
|
||||||
.y((d: any) => this.y(d))
|
.y((d: any) => this.y(d))
|
||||||
.curve(curveLinear);
|
.curve(curveLinear);
|
||||||
|
|
||||||
|
@ -121,16 +142,24 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
.attr('width', this.width)
|
.attr('width', this.width)
|
||||||
.attr('height', this.height);
|
.attr('height', this.height);
|
||||||
|
|
||||||
this.xAxis = this.svg.append('g')
|
this.xAxis = this.svg
|
||||||
|
.append('g')
|
||||||
.attr('class', 'x axis')
|
.attr('class', 'x axis')
|
||||||
.attr('transform', `translate(0, ${this.height})`)
|
.attr('transform', `translate(0, ${this.height})`)
|
||||||
.call(axisBottom(this.x).tickSize(-this.height).ticks(timeSecond, 5).tickFormat(timeFormat('%H:%M:%S')));
|
.call(
|
||||||
|
axisBottom(this.x)
|
||||||
|
.tickSize(-this.height)
|
||||||
|
.ticks(timeSecond, 5)
|
||||||
|
.tickFormat(timeFormat('%H:%M:%S'))
|
||||||
|
);
|
||||||
|
|
||||||
this.yAxis = this.svg.append('g')
|
this.yAxis = this.svg
|
||||||
|
.append('g')
|
||||||
.attr('class', 'y axis')
|
.attr('class', 'y axis')
|
||||||
.call(axisLeft(this.y).tickSize(-this.width));
|
.call(axisLeft(this.y).tickSize(-this.width));
|
||||||
|
|
||||||
this.path = this.svg.append('g')
|
this.path = this.svg
|
||||||
|
.append('g')
|
||||||
.attr('clip-path', 'url(#clip)')
|
.attr('clip-path', 'url(#clip)')
|
||||||
.append('path')
|
.append('path')
|
||||||
.data([this.data])
|
.data([this.data])
|
||||||
|
@ -149,8 +178,12 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
this.data.push(value * 1000000);
|
this.data.push(value * 1000000);
|
||||||
this.now = new Date();
|
this.now = new Date();
|
||||||
|
|
||||||
this.x.domain([<any>this.now - (this.limit - 2) * this.duration, <any>this.now - this.duration]);
|
this.x.domain([
|
||||||
const minv = min(this.data, (d: any) => d) > 0 ? min(this.data, (d: any) => d) - 4 : 0;
|
(this.now as any) - (this.limit - 2) * this.duration,
|
||||||
|
(this.now as any) - this.duration
|
||||||
|
]);
|
||||||
|
const minv =
|
||||||
|
min(this.data, (d: any) => d) > 0 ? min(this.data, (d: any) => d) - 4 : 0;
|
||||||
const maxv = max(this.data, (d: any) => d) + 4;
|
const maxv = max(this.data, (d: any) => d) + 4;
|
||||||
this.y.domain([minv, maxv]);
|
this.y.domain([minv, maxv]);
|
||||||
|
|
||||||
|
@ -158,7 +191,12 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
.transition()
|
.transition()
|
||||||
.duration(this.firstDisplay || this.dirty ? 0 : this.duration)
|
.duration(this.firstDisplay || this.dirty ? 0 : this.duration)
|
||||||
.ease(easeLinear)
|
.ease(easeLinear)
|
||||||
.call(axisBottom(this.x).tickSize(-this.height).ticks(timeSecond, 5).tickFormat(timeFormat('%H:%M:%S')));
|
.call(
|
||||||
|
axisBottom(this.x)
|
||||||
|
.tickSize(-this.height)
|
||||||
|
.ticks(timeSecond, 5)
|
||||||
|
.tickFormat(timeFormat('%H:%M:%S'))
|
||||||
|
);
|
||||||
|
|
||||||
this.xAxis
|
this.xAxis
|
||||||
.transition()
|
.transition()
|
||||||
|
@ -183,7 +221,12 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||||
.transition()
|
.transition()
|
||||||
.duration(this.duration)
|
.duration(this.duration)
|
||||||
.ease(easeLinear)
|
.ease(easeLinear)
|
||||||
.attr('transform', `translate(${this.x(<any>this.now - (this.limit - 1) * this.duration)})`);
|
.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${this.x(
|
||||||
|
(this.now as any) - (this.limit - 1) * this.duration
|
||||||
|
)})`
|
||||||
|
);
|
||||||
|
|
||||||
this.firstDisplay = false;
|
this.firstDisplay = false;
|
||||||
this.dirty = false;
|
this.dirty = false;
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
<nav class="navbar is-fixed-top is-transparent" role="navigation" aria-label="main navigation">
|
<nav
|
||||||
|
class="navbar is-fixed-top is-transparent"
|
||||||
|
role="navigation"
|
||||||
|
aria-label="main navigation"
|
||||||
|
>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item" routerLink="/" (click)="burger = false">
|
<a class="navbar-item" routerLink="/" (click)="burger = false">
|
||||||
<img src="./assets/images/traefik.logo.svg" alt="Traefik" class="navbar-logo">
|
<img
|
||||||
|
src="./assets/images/traefik.logo.svg"
|
||||||
|
alt="Traefik"
|
||||||
|
class="navbar-logo"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
<div class="navbar-burger burger" data-target="navbarMain" (click)="burger = !burger" [class.is-active]="burger">
|
<div
|
||||||
|
class="navbar-burger burger"
|
||||||
|
data-target="navbarMain"
|
||||||
|
(click)="burger = !burger"
|
||||||
|
[class.is-active]="burger"
|
||||||
|
>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
|
@ -14,10 +26,21 @@
|
||||||
|
|
||||||
<div id="navbarMain" class="navbar-menu" [class.is-active]="burger">
|
<div id="navbarMain" class="navbar-menu" [class.is-active]="burger">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<a class="navbar-item" routerLink="/" routerLinkActive="is-active" [routerLinkActiveOptions]="{ exact: true }" (click)="burger = false">
|
<a
|
||||||
|
class="navbar-item"
|
||||||
|
routerLink="/"
|
||||||
|
routerLinkActive="is-active"
|
||||||
|
[routerLinkActiveOptions]="{ exact: true }"
|
||||||
|
(click)="burger = false"
|
||||||
|
>
|
||||||
Providers
|
Providers
|
||||||
</a>
|
</a>
|
||||||
<a class="navbar-item" routerLink="/status" routerLinkActive="is-active" (click)="burger = false">
|
<a
|
||||||
|
class="navbar-item"
|
||||||
|
routerLink="/status"
|
||||||
|
routerLinkActive="is-active"
|
||||||
|
(click)="burger = false"
|
||||||
|
>
|
||||||
Health
|
Health
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,6 +53,5 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -11,14 +11,14 @@ export class HeaderComponent implements OnInit {
|
||||||
releaseLink: string;
|
releaseLink: string;
|
||||||
burger: boolean;
|
burger: boolean;
|
||||||
|
|
||||||
constructor(private apiService: ApiService) { }
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.apiService.fetchVersion()
|
this.apiService.fetchVersion().subscribe(data => {
|
||||||
.subscribe(data => {
|
|
||||||
this.version = data.Version;
|
this.version = data.Version;
|
||||||
this.codename = data.Codename;
|
this.codename = data.Codename;
|
||||||
this.releaseLink = 'https://github.com/containous/traefik/tree/' + data.Version;
|
this.releaseLink =
|
||||||
|
'https://github.com/containous/traefik/tree/' + data.Version;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="columns is-multiline">
|
<div class="columns is-multiline">
|
||||||
|
|
||||||
<div class="column is-12">
|
<div class="column is-12">
|
||||||
<div class="content-item">
|
<div class="content-item">
|
||||||
<div class="content-item-data">
|
<div class="content-item-data">
|
||||||
|
@ -9,7 +8,9 @@
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<div class="item-data border-right">
|
<div class="item-data border-right">
|
||||||
<span class="data-grey">Total Response Time</span>
|
<span class="data-grey">Total Response Time</span>
|
||||||
<span class="data-blue" [title]="exactTotalResponseTime">{{ totalResponseTime }}</span>
|
<span class="data-blue" [title]="exactTotalResponseTime">{{
|
||||||
|
totalResponseTime
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
|
@ -20,7 +21,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<div class="item-data">
|
<div class="item-data">
|
||||||
<span class="data-grey">Uptime Since <br/>{{ uptimeSince }}</span>
|
<span class="data-grey"
|
||||||
|
>Uptime Since <br />{{ uptimeSince }}</span
|
||||||
|
>
|
||||||
<span class="data-blue">{{ uptime }}</span>
|
<span class="data-blue">{{ uptime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +36,9 @@
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<div class="item-data border-right">
|
<div class="item-data border-right">
|
||||||
<span class="data-grey">Average Response Time</span>
|
<span class="data-grey">Average Response Time</span>
|
||||||
<span class="data-blue" [title]="exactAverageResponseTime">{{ averageResponseTime }}</span>
|
<span class="data-blue" [title]="exactAverageResponseTime">{{
|
||||||
|
averageResponseTime
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
|
@ -69,7 +74,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -82,15 +86,23 @@
|
||||||
<td>Request</td>
|
<td>Request</td>
|
||||||
<td>Time</td>
|
<td>Time</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let entry of recentErrors; trackBy: trackRecentErrors;">
|
<tr *ngFor="let entry of recentErrors; trackBy: trackRecentErrors">
|
||||||
<td>
|
<td>
|
||||||
<span class="tag is-info" [title]="entry.status">{{ entry.status_code }}</span> <span class="is-hidden-mobile is-hidden-desktop-only">{{ entry.status }}</span>
|
<span class="tag is-info" [title]="entry.status">{{
|
||||||
|
entry.status_code
|
||||||
|
}}</span
|
||||||
|
> <span class="is-hidden-mobile is-hidden-desktop-only">{{
|
||||||
|
entry.status
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="tag">{{ entry.method }}</span> <span>{{ entry.host }}{{ entry.path }}</span>
|
<span class="tag">{{ entry.method }}</span
|
||||||
|
> <span>{{ entry.host }}{{ entry.path }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span [title]="entry.time | date:'yyyy-MM-dd HH:mm:ss:SSS a z'">{{ entry.time | date:'yyyy-MM-dd HH:mm:ss a z' }}</span>
|
<span [title]="entry.time | date: 'yyyy-MM-dd HH:mm:ss:SSS a z'">{{
|
||||||
|
entry.time | date: 'yyyy-MM-dd HH:mm:ss a z'
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="!recentErrors?.length">
|
<tr *ngIf="!recentErrors?.length">
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class HealthComponent implements OnInit, OnDestroy {
|
||||||
chartValue: any;
|
chartValue: any;
|
||||||
statusCodeValue: any;
|
statusCodeValue: any;
|
||||||
|
|
||||||
constructor(private apiService: ApiService) { }
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.sub = Observable.timer(0, 3000)
|
this.sub = Observable.timer(0, 3000)
|
||||||
|
@ -42,16 +42,30 @@ export class HealthComponent implements OnInit, OnDestroy {
|
||||||
this.recentErrors = data.recent_errors;
|
this.recentErrors = data.recent_errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chartValue = {count: data.average_response_time_sec, date: data.time};
|
this.chartValue = {
|
||||||
this.statusCodeValue = Object.keys(data.total_status_code_count)
|
count: data.average_response_time_sec,
|
||||||
.map(key => ({code: key, count: data.total_status_code_count[key]}));
|
date: data.time
|
||||||
|
};
|
||||||
|
this.statusCodeValue = Object.keys(data.total_status_code_count).map(
|
||||||
|
key => ({ code: key, count: data.total_status_code_count[key] })
|
||||||
|
);
|
||||||
|
|
||||||
this.pid = data.pid;
|
this.pid = data.pid;
|
||||||
this.uptime = distanceInWordsStrict(subSeconds(new Date(), data.uptime_sec), new Date());
|
this.uptime = distanceInWordsStrict(
|
||||||
this.uptimeSince = format(subSeconds(new Date(), data.uptime_sec), 'YYYY-MM-DD HH:mm:ss Z');
|
subSeconds(new Date(), data.uptime_sec),
|
||||||
this.totalResponseTime = distanceInWordsStrict(subSeconds(new Date(), data.total_response_time_sec), new Date());
|
new Date()
|
||||||
|
);
|
||||||
|
this.uptimeSince = format(
|
||||||
|
subSeconds(new Date(), data.uptime_sec),
|
||||||
|
'YYYY-MM-DD HH:mm:ss Z'
|
||||||
|
);
|
||||||
|
this.totalResponseTime = distanceInWordsStrict(
|
||||||
|
subSeconds(new Date(), data.total_response_time_sec),
|
||||||
|
new Date()
|
||||||
|
);
|
||||||
this.exactTotalResponseTime = data.total_response_time;
|
this.exactTotalResponseTime = data.total_response_time;
|
||||||
this.averageResponseTime = Math.floor(data.average_response_time_sec * 1000) + ' ms';
|
this.averageResponseTime =
|
||||||
|
Math.floor(data.average_response_time_sec * 1000) + ' ms';
|
||||||
this.exactAverageResponseTime = data.average_response_time;
|
this.exactAverageResponseTime = data.average_response_time;
|
||||||
this.codeCount = data.count;
|
this.codeCount = data.count;
|
||||||
this.totalCodeCount = data.total_count;
|
this.totalCodeCount = data.total_count;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,7 +18,7 @@ export class ProvidersComponent implements OnInit, OnDestroy {
|
||||||
tab: string;
|
tab: string;
|
||||||
keyword: string;
|
keyword: string;
|
||||||
|
|
||||||
constructor(private apiService: ApiService) { }
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.maxItem = 100;
|
this.maxItem = 100;
|
||||||
|
|
|
@ -8,9 +8,12 @@ interface LetContext<T> {
|
||||||
selector: '[appLet]'
|
selector: '[appLet]'
|
||||||
})
|
})
|
||||||
export class LetDirective<T> {
|
export class LetDirective<T> {
|
||||||
private _context: LetContext<T> = {appLet: null};
|
private _context: LetContext<T> = { appLet: null };
|
||||||
|
|
||||||
constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef<LetContext<T>>) {
|
constructor(
|
||||||
|
_viewContainer: ViewContainerRef,
|
||||||
|
_templateRef: TemplateRef<LetContext<T>>
|
||||||
|
) {
|
||||||
_viewContainer.createEmbeddedView(_templateRef, this._context);
|
_viewContainer.createEmbeddedView(_templateRef, this._context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,10 @@ export class BackendFilterPipe implements PipeTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyword = filter.toLowerCase();
|
const keyword = filter.toLowerCase();
|
||||||
return items.filter(d => d.id.toLowerCase().includes(keyword)
|
return items.filter(
|
||||||
|| d.servers.some(r => r.url.toLowerCase().includes(keyword)));
|
d =>
|
||||||
|
d.id.toLowerCase().includes(keyword) ||
|
||||||
|
d.servers.some(r => r.url.toLowerCase().includes(keyword))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,11 @@ export class FrontendFilterPipe implements PipeTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyword = filter.toLowerCase();
|
const keyword = filter.toLowerCase();
|
||||||
return items.filter(d => d.id.toLowerCase().includes(keyword)
|
return items.filter(
|
||||||
|| d.backend.toLowerCase().includes(keyword)
|
d =>
|
||||||
|| d.routes.some(r => r.rule.toLowerCase().includes(keyword)));
|
d.id.toLowerCase().includes(keyword) ||
|
||||||
|
d.backend.toLowerCase().includes(keyword) ||
|
||||||
|
d.routes.some(r => r.rule.toLowerCase().includes(keyword))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,38 +3,39 @@ import { HumanReadableFilterPipe } from './humanreadable.filter.pipe';
|
||||||
describe('HumanReadableFilterPipe', () => {
|
describe('HumanReadableFilterPipe', () => {
|
||||||
const pipe = new HumanReadableFilterPipe();
|
const pipe = new HumanReadableFilterPipe();
|
||||||
|
|
||||||
const datatable = [{
|
const datatable = [
|
||||||
'given': '180000000000',
|
{
|
||||||
'expected': '180s'
|
given: '180000000000',
|
||||||
|
expected: '180s'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'given': '4096.0',
|
given: '4096.0',
|
||||||
'expected': '4096ns'
|
expected: '4096ns'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'given': '7200000000000',
|
given: '7200000000000',
|
||||||
'expected': '120m'
|
expected: '120m'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'given': '1337',
|
given: '1337',
|
||||||
'expected': '1337ns'
|
expected: '1337ns'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'given': 'traefik',
|
given: 'traefik',
|
||||||
'expected': 'traefik',
|
expected: 'traefik'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'given': '-23',
|
given: '-23',
|
||||||
'expected': '-23',
|
expected: '-23'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'given': '0',
|
given: '0',
|
||||||
'expected': '0',
|
expected: '0'
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
datatable.forEach(item => {
|
datatable.forEach(item => {
|
||||||
it((item.given + ' should be transformed to ' + item.expected ), () => {
|
it(item.given + ' should be transformed to ' + item.expected, () => {
|
||||||
expect(pipe.transform(item.given)).toEqual(item.expected);
|
expect(pipe.transform(item.given)).toEqual(item.expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -42,5 +43,4 @@ describe('HumanReadableFilterPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
expect(pipe).toBeTruthy();
|
expect(pipe).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||||
* HumanReadableFilterPipe converts a time period in nanoseconds to a human-readable
|
* HumanReadableFilterPipe converts a time period in nanoseconds to a human-readable
|
||||||
* string.
|
* string.
|
||||||
*/
|
*/
|
||||||
@Pipe({name: 'humanreadable'})
|
@Pipe({ name: 'humanreadable' })
|
||||||
export class HumanReadableFilterPipe implements PipeTransform {
|
export class HumanReadableFilterPipe implements PipeTransform {
|
||||||
transform(value): any {
|
transform(value): any {
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
@Pipe({name: 'keys'})
|
@Pipe({ name: 'keys' })
|
||||||
export class KeysPipe implements PipeTransform {
|
export class KeysPipe implements PipeTransform {
|
||||||
transform(value, args: string[]): any {
|
transform(value, args: string[]): any {
|
||||||
return Object.keys(value);
|
return Object.keys(value);
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
|
import {
|
||||||
|
HttpClient,
|
||||||
|
HttpErrorResponse,
|
||||||
|
HttpHeaders
|
||||||
|
} from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import 'rxjs/add/observable/of';
|
import 'rxjs/add/observable/of';
|
||||||
import 'rxjs/add/operator/catch';
|
import 'rxjs/add/operator/catch';
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
import 'rxjs/add/operator/retry';
|
import 'rxjs/add/operator/retry';
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { EMPTY } from 'rxjs/internal/observable/empty';
|
import { EMPTY } from 'rxjs/internal/observable/empty';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
export interface ProviderType {
|
export interface ProviderType {
|
||||||
[provider: string]: {
|
[provider: string]: {
|
||||||
|
@ -25,28 +29,37 @@ export class ApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchVersion(): Observable<any> {
|
fetchVersion(): Observable<any> {
|
||||||
return this.http.get('../api/version', {headers: this.headers})
|
return this.http
|
||||||
|
.get('../api/version', { headers: this.headers })
|
||||||
.retry(4)
|
.retry(4)
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
console.error(`[version] returned code ${err.status}, body was: ${err.error}`);
|
console.error(
|
||||||
|
`[version] returned code ${err.status}, body was: ${err.error}`
|
||||||
|
);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchHealthStatus(): Observable<any> {
|
fetchHealthStatus(): Observable<any> {
|
||||||
return this.http.get('../health', {headers: this.headers})
|
return this.http
|
||||||
|
.get('../health', { headers: this.headers })
|
||||||
.retry(2)
|
.retry(2)
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
console.error(`[health] returned code ${err.status}, body was: ${err.error}`);
|
console.error(
|
||||||
|
`[health] returned code ${err.status}, body was: ${err.error}`
|
||||||
|
);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchProviders(): Observable<any> {
|
fetchProviders(): Observable<any> {
|
||||||
return this.http.get('../api/providers', {headers: this.headers})
|
return this.http
|
||||||
|
.get('../api/providers', { headers: this.headers })
|
||||||
.retry(2)
|
.retry(2)
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
console.error(`[providers] returned code ${err.status}, body was: ${err.error}`);
|
console.error(
|
||||||
|
`[providers] returned code ${err.status}, body was: ${err.error}`
|
||||||
|
);
|
||||||
return Observable.of<any>({});
|
return Observable.of<any>({});
|
||||||
})
|
})
|
||||||
.map((data: any): ProviderType => this.parseProviders(data));
|
.map((data: any): ProviderType => this.parseProviders(data));
|
||||||
|
@ -58,34 +71,47 @@ export class ApiService {
|
||||||
.reduce((acc, curr) => {
|
.reduce((acc, curr) => {
|
||||||
acc[curr] = {};
|
acc[curr] = {};
|
||||||
|
|
||||||
acc[curr].frontends = this.toArray(data[curr].frontends, 'id')
|
acc[curr].frontends = this.toArray(data[curr].frontends, 'id').map(
|
||||||
.map(frontend => {
|
frontend => {
|
||||||
frontend.routes = this.toArray(frontend.routes, 'id');
|
frontend.routes = this.toArray(frontend.routes, 'id');
|
||||||
frontend.errors = this.toArray(frontend.errors, 'id');
|
frontend.errors = this.toArray(frontend.errors, 'id');
|
||||||
if (frontend.headers) {
|
if (frontend.headers) {
|
||||||
frontend.headers.customRequestHeaders = this.toHeaderArray(frontend.headers.customRequestHeaders);
|
frontend.headers.customRequestHeaders = this.toHeaderArray(
|
||||||
frontend.headers.customResponseHeaders = this.toHeaderArray(frontend.headers.customResponseHeaders);
|
frontend.headers.customRequestHeaders
|
||||||
frontend.headers.sslProxyHeaders = this.toHeaderArray(frontend.headers.sslProxyHeaders);
|
);
|
||||||
|
frontend.headers.customResponseHeaders = this.toHeaderArray(
|
||||||
|
frontend.headers.customResponseHeaders
|
||||||
|
);
|
||||||
|
frontend.headers.sslProxyHeaders = this.toHeaderArray(
|
||||||
|
frontend.headers.sslProxyHeaders
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (frontend.ratelimit && frontend.ratelimit.rateset) {
|
if (frontend.ratelimit && frontend.ratelimit.rateset) {
|
||||||
frontend.ratelimit.rateset = this.toArray(frontend.ratelimit.rateset, 'id');
|
frontend.ratelimit.rateset = this.toArray(
|
||||||
|
frontend.ratelimit.rateset,
|
||||||
|
'id'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return frontend;
|
return frontend;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
acc[curr].backends = this.toArray(data[curr].backends, 'id')
|
acc[curr].backends = this.toArray(data[curr].backends, 'id').map(
|
||||||
.map(backend => {
|
backend => {
|
||||||
backend.servers = this.toArray(backend.servers, 'id');
|
backend.servers = this.toArray(backend.servers, 'id');
|
||||||
return backend;
|
return backend;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
toHeaderArray(data: any): any[] {
|
toHeaderArray(data: any): any[] {
|
||||||
return Object.keys(data || {}).map(key => ({name: key, value: data[key]}));
|
return Object.keys(data || {}).map(key => ({
|
||||||
|
name: key,
|
||||||
|
value: data[key]
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
toArray(data: any, fieldKeyName: string): any[] {
|
toArray(data: any, fieldKeyName: string): any[] {
|
||||||
|
@ -94,5 +120,4 @@ export class ApiService {
|
||||||
return data[key];
|
return data[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,12 @@ export class WindowService {
|
||||||
|
|
||||||
constructor(private eventManager: EventManager) {
|
constructor(private eventManager: EventManager) {
|
||||||
this.resize = new Subject();
|
this.resize = new Subject();
|
||||||
this.eventManager.addGlobalEventListener('window', 'resize', this.onResize);
|
this.eventManager.addGlobalEventListener(
|
||||||
}
|
'window',
|
||||||
|
'resize',
|
||||||
onResize = (event: UIEvent) => {
|
(event: UIEvent) => {
|
||||||
this.resize.next(event.target);
|
this.resize.next(event.target);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html class="has-navbar-fixed-top">
|
<html class="has-navbar-fixed-top">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<title>Traefik</title>
|
<title>Traefik</title>
|
||||||
<base href="./">
|
<base href="./" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="./assets/images/traefik.icon.png">
|
<link
|
||||||
</head>
|
rel="icon"
|
||||||
<body>
|
type="image/x-icon"
|
||||||
|
href="./assets/images/traefik.icon.png"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -8,5 +8,6 @@ if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic()
|
||||||
|
.bootstrapModule(AppModule)
|
||||||
.catch(err => console.log(err));
|
.catch(err => console.log(err));
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required to support Web Animations `@angular/platform-browser/animations`.
|
* Required to support Web Animations `@angular/platform-browser/animations`.
|
||||||
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
|
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
|
||||||
|
@ -12,11 +9,11 @@
|
||||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||||
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||||
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||||
*/
|
*/
|
||||||
|
@ -27,8 +24,6 @@
|
||||||
*/
|
*/
|
||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* APPLICATION IMPORTS
|
* APPLICATION IMPORTS
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||||
|
|
||||||
import 'zone.js/dist/zone-testing';
|
|
||||||
import { getTestBed } from '@angular/core/testing';
|
import { getTestBed } from '@angular/core/testing';
|
||||||
import {
|
import {
|
||||||
BrowserDynamicTestingModule,
|
BrowserDynamicTestingModule,
|
||||||
platformBrowserDynamicTesting
|
platformBrowserDynamicTesting
|
||||||
} from '@angular/platform-browser-dynamic/testing';
|
} from '@angular/platform-browser-dynamic/testing';
|
||||||
|
import 'zone.js/dist/zone-testing';
|
||||||
|
|
||||||
declare const require: any;
|
declare const require: any;
|
||||||
|
|
||||||
|
|
|
@ -138,5 +138,8 @@
|
||||||
"use-pipe-transform-interface": true,
|
"use-pipe-transform-interface": true,
|
||||||
"component-class-suffix": true,
|
"component-class-suffix": true,
|
||||||
"directive-class-suffix": true
|
"directive-class-suffix": true
|
||||||
}
|
},
|
||||||
|
"extends": [
|
||||||
|
"tslint-config-prettier"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -5706,6 +5706,11 @@ preserve@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||||
|
|
||||||
|
prettier@^1.16.4:
|
||||||
|
version "1.16.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717"
|
||||||
|
integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==
|
||||||
|
|
||||||
process-nextick-args@~1.0.6:
|
process-nextick-args@~1.0.6:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
|
||||||
|
@ -7342,6 +7347,11 @@ tslib@^1.9.0:
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||||
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
|
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
|
||||||
|
|
||||||
|
tslint-config-prettier@^1.18.0:
|
||||||
|
version "1.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37"
|
||||||
|
integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==
|
||||||
|
|
||||||
tslint@~5.12.1:
|
tslint@~5.12.1:
|
||||||
version "5.12.1"
|
version "5.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1"
|
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1"
|
||||||
|
|
Loading…
Reference in a new issue