diff --git a/webui/src/app/app.module.ts b/webui/src/app/app.module.ts index c9ba2741a..c9f573855 100644 --- a/webui/src/app/app.module.ts +++ b/webui/src/app/app.module.ts @@ -13,6 +13,7 @@ import { ProvidersComponent } from './components/providers/providers.component'; import { LetDirective } from './directives/let.directive'; import { BackendFilterPipe } from './pipes/backend.filter.pipe'; import { FrontendFilterPipe } from './pipes/frontend.filter.pipe'; +import { HumanReadableFilterPipe } from './pipes/humanreadable.filter.pipe'; import { KeysPipe } from './pipes/keys.pipe'; import { ApiService } from './services/api.service'; import { WindowService } from './services/window.service'; @@ -28,6 +29,7 @@ import { WindowService } from './services/window.service'; KeysPipe, FrontendFilterPipe, BackendFilterPipe, + HumanReadableFilterPipe, LetDirective ], imports: [ diff --git a/webui/src/app/components/providers/providers.component.html b/webui/src/app/components/providers/providers.component.html index bcf9df5e5..72fa96973 100644 --- a/webui/src/app/components/providers/providers.component.html +++ b/webui/src/app/components/providers/providers.component.html @@ -435,6 +435,41 @@ + +
+
+
+
+
+

Rate Limiting

+
+
+
+
+ Extractor Function + {{ p.ratelimit.extractorFunc }} +
+
+ + + + + + + + + + + + + + + +
RatesetPeriodAverageBurst
{{rateset.id}}{{rateset.period | humanreadable}}{{rateset.average}}{{rateset.burst}}
+
+
+
+
diff --git a/webui/src/app/pipes/humanreadable.filter.pipe.spec.ts b/webui/src/app/pipes/humanreadable.filter.pipe.spec.ts new file mode 100644 index 000000000..4cdaa6f4a --- /dev/null +++ b/webui/src/app/pipes/humanreadable.filter.pipe.spec.ts @@ -0,0 +1,46 @@ +import { HumanReadableFilterPipe } from './humanreadable.filter.pipe'; + +describe('HumanReadableFilterPipe', () => { + const pipe = new HumanReadableFilterPipe(); + + const datatable = [{ + 'given': '180000000000', + 'expected': '180s' + }, + { + 'given': '4096.0', + 'expected': '4096ns' + }, + { + 'given': '7200000000000', + 'expected': '120m' + }, + { + 'given': '1337', + 'expected': '1337ns' + }, + { + 'given': 'traefik', + 'expected': 'traefik', + }, + { + 'given': '-23', + 'expected': '-23', + }, + { + 'given': '0', + 'expected': '0', + }, + ]; + + datatable.forEach(item => { + it((item.given + ' should be transformed to ' + item.expected ), () => { + expect(pipe.transform(item.given)).toEqual(item.expected); + }); + }); + + it('create an instance', () => { + expect(pipe).toBeTruthy(); + }); + +}); diff --git a/webui/src/app/pipes/humanreadable.filter.pipe.ts b/webui/src/app/pipes/humanreadable.filter.pipe.ts new file mode 100644 index 000000000..d0f5e8736 --- /dev/null +++ b/webui/src/app/pipes/humanreadable.filter.pipe.ts @@ -0,0 +1,27 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +/** + * HumanReadableFilterPipe converts a time period in nanoseconds to a human-readable + * string. + */ +@Pipe({name: 'humanreadable'}) +export class HumanReadableFilterPipe implements PipeTransform { + transform(value): any { + let result = ''; + const powerOf10 = Math.floor(Math.log10(value)); + + if (powerOf10 > 11) { + result = value / (60 * Math.pow(10, 9)) + 'm'; + } else if (powerOf10 > 9) { + result = value / Math.pow(10, 9) + 's'; + } else if (powerOf10 > 6) { + result = value / Math.pow(10, 6) + 'ms'; + } else if (value > 0) { + result = Math.floor(value) + 'ns'; + } else { + result = value; + } + + return result; + } +} diff --git a/webui/src/app/services/api.service.ts b/webui/src/app/services/api.service.ts index ae87b4ff8..d4b02289e 100644 --- a/webui/src/app/services/api.service.ts +++ b/webui/src/app/services/api.service.ts @@ -67,6 +67,9 @@ export class ApiService { frontend.headers.customResponseHeaders = this.toHeaderArray(frontend.headers.customResponseHeaders); frontend.headers.sslProxyHeaders = this.toHeaderArray(frontend.headers.sslProxyHeaders); } + if (frontend.ratelimit && frontend.ratelimit.rateset) { + frontend.ratelimit.rateset = this.toArray(frontend.ratelimit.rateset, 'id'); + } return frontend; });