Merge branch 'v1.6' into master

This commit is contained in:
Fernandez Ludovic 2018-05-04 17:57:06 +02:00
commit 8e64bc8785
121 changed files with 7788 additions and 5764 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@
/traefik
/traefik.toml
/static/
/webui/.tmp/
.vscode/
/site/
*.log

View file

@ -1,5 +1,185 @@
# Change Log
## [v1.6.0](https://github.com/containous/traefik/tree/v1.6.0) (2018-04-30)
[Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0)
[Commits pre RC](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0-rc1)
**Enhancements:**
- **[acme]** Create ACME Provider ([#2889](https://github.com/containous/traefik/pull/2889) by [nmengin](https://github.com/nmengin))
- **[acme]** Update Lego (Gandi API v5, cloudxns, ...) ([#2844](https://github.com/containous/traefik/pull/2844) by [ldez](https://github.com/ldez))
- **[acme]** Simplify storing renewed acme certificate ([#2614](https://github.com/containous/traefik/pull/2614) by [ferhatelmas](https://github.com/ferhatelmas))
- **[acme]** ACME V2 Integration ([#3063](https://github.com/containous/traefik/pull/3063) by [nmengin](https://github.com/nmengin))
- **[acme]** Bump Lego Version for GoDaddy DNS Provider ([#2482](https://github.com/containous/traefik/pull/2482) by [sjawhar](https://github.com/sjawhar))
- **[acme]** Delete TLS-SNI-01 challenge from ACME ([#2971](https://github.com/containous/traefik/pull/2971) by [nmengin](https://github.com/nmengin))
- **[acme]** Create backup file during migration from ACME V1 to ACME V2 ([#3191](https://github.com/containous/traefik/pull/3191) by [nmengin](https://github.com/nmengin))
- **[acme]** Generate wildcard certificate with SANs in ACME ([#3167](https://github.com/containous/traefik/pull/3167) by [nmengin](https://github.com/nmengin))
- **[api,cluster]** Added cluster/leader endpoint ([#3009](https://github.com/containous/traefik/pull/3009) by [aantono](https://github.com/aantono))
- **[authentication]** Forward Authentication: add X-Forwarded-Uri ([#2398](https://github.com/containous/traefik/pull/2398) by [sebastianbauer](https://github.com/sebastianbauer))
- **[boltdb,consul,etcd,kv,zk]** Add all available configuration to KV Backend ([#2652](https://github.com/containous/traefik/pull/2652) by [ldez](https://github.com/ldez))
- **[boltdb,consul,etcd,kv,zk]** homogenization of templates: KV ([#2661](https://github.com/containous/traefik/pull/2661) by [ldez](https://github.com/ldez))
- **[boltdb,consul,etcd,kv,zk]** Homogenization of the providers (part 1): KV ([#2616](https://github.com/containous/traefik/pull/2616) by [ldez](https://github.com/ldez))
- **[consul,consulcatalog]** Homogenization of templates: Consul Catalog ([#2668](https://github.com/containous/traefik/pull/2668) by [ldez](https://github.com/ldez))
- **[consul,consulcatalog]** Split consul and consul catalog. ([#2655](https://github.com/containous/traefik/pull/2655) by [ldez](https://github.com/ldez))
- **[consulcatalog,ecs,mesos]** Factorize labels managements. ([#3099](https://github.com/containous/traefik/pull/3099) by [ldez](https://github.com/ldez))
- **[consulcatalog]** Check for endpoints while detecting Consul service changes ([#2882](https://github.com/containous/traefik/pull/2882) by [caseycs](https://github.com/caseycs))
- **[consulcatalog]** TLS Support for ConsulCatalog ([#2900](https://github.com/containous/traefik/pull/2900) by [mmatur](https://github.com/mmatur))
- **[consulcatalog]** Add all available tags to Consul Catalog Backend ([#2646](https://github.com/containous/traefik/pull/2646) by [ldez](https://github.com/ldez))
- **[docker,docker/swarm]** Fix support for macvlan driver in docker provider ([#2827](https://github.com/containous/traefik/pull/2827) by [mmatur](https://github.com/mmatur))
- **[docker,marathon,rancher]** Segments Labels: Rancher & Marathon ([#3073](https://github.com/containous/traefik/pull/3073) by [ldez](https://github.com/ldez))
- **[docker]** Add all available labels to Docker Backend ([#2584](https://github.com/containous/traefik/pull/2584) by [ldez](https://github.com/ldez))
- **[docker]** Homogenization of templates: Docker ([#2659](https://github.com/containous/traefik/pull/2659) by [ldez](https://github.com/ldez))
- **[docker]** Custom headers by service labels for docker backends ([#2514](https://github.com/containous/traefik/pull/2514) by [Tiscs](https://github.com/Tiscs))
- **[docker]** Segment labels: Docker ([#3055](https://github.com/containous/traefik/pull/3055) by [ldez](https://github.com/ldez))
- **[dynamodb,ecs]** Upgrade AWS SKD to version v1.13.1 ([#2908](https://github.com/containous/traefik/pull/2908) by [mmatur](https://github.com/mmatur))
- **[ecs]** Add all available labels to ECS Backend ([#2605](https://github.com/containous/traefik/pull/2605) by [ldez](https://github.com/ldez))
- **[ecs]** Homogenization of templates: ECS ([#2663](https://github.com/containous/traefik/pull/2663) by [ldez](https://github.com/ldez))
- **[ecs]** Factorize labels managements. ([#3159](https://github.com/containous/traefik/pull/3159) by [ldez](https://github.com/ldez))
- **[eureka]** Homogenization of templates: Eureka ([#2846](https://github.com/containous/traefik/pull/2846) by [ldez](https://github.com/ldez))
- **[eureka]** Replace Delay by RefreshSecond in Eureka ([#2972](https://github.com/containous/traefik/pull/2972) by [ldez](https://github.com/ldez))
- **[file]** Added support for templates to file provider ([#2991](https://github.com/containous/traefik/pull/2991) by [aantono](https://github.com/aantono))
- **[healthcheck]** Toggle /ping to artificially return unhealthy response on SIGTERM during requestAcceptGraceTimeout interval ([#3062](https://github.com/containous/traefik/pull/3062) by [ravilr](https://github.com/ravilr))
- **[healthcheck]** Improve logging output for failing healthchecks ([#2443](https://github.com/containous/traefik/pull/2443) by [marco-jantke](https://github.com/marco-jantke))
- **[k8s,tls]** Add support for fetching k8s Ingress TLS data from secrets ([#2439](https://github.com/containous/traefik/pull/2439) by [gopenguin](https://github.com/gopenguin))
- **[k8s]** Introduce k8s informer factory ([#2867](https://github.com/containous/traefik/pull/2867) by [yue9944882](https://github.com/yue9944882))
- **[k8s]** Add all available annotations to k8s Backend ([#2612](https://github.com/containous/traefik/pull/2612) by [ldez](https://github.com/ldez))
- **[k8s]** Bump kubernetes/client-go ([#2848](https://github.com/containous/traefik/pull/2848) by [yue9944882](https://github.com/yue9944882))
- **[k8s]** Add app-root annotation support for kubernetes ingress ([#2522](https://github.com/containous/traefik/pull/2522) by [yue9944882](https://github.com/yue9944882))
- **[k8s]** Builders in k8s tests ([#2513](https://github.com/containous/traefik/pull/2513) by [ldez](https://github.com/ldez))
- **[k8s]** Allow custom value for kubernetes.io/ingress.class annotation ([#2222](https://github.com/containous/traefik/pull/2222) by [yuvipanda](https://github.com/yuvipanda))
- **[logs,middleware]** Add access log filter for retry attempts ([#3042](https://github.com/containous/traefik/pull/3042) by [marco-jantke](https://github.com/marco-jantke))
- **[logs,middleware]** Add username in accesslog ([#2111](https://github.com/containous/traefik/pull/2111) by [bastiaanb](https://github.com/bastiaanb))
- **[logs,middleware]** Ultimate Access log filter ([#2988](https://github.com/containous/traefik/pull/2988) by [mmatur](https://github.com/mmatur))
- **[logs]** Allow overriding the log level in debug mode. ([#3050](https://github.com/containous/traefik/pull/3050) by [timoreimann](https://github.com/timoreimann))
- **[logs]** Display file log when test fails. ([#2801](https://github.com/containous/traefik/pull/2801) by [ldez](https://github.com/ldez))
- **[marathon]** Remove health check filter from Marathon tasks. ([#2817](https://github.com/containous/traefik/pull/2817) by [timoreimann](https://github.com/timoreimann))
- **[marathon]** Add all available labels to Marathon Backend ([#2602](https://github.com/containous/traefik/pull/2602) by [ldez](https://github.com/ldez))
- **[marathon]** homogenization of templates: Marathon ([#2665](https://github.com/containous/traefik/pull/2665) by [ldez](https://github.com/ldez))
- **[mesos]** Add all available labels to Mesos Backend ([#2687](https://github.com/containous/traefik/pull/2687) by [ldez](https://github.com/ldez))
- **[metrics]** Added entrypoint metrics to influxdb ([#2992](https://github.com/containous/traefik/pull/2992) by [adityacs](https://github.com/adityacs))
- **[metrics]** Remove unnecessary conversion ([#2850](https://github.com/containous/traefik/pull/2850) by [ferhatelmas](https://github.com/ferhatelmas))
- **[metrics]** Extend metrics and rebuild prometheus exporting logic ([#2567](https://github.com/containous/traefik/pull/2567) by [marco-jantke](https://github.com/marco-jantke))
- **[metrics]** Added missing metrics to registry for DataDog and StatsD ([#2890](https://github.com/containous/traefik/pull/2890) by [aantono](https://github.com/aantono))
- **[middleware,consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** New option in secure middleware ([#2958](https://github.com/containous/traefik/pull/2958) by [mmatur](https://github.com/mmatur))
- **[middleware,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Ability to use "X-Forwarded-For" as a source of IP for white list. ([#3070](https://github.com/containous/traefik/pull/3070) by [ldez](https://github.com/ldez))
- **[middleware,docker]** Use pointer of error pages ([#2607](https://github.com/containous/traefik/pull/2607) by [ldez](https://github.com/ldez))
- **[middleware,provider]** Redirection: permanent move option. ([#2774](https://github.com/containous/traefik/pull/2774) by [ldez](https://github.com/ldez))
- **[middleware]** Add tests on IPWhiteLister. ([#3106](https://github.com/containous/traefik/pull/3106) by [ldez](https://github.com/ldez))
- **[middleware]** Change port of traefik for error pages integration test ([#2907](https://github.com/containous/traefik/pull/2907) by [mmatur](https://github.com/mmatur))
- **[middleware]** Remove unnecessary returns in tracing setup ([#2880](https://github.com/containous/traefik/pull/2880) by [ferhatelmas](https://github.com/ferhatelmas))
- **[middleware]** Request buffering middleware ([#2217](https://github.com/containous/traefik/pull/2217) by [harnash](https://github.com/harnash))
- **[middleware]** Add new options to the CLI entrypoint definition. ([#2799](https://github.com/containous/traefik/pull/2799) by [ldez](https://github.com/ldez))
- **[provider]** No error pages must return nil. ([#2610](https://github.com/containous/traefik/pull/2610) by [ldez](https://github.com/ldez))
- **[provider]** Homogenization of the providers (part 1) ([#2518](https://github.com/containous/traefik/pull/2518) by [ldez](https://github.com/ldez))
- **[rancher]** Add all available labels to Rancher Backend ([#2601](https://github.com/containous/traefik/pull/2601) by [ldez](https://github.com/ldez))
- **[rancher]** Homogenization of templates: Rancher ([#2662](https://github.com/containous/traefik/pull/2662) by [ldez](https://github.com/ldez))
- **[rules]** Externalize Træfik rules in a dedicated package ([#2933](https://github.com/containous/traefik/pull/2933) by [nmengin](https://github.com/nmengin))
- **[servicefabric]** Use shared label system ([#3197](https://github.com/containous/traefik/pull/3197) by [ldez](https://github.com/ldez))
- **[servicefabric]** Update Service Fabric backend. ([#3064](https://github.com/containous/traefik/pull/3064) by [ldez](https://github.com/ldez))
- **[servicefabric]** Add white list for Service Fabric ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
- **[tls]** Use default entryPoints when certificates are added with no entryPoints. ([#2534](https://github.com/containous/traefik/pull/2534) by [nmengin](https://github.com/nmengin))
- **[tracing]** Handle zipkin collector creation ([#2860](https://github.com/containous/traefik/pull/2860) by [ferhatelmas](https://github.com/ferhatelmas))
- **[tracing]** Opentracing support ([#2587](https://github.com/containous/traefik/pull/2587) by [tcolgate](https://github.com/tcolgate) and [mmatur](https://github.com/mmatur))
- **[webui]** New web ui ([#2226](https://github.com/containous/traefik/pull/2226) by [jkuri](https://github.com/jkuri))
- **[webui]** Add status code text to webui bar chart tooltip ([#2639](https://github.com/containous/traefik/pull/2639) by [wader](https://github.com/wader))
- Logger and Leaks ([#2847](https://github.com/containous/traefik/pull/2847) by [ldez](https://github.com/ldez))
- Separate command from the main package ([#2951](https://github.com/containous/traefik/pull/2951) by [Juliens](https://github.com/Juliens))
- Use context in Server ([#3007](https://github.com/containous/traefik/pull/3007) by [Juliens](https://github.com/Juliens))
**Bug fixes:**
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider ([#2970](https://github.com/containous/traefik/pull/2970) by [nmengin](https://github.com/nmengin))
- **[acme]** Update lego. ([#3158](https://github.com/containous/traefik/pull/3158) by [ldez](https://github.com/ldez))
- **[acme]** Fix panic with wrong ACME configuration ([#3084](https://github.com/containous/traefik/pull/3084) by [nmengin](https://github.com/nmengin))
- **[acme]** Minor updates to dumpcerts.sh ([#3116](https://github.com/containous/traefik/pull/3116) by [mathuin](https://github.com/mathuin))
- **[acme]** Add ACME certificates only on ACME EntryPoint ([#3136](https://github.com/containous/traefik/pull/3136) by [nmengin](https://github.com/nmengin))
- **[acme]** Add TTL and custom Timeout in DigitalOcean DNS provider ([#3143](https://github.com/containous/traefik/pull/3143) by [ldez](https://github.com/ldez))
- **[acme]** Fix acme.json file automatic creation ([#3156](https://github.com/containous/traefik/pull/3156) by [nmengin](https://github.com/nmengin))
- **[acme]** Fix wildcard match to ACME domains in cluster mode ([#3080](https://github.com/containous/traefik/pull/3080) by [oldmantaiter](https://github.com/oldmantaiter))
- **[api,cluster]** Moved /api/cluster/leadership handler under public routes (requires no authentication) ([#3101](https://github.com/containous/traefik/pull/3101) by [aantono](https://github.com/aantono))
- **[authentication,middleware]** Forward auth: copy response headers when auth failed. ([#3207](https://github.com/containous/traefik/pull/3207) by [ldez](https://github.com/ldez))
- **[consul,docker,ecs,eureka,k8s,kv,marathon,mesos,rancher]** Server weight zero ([#3130](https://github.com/containous/traefik/pull/3130) by [ldez](https://github.com/ldez))
- **[docker,k8s,marathon]** Fix custom headers template ([#2622](https://github.com/containous/traefik/pull/2622) by [ldez](https://github.com/ldez))
- **[docker,marathon,mesos,rancher]** Fix: label 'traefik.domain' ([#3201](https://github.com/containous/traefik/pull/3201) by [ldez](https://github.com/ldez))
- **[docker,rancher]** Frontend rule and segment labels. ([#3091](https://github.com/containous/traefik/pull/3091) by [ldez](https://github.com/ldez))
- **[docker,rancher]** Ignore server for container with empty IP address. ([#3213](https://github.com/containous/traefik/pull/3213) by [ldez](https://github.com/ldez))
- **[docker]** Fix multiple frontends with docker-compose --scale ([#3190](https://github.com/containous/traefik/pull/3190) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[healthcheck]** Remove unnecessary mutex usage in health checks ([#2726](https://github.com/containous/traefik/pull/2726) by [marco-jantke](https://github.com/marco-jantke))
- **[k8s]** Missing annotation prefix support. ([#2915](https://github.com/containous/traefik/pull/2915) by [ldez](https://github.com/ldez))
- **[k8s]** Remove hardcoded frontend prefix in Kubernetes template ([#2914](https://github.com/containous/traefik/pull/2914) by [psalaberria002](https://github.com/psalaberria002))
- **[k8s]** Limit label selector to Ingress factory. ([#3137](https://github.com/containous/traefik/pull/3137) by [timoreimann](https://github.com/timoreimann))
- **[k8s]** Fixes prefixed annotations support. ([#3110](https://github.com/containous/traefik/pull/3110) by [ldez](https://github.com/ldez))
- **[logs,middleware]** Fix bad access log ([#2682](https://github.com/containous/traefik/pull/2682) by [mmatur](https://github.com/mmatur))
- **[logs]** Add missing argument in log. ([#3188](https://github.com/containous/traefik/pull/3188) by [chemidy](https://github.com/chemidy))
- **[marathon]** Several apps with same backend name in Marathon. ([#3109](https://github.com/containous/traefik/pull/3109) by [ldez](https://github.com/ldez))
- **[mesos]** fix: overflow on 32 bits arch. ([#3127](https://github.com/containous/traefik/pull/3127) by [ldez](https://github.com/ldez))
- **[metrics]** Fix duplicated tags in InfluxDB ([#3189](https://github.com/containous/traefik/pull/3189) by [mmatur](https://github.com/mmatur))
- **[middleware,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Fix: error pages ([#3138](https://github.com/containous/traefik/pull/3138) by [ldez](https://github.com/ldez))
- **[middleware,tracing]** Fix <nil> tracer value in KV ([#2911](https://github.com/containous/traefik/pull/2911) by [mmatur](https://github.com/mmatur))
- **[middleware,tracing]** Fix nil value when tracing is enabled ([#3192](https://github.com/containous/traefik/pull/3192) by [mmatur](https://github.com/mmatur))
- **[middleware]** Use responseModifier to override secure headers ([#2946](https://github.com/containous/traefik/pull/2946) by [mmatur](https://github.com/mmatur))
- **[middleware]** Correct conditional setting of buffering retry expression. ([#2865](https://github.com/containous/traefik/pull/2865) by [ldez](https://github.com/ldez))
- **[middleware]** Fix high memory usage in retry middleware ([#2740](https://github.com/containous/traefik/pull/2740) by [marco-jantke](https://github.com/marco-jantke))
- **[middleware]** Fix whitelist and XFF. ([#3211](https://github.com/containous/traefik/pull/3211) by [ldez](https://github.com/ldez))
- **[middleware]** Fix panic in atomic on ARM and x86-32 platforms ([#3195](https://github.com/containous/traefik/pull/3195) by [mmatur](https://github.com/mmatur))
- **[middleware]** Redirect to HTTPS first before basic auth if header redirect (secure) is set ([#3187](https://github.com/containous/traefik/pull/3187) by [SantoDE](https://github.com/SantoDE))
- **[middleware]** Fix error pages redirect and headers. ([#3217](https://github.com/containous/traefik/pull/3217) by [ldez](https://github.com/ldez))
- **[provider]** Add some missing quotes in templates ([#2973](https://github.com/containous/traefik/pull/2973) by [ldez](https://github.com/ldez))
- **[servicefabric]** Fix backend name for stateful service and more. ([#3183](https://github.com/containous/traefik/pull/3183) by [ldez](https://github.com/ldez))
- **[tracing]** Fix missing configuration for jaeger reporter ([#2720](https://github.com/containous/traefik/pull/2720) by [mmatur](https://github.com/mmatur))
- **[tracing]** Tracing statusCodeTracker need to implement CloseNotify ([#2733](https://github.com/containous/traefik/pull/2733) by [mmatur](https://github.com/mmatur))
- **[tracing]** Fix integration tests in tracing ([#2759](https://github.com/containous/traefik/pull/2759) by [mmatur](https://github.com/mmatur))
- **[webui]** Remove useless ACME tab from UI. ([#3154](https://github.com/containous/traefik/pull/3154) by [ldez](https://github.com/ldez))
- **[webui]** Add redirect section. ([#3243](https://github.com/containous/traefik/pull/3243) by [ldez](https://github.com/ldez))
**Documentation:**
- **[docker]** Add default values for some Docker labels ([#2604](https://github.com/containous/traefik/pull/2604) by [ldez](https://github.com/ldez))
- **[file]** Add documentation about Templating in backend file ([#3223](https://github.com/containous/traefik/pull/3223) by [nmengin](https://github.com/nmengin))
- **[k8s]** Update traefik-ds.yaml with --api command line parameter ([#2803](https://github.com/containous/traefik/pull/2803) by [maniankara](https://github.com/maniankara))
- **[k8s]** Remove web provider in example ([#2807](https://github.com/containous/traefik/pull/2807) by [pigletfly](https://github.com/pigletfly))
- **[k8s]** Drop capabilities in Kubernetes DaemonSet example ([#3028](https://github.com/containous/traefik/pull/3028) by [nogoegst](https://github.com/nogoegst))
- **[k8s]** Docs: Fix typos in k8s user-guide ([#2898](https://github.com/containous/traefik/pull/2898) by [cez81](https://github.com/cez81))
- **[k8s]** Change boolean annotation values to string ([#2839](https://github.com/containous/traefik/pull/2839) by [hobti01](https://github.com/hobti01))
- **[k8s]** Update kubernetes.md ([#3093](https://github.com/containous/traefik/pull/3093) by [rdrgporto](https://github.com/rdrgporto))
- **[k8s]** Document custom k8s ingress class usage in guide. ([#3242](https://github.com/containous/traefik/pull/3242) by [timoreimann](https://github.com/timoreimann))
- **[k8s]** Update kubernetes.md ([#3171](https://github.com/containous/traefik/pull/3171) by [andreyfedoseev](https://github.com/andreyfedoseev))
- **[provider]** Split security labels and custom labels documentation. ([#2872](https://github.com/containous/traefik/pull/2872) by [ldez](https://github.com/ldez))
- **[provider]** Remove non-supported label. ([#3065](https://github.com/containous/traefik/pull/3065) by [ldez](https://github.com/ldez))
- **[provider]** Remove obsolete paragraph about error pages. ([#2608](https://github.com/containous/traefik/pull/2608) by [ldez](https://github.com/ldez))
- **[provider]** Cleaning labels/annotations documentation. ([#3245](https://github.com/containous/traefik/pull/3245) by [ldez](https://github.com/ldez))
- **[provider]** Fix template version documentation. ([#3184](https://github.com/containous/traefik/pull/3184) by [ldez](https://github.com/ldez))
- **[servicefabric]** Add SF to supported backends in docs ([#3033](https://github.com/containous/traefik/pull/3033) by [lawrencegripper](https://github.com/lawrencegripper))
- **[servicefabric]** Update SF white list documentation section. ([#3082](https://github.com/containous/traefik/pull/3082) by [ldez](https://github.com/ldez))
- **[tracing]** Fix typo in doc for rate limit label ([#2790](https://github.com/containous/traefik/pull/2790) by [mmatur](https://github.com/mmatur))
- **[tracing]** Add Tracing entry in the documentation. ([#2713](https://github.com/containous/traefik/pull/2713) by [ldez](https://github.com/ldez))
- **[tracing]** Fix documentation for tracing with Jaeger ([#3227](https://github.com/containous/traefik/pull/3227) by [mmatur](https://github.com/mmatur))
- **[webui]** doc: update Traefik images. ([#3241](https://github.com/containous/traefik/pull/3241) by [ldez](https://github.com/ldez))
- Fix typo in documentation ([#3215](https://github.com/containous/traefik/pull/3215) by [arnaslu](https://github.com/arnaslu))
- Minor improvements to documentation ([#3221](https://github.com/containous/traefik/pull/3221) by [colincoller](https://github.com/colincoller))
- Update some examples ([#3150](https://github.com/containous/traefik/pull/3150) by [zaporylie](https://github.com/zaporylie))
- Normalize parameter names in configs ([#3132](https://github.com/containous/traefik/pull/3132) by [kachkaev](https://github.com/kachkaev))
- Fixed documentation urls on README.md ([#3102](https://github.com/containous/traefik/pull/3102) by [emir](https://github.com/emir))
- Fix typo and tweak formatting in quickstart ([#3250](https://github.com/containous/traefik/pull/3250) by [alexymik](https://github.com/alexymik))
- Fix basic documentation ([#3086](https://github.com/containous/traefik/pull/3086) by [mmatur](https://github.com/mmatur))
- Prepare release v1.6.0-rc6 ([#3199](https://github.com/containous/traefik/pull/3199) by [mmatur](https://github.com/mmatur))
- Prepare release v1.6.0-rc5 ([#3179](https://github.com/containous/traefik/pull/3179) by [Juliens](https://github.com/Juliens))
- Prepare release v1.6.0-rc4 ([#3126](https://github.com/containous/traefik/pull/3126) by [ldez](https://github.com/ldez))
- Prepare release v1.6.0-rc3 ([#3096](https://github.com/containous/traefik/pull/3096) by [ldez](https://github.com/ldez))
- Prepare release v1.6.0-rc2 ([#3087](https://github.com/containous/traefik/pull/3087) by [nmengin](https://github.com/nmengin))
- Prepare release v1.6.0-rc1 ([#3078](https://github.com/containous/traefik/pull/3078) by [Juliens](https://github.com/Juliens))
- Prepare release v1.6.0 ([#3251](https://github.com/containous/traefik/pull/3251) by [Juliens](https://github.com/Juliens))
**Misc:**
- **[oxy]** Disable closeNotify when method GET for http pipelining ([#3108](https://github.com/containous/traefik/pull/3108) by [Juliens](https://github.com/Juliens))
- **[boltdb,consul,etcd,kv,zk]** Migrate from libkv to valkeyrie library ([#2743](https://github.com/containous/traefik/pull/2743) by [nmengin](https://github.com/nmengin))
- Drop unnecessary type conversions ([#2583](https://github.com/containous/traefik/pull/2583) by [ferhatelmas](https://github.com/ferhatelmas))
- Code simplification ([#2516](https://github.com/containous/traefik/pull/2516) by [ferhatelmas](https://github.com/ferhatelmas))
- Merge v1.5.4 into master ([#3024](https://github.com/containous/traefik/pull/3024) by [ldez](https://github.com/ldez))
- Merge v1.5.3 into master ([#2943](https://github.com/containous/traefik/pull/2943) by [ldez](https://github.com/ldez))
- Merge v1.5.2 into master ([#2843](https://github.com/containous/traefik/pull/2843) by [ldez](https://github.com/ldez))
- Merge v1.5.1 into master ([#2781](https://github.com/containous/traefik/pull/2781) by [ldez](https://github.com/ldez))
- Merge v1.5.0-rc5 into master ([#2708](https://github.com/containous/traefik/pull/2708) by [ldez](https://github.com/ldez))
- Merge v1.5.0-rc3 into master ([#2600](https://github.com/containous/traefik/pull/2600) by [ldez](https://github.com/ldez))
- Merge v1.5.0-rc2 into master ([#2536](https://github.com/containous/traefik/pull/2536) by [ldez](https://github.com/ldez))
## [v1.6.0-rc6](https://github.com/containous/traefik/tree/v1.6.0-rc6) (2018-04-17)
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc5...v1.6.0-rc6)

View file

@ -14,9 +14,19 @@ type DashboardHandler struct{}
// AddRoutes add dashboard routes on a router
func (g DashboardHandler) AddRoutes(router *mux.Router) {
// Expose dashboard
router.Methods(http.MethodGet).Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", 302)
})
router.Methods(http.MethodGet).PathPrefix("/dashboard/").
router.Methods(http.MethodGet).
Path("/").
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", 302)
})
router.Methods(http.MethodGet).
Path("/dashboard/status").
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
http.Redirect(response, request, "/dashboard/", 302)
})
router.Methods(http.MethodGet).
PathPrefix("/dashboard/").
Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"})))
}

View file

@ -90,7 +90,6 @@ Additional settings can be defined using Consul Catalog tags.
| Label | Description |
|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<prefix>.enable=false` | Disable this container in Træfik. |
| `<prefix>.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `<prefix>.protocol=https` | Override the default `http` protocol. |
| `<prefix>.weight=10` | Assign this weight to the container. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
@ -148,7 +147,17 @@ Additional settings can be defined using Consul Catalog tags.
| Label | Description |
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<prefix>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `<prefix>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `<prefix>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `<prefix>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `<prefix>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `<prefix>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `<prefix>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `<prefix>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `<prefix>.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `<prefix>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `<prefix>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `<prefix>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `<prefix>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `<prefix>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `<prefix>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
@ -156,16 +165,6 @@ Additional settings can be defined using Consul Catalog tags.
| `<prefix>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `<prefix>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `<prefix>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `<prefix>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `<prefix>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `<prefix>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `<prefix>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `<prefix>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `<prefix>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `<prefix>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `<prefix>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
### Examples

View file

@ -257,7 +257,17 @@ Or if your service references external network use it's name instead.
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
@ -265,16 +275,6 @@ Or if your service references external network use it's name instead.
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
### On containers with Multiple Ports (segment labels)
@ -284,63 +284,63 @@ You can define as many segments as ports exposed in a container.
Segment labels override the default behavior.
| Label | Description |
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.domain` | Default domain used for frontend rules. |
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the segment labels could be used. |
| `traefik.<segment_name>.protocol` | Overrides `traefik.protocol`. |
| `traefik.<segment_name>.weight` | Assign this segment weight. Overrides `traefik.weight`. |
| `traefik.<segment_name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this segment frontend to `BACKEND`. Default is to assign to the segment backend. |
| `traefik.<segment_name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
| `traefik.<segment_name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
| `traefik.<segment_name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Overrides `traefik.frontend.whiteList.useXForwardedFor`. |
| Label | Description |
|---------------------------------------------------------------------------|-------------------------------------------------------------|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Same as `traefik.frontend.backend` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| Label | Description |
|----------------------------------------------------------------------|----------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
!!! note
If a label is defined both as a `container label` and a `segment label` (for example `traefik.<segment_name>.port=PORT` and `traefik.port=PORT` ), the `segment label` is used to defined the `<segment_name>` property (`port` in the example).

View file

@ -191,7 +191,17 @@ Labels can be used on task containers to override default behaviour:
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
@ -199,13 +209,3 @@ Labels can be used on task containers to override default behaviour:
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |

View file

@ -218,24 +218,24 @@ The following security annotations are applicable on the Ingress object:
| Annotation | Description |
| ----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ingress.kubernetes.io/allowed-hosts: EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
| `ingress.kubernetes.io/browser-xss-filter: "true"` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `ingress.kubernetes.io/content-security-policy: VALUE` | Adds CSP Header with the custom value. |
| `ingress.kubernetes.io/content-type-nosniff: "true"` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `ingress.kubernetes.io/custom-browser-xss-value: VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `ingress.kubernetes.io/force-hsts: "false"` | Adds the STS header to non-SSL requests. |
| `ingress.kubernetes.io/frame-deny: "false"` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `ingress.kubernetes.io/hsts-max-age: "315360000"` | Sets the max-age of the HSTS header. |
| `ingress.kubernetes.io/hsts-include-subdomains: "true"` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-preload: "true"` | Adds the preload flag to the HSTS header. |
| `ingress.kubernetes.io/is-development: "false"` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `ingress.kubernetes.io/proxy-headers: EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
| `ingress.kubernetes.io/public-key: VALUE` | Adds pinned HTST public key header. |
| `ingress.kubernetes.io/referrer-policy: VALUE` | Adds referrer policy header. |
| `ingress.kubernetes.io/ssl-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `ingress.kubernetes.io/ssl-temporary-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `ingress.kubernetes.io/ssl-host: HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `ingress.kubernetes.io/hsts-max-age: "315360000"` | Sets the max-age of the HSTS header. |
| `ingress.kubernetes.io/hsts-include-subdomains: "true"` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-preload: "true"` | Adds the preload flag to the HSTS header. |
| `ingress.kubernetes.io/force-hsts: "false"` | Adds the STS header to non-SSL requests. |
| `ingress.kubernetes.io/frame-deny: "false"` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `ingress.kubernetes.io/content-type-nosniff: "true"` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `ingress.kubernetes.io/browser-xss-filter: "true"` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `ingress.kubernetes.io/custom-browser-xss-value: VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `ingress.kubernetes.io/content-security-policy: VALUE` | Adds CSP Header with the custom value. |
| `ingress.kubernetes.io/public-key: VALUE` | Adds pinned HTST public key header. |
| `ingress.kubernetes.io/referrer-policy: VALUE` | Adds referrer policy header. |
| `ingress.kubernetes.io/is-development: "false"` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
### Authentication

View file

@ -228,7 +228,17 @@ The following labels can be defined on Marathon applications. They adjust the be
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
@ -236,16 +246,6 @@ The following labels can be defined on Marathon applications. They adjust the be
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
### Applications with Multiple Ports (segment labels)
@ -255,62 +255,61 @@ You can define as many segments as ports exposed in an application.
Segment labels override the default behavior.
| Label | Description |
|---------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.domain` | Default domain used for frontend rules. |
| `traefik.<segment_name>.portIndex=1` | Create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. |
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
| `traefik.<segment_name>.protocol=http` | Overrides `traefik.protocol`. |
| `traefik.<segment_name>.weight=10` | Assign this service weight. Overrides `traefik.weight`. |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Sets a Basic Auth for that frontend |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
| `traefik.<segment_name>.frontend.entryPoints=https` | Overrides `traefik.frontend.entrypoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Overrides `traefik.frontend.passHostHeader`. |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Overrides `traefik.frontend.passTLSCert`. |
| `traefik.<segment_name>.frontend.priority=10` | Overrides `traefik.frontend.priority`. |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.<segment_name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
| Label | Description |
|---------------------------------------------------------------------------|-------------------------------------------------------------|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Same as `traefik.frontend.backend` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| Label | Description |
|----------------------------------------------------------------------|----------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |

View file

@ -163,7 +163,17 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
@ -171,13 +181,3 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |

View file

@ -195,7 +195,17 @@ Labels can be used on task containers to override default behavior:
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
@ -203,16 +213,6 @@ Labels can be used on task containers to override default behavior:
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
### On containers with Multiple Ports (segment labels)
@ -222,60 +222,60 @@ You can define as many segments as ports exposed in a container.
Segment labels override the default behavior.
| Label | Description |
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.domain` | Default domain used for frontend rules. |
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the segment labels could be used. |
| `traefik.<segment_name>.protocol` | Overrides `traefik.protocol`. |
| `traefik.<segment_name>.weight` | Assign this segment weight. Overrides `traefik.weight`. |
| `traefik.<segment_name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this segment frontend to `BACKEND`. Default is to assign to the segment backend. |
| `traefik.<segment_name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
| `traefik.<segment_name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
| `traefik.<segment_name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Overrides `traefik.frontend.whiteList.useXForwardedFor`. |
| Label | Description |
|---------------------------------------------------------------------------|-------------------------------------------------------------|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Same as `traefik.frontend.backend` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|-----------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders=EXPR ` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders=EXPR` |
| Label | Description |
|----------------------------------------------------------------------|------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts=EXPR` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect=true` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect=true` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost=HOST` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload=true` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader=false` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny=false` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue=VALUE` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff=true` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter=true` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue=VALUE` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy=VALUE` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey=VALUE` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy=VALUE` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment=false` |
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload` |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 218 KiB

View file

@ -77,13 +77,13 @@ version: '3'
services:
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
image: traefik # The official Traefik docker image
command: --api --docker # Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
- "80:80" # The HTTP port
- "8080:8080" # The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
```
**That's it. Now you can launch Træfik!**
@ -105,7 +105,7 @@ Edit your `docker-compose.yml` file and add the following at the end of your fil
```yaml
# ...
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
image: emilevauge/whoami # A container that exposes an API to show its IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
```

View file

@ -350,7 +350,8 @@ We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube)
### Add a TLS Certificate to the Ingress
!!! note
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point. For more details see [here](/configuration/entrypoints/).
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point.
For more details see [here](/configuration/entrypoints/).
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
@ -374,7 +375,8 @@ spec:
- secretName: traefik-ui-tls-cert
```
In addition to the modified ingress you need to provide the TLS certificate via a Kubernetes secret in the same namespace as the ingress. The following two commands will generate a new certificate and create a secret containing the key and cert files.
In addition to the modified ingress you need to provide the TLS certificate via a Kubernetes secret in the same namespace as the ingress.
The following two commands will generate a new certificate and create a secret containing the key and cert files.
```shell
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=traefik-ui.minikube"
@ -384,13 +386,16 @@ kubectl -n kube-system create secret tls traefik-ui-tls-cert --key=tls.key --cer
If there are any errors while loading the TLS section of an ingress, the whole ingress will be skipped.
!!! note
The secret must have two entries named `tls.key`and `tls.crt`. See the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) for more details.
The secret must have two entries named `tls.key`and `tls.crt`.
See the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) for more details.
!!! note
The TLS certificates will be added to all entrypoints defined by the ingress annotation `traefik.frontend.entryPoints`. If no such annotation is provided, the TLS certificates will be added to all TLS-enabled `defaultEntryPoints`.
The TLS certificates will be added to all entrypoints defined by the ingress annotation `traefik.frontend.entryPoints`.
If no such annotation is provided, the TLS certificates will be added to all TLS-enabled `defaultEntryPoints`.
!!! note
The field `hosts` in the TLS configuration is ignored. Instead, the domains provided by the certificate are used for this purpose. It is recommended to not use wildcard certificates as they will match globally.
The field `hosts` in the TLS configuration is ignored. Instead, the domains provided by the certificate are used for this purpose.
It is recommended to not use wildcard certificates as they will match globally.
## Basic Authentication
@ -831,13 +836,21 @@ Sometimes Træfik runs along other Ingress controller implementations. One such
The `kubernetes.io/ingress.class` annotation can be attached to any Ingress object in order to control whether Træfik should handle it.
If the annotation is missing, contains an empty value, or the value `traefik`, then the Træfik controller will take responsibility and process the associated Ingress object. If the annotation contains any other value (usually the name of a different Ingress controller), Træfik will ignore the object.
If the annotation is missing, contains an empty value, or the value `traefik`, then the Træfik controller will take responsibility and process the associated Ingress object.
If the annotation contains any other value (usually the name of a different Ingress controller), Træfik will ignore the object.
It is also possible to set the `ingressClass` option in Træfik to a particular value.
If that's the case and the value contains a `traefik` prefix, then only those Ingress objects matching the same value will be processed.
For instance, setting the option to `traefik-internal` causes Træfik to process Ingress objects with the same `kubernetes.io/ingress.class` annotation value, ignoring all other objects (including those with a `traefik` value, empty value, and missing annotation).
### Between multiple Træfik Deployments
Sometimes multiple Træfik Deployments are supposed to run concurrently. For instance, it is conceivable to have one Deployment deal with internal and another one with external traffic.
Sometimes multiple Træfik Deployments are supposed to run concurrently.
For instance, it is conceivable to have one Deployment deal with internal and another one with external traffic.
For such cases, it is advisable to classify Ingress objects through a label and configure the `labelSelector` option per each Træfik Deployment accordingly. To stick with the internal/external example above, all Ingress objects meant for internal traffic could receive a `traffic-type: internal` label while objects designated for external traffic receive a `traffic-type: external` label. The label selectors on the Træfik Deployments would then be `traffic-type=internal` and `traffic-type=external`, respectively.
For such cases, it is advisable to classify Ingress objects through a label and configure the `labelSelector` option per each Træfik Deployment accordingly.
To stick with the internal/external example above, all Ingress objects meant for internal traffic could receive a `traffic-type: internal` label while objects designated for external traffic receive a `traffic-type: external` label.
The label selectors on the Træfik Deployments would then be `traffic-type=internal` and `traffic-type=external`, respectively.
## Production advice

View file

@ -13,11 +13,11 @@ version: '3'
services:
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
image: traefik # The official Traefik docker image
command: --api --docker # Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
- "80:80" # The HTTP port
- "8080:8080" # The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
```
@ -41,7 +41,7 @@ Edit your `docker-compose.yml` file and add the following at the end of your fil
```yaml
# ...
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
image: emilevauge/whoami # A container that exposes an API to show its IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
```
@ -104,4 +104,4 @@ IP: 172.27.0.4
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](https://docs.traefik.io/) and let Træfik work for you!
Whatever your infrastructure is, there is probably [an available Træfik backend](https://docs.traefik.io/#supported-backends) that will do the job.
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).

63
webui/.angular-cli.json Normal file
View file

@ -0,0 +1,63 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "webui"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles/app.sass"
],
"scripts": [
"../node_modules/@fortawesome/fontawesome/index.js",
"../node_modules/@fortawesome/fontawesome-free-solid/index.js"
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "sass",
"component": {}
}
}

View file

@ -1,3 +0,0 @@
{
"presets": ["es2015"]
}

View file

@ -1,13 +1,13 @@
# http://editorconfig.org
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View file

@ -1 +0,0 @@
* text=auto

48
webui/.gitignore vendored
View file

@ -1,6 +1,44 @@
.tmp/
coverage/
dist/
node_modules/
.sass-cache/
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db

View file

@ -1,20 +0,0 @@
{
"generator-fountain-angular1": {
"version": "0.6.0",
"props": {
"resolved": "/Users/micael/Documents/zenika/fountain/generator-fountain-angular1/generators/app/index.js",
"namespace": "fountain-angular1:app",
"argv": {
"remain": [],
"cooked": [],
"original": []
},
"framework": "angular1",
"modules": "webpack",
"css": "scss",
"js": "js",
"sample": "hello",
"router": "none"
}
}
}

View file

@ -19,4 +19,4 @@ RUN yarn install
COPY . $WEBUI_DIR/
EXPOSE 3000 3001 8080
EXPOSE 8080

View file

@ -1,12 +0,0 @@
const conf = require('./gulp.conf');
module.exports = function () {
return {
server: {
baseDir: [
conf.paths.dist
]
},
open: false
};
};

View file

@ -1,28 +0,0 @@
const conf = require('./gulp.conf');
const proxy = require('http-proxy-middleware');
const apiProxy = proxy('/api', {
target: 'http://localhost:8080',
changeOrigin: true
});
const healthProxy = proxy('/health', {
target: 'http://localhost:8080',
changeOrigin: true
});
module.exports = function () {
return {
server: {
baseDir: [
conf.paths.tmp,
conf.paths.src
],
middleware: [
apiProxy,
healthProxy
]
},
open: false
};
};

View file

@ -1,47 +0,0 @@
'use strict';
/**
* This file contains the variables used in other gulp files
* which defines tasks
* By design, we only put there very generic config values
* which are used in several places to keep good readability
* of the tasks
*/
const path = require('path');
const gutil = require('gulp-util');
exports.ngModule = 'traefik';
/**
* The main paths of your project handle these with care
*/
exports.paths = {
src: 'src',
dist: '../static',
tmp: '.tmp',
e2e: 'e2e',
tasks: 'gulp_tasks'
};
exports.path = {};
for (const pathName in exports.paths) {
if (exports.paths.hasOwnProperty(pathName)) {
exports.path[pathName] = function pathJoin() {
const pathValue = exports.paths[pathName];
const funcArgs = Array.prototype.slice.call(arguments);
const joinArgs = [pathValue].concat(funcArgs);
return path.join.apply(this, joinArgs);
};
}
}
/**
* Common implementation for an error handler of a Gulp plugin
*/
exports.errorHandler = function (title) {
return function (err) {
gutil.log(gutil.colors.red(`[${title}]`), err.toString());
this.emit('end');
};
};

View file

@ -1,55 +0,0 @@
const conf = require('./gulp.conf');
module.exports = function (config) {
const configuration = {
basePath: '../',
singleRun: false,
autoWatch: true,
logLevel: 'INFO',
junitReporter: {
outputDir: 'test-reports'
},
browsers: [
'PhantomJS'
],
frameworks: [
'jasmine'
],
files: [
'node_modules/es6-shim/es6-shim.js',
conf.path.src('index.spec.js'),
conf.path.src('**/*.html')
],
preprocessors: {
[conf.path.src('index.spec.js')]: [
'webpack'
],
[conf.path.src('**/*.html')]: [
'ng-html2js'
]
},
ngHtml2JsPreprocessor: {
stripPrefix: `${conf.paths.src}/`
},
reporters: ['progress', 'coverage'],
coverageReporter: {
type: 'html',
dir: 'coverage/'
},
webpack: require('./webpack-test.conf'),
webpackMiddleware: {
noInfo: true
},
plugins: [
require('karma-jasmine'),
require('karma-junit-reporter'),
require('karma-coverage'),
require('karma-phantomjs-launcher'),
require('karma-phantomjs-shim'),
require('karma-ng-html2js-preprocessor'),
require('karma-webpack')
]
};
config.set(configuration);
};

View file

@ -1,55 +0,0 @@
const conf = require('./gulp.conf');
module.exports = function (config) {
const configuration = {
basePath: '../',
singleRun: true,
autoWatch: false,
logLevel: 'INFO',
junitReporter: {
outputDir: 'test-reports'
},
browsers: [
'PhantomJS'
],
frameworks: [
'jasmine'
],
files: [
'node_modules/es6-shim/es6-shim.js',
conf.path.src('index.spec.js'),
conf.path.src('**/*.html')
],
preprocessors: {
[conf.path.src('index.spec.js')]: [
'webpack'
],
[conf.path.src('**/*.html')]: [
'ng-html2js'
]
},
ngHtml2JsPreprocessor: {
stripPrefix: `${conf.paths.src}/`
},
reporters: ['progress', 'coverage'],
coverageReporter: {
type: 'html',
dir: 'coverage/'
},
webpack: require('./webpack-test.conf'),
webpackMiddleware: {
noInfo: true
},
plugins: [
require('karma-jasmine'),
require('karma-junit-reporter'),
require('karma-coverage'),
require('karma-phantomjs-launcher'),
require('karma-phantomjs-shim'),
require('karma-ng-html2js-preprocessor'),
require('karma-webpack')
]
};
config.set(configuration);
};

View file

@ -1,75 +0,0 @@
const webpack = require('webpack');
const conf = require('./gulp.conf');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SplitByPathPlugin = require('webpack-split-by-path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const autoprefixer = require('autoprefixer');
module.exports = {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint'
}
],
loaders: [
{
test: /.json$/,
loaders: [
'json'
]
},
{
test: /\.(css|scss)$/,
loaders: ExtractTextPlugin.extract('style', 'css?minimize!sass', 'postcss')
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'babel-loader',
'ng-annotate'
]
},
{
test: /.html$/,
loaders: [
'html'
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
template: conf.path.src('index.html'),
inject: true
}),
new webpack.optimize.UglifyJsPlugin({
compress: {unused: true, dead_code: true} // eslint-disable-line camelcase
}),
new SplitByPathPlugin([{
name: 'vendor',
path: path.join(__dirname, '../node_modules')
}]),
new ExtractTextPlugin('./index-[contenthash].css')
],
postcss: () => [autoprefixer],
output: {
path: path.join(process.cwd(), conf.paths.dist),
filename: './[name]-[hash].js'
},
entry: {
app: `./${conf.path.src('index')}`
}
};

View file

@ -1,41 +0,0 @@
module.exports = {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint'
}
],
loaders: [
{
test: /.json$/,
loaders: [
'json'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate'
]
},
{
test: /.html$/,
loaders: [
'html'
]
},
{
test: /\.js$/,
exclude: /(node_modules|.*\.spec\.js)/,
loader: 'isparta'
}
]
},
plugins: [],
debug: true,
devtool: 'cheap-module-eval-source-map'
};

View file

@ -1,61 +0,0 @@
const webpack = require('webpack');
const conf = require('./gulp.conf');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');
module.exports = {
module: {
loaders: [
{
test: /.json$/,
loaders: [
'json'
]
},
{
test: /\.(css|scss)$/,
loaders: [
'style',
'css',
'sass',
'postcss'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate'
]
},
{
test: /.html$/,
loaders: [
'html'
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
template: conf.path.src('index.html'),
inject: true
})
],
postcss: () => [autoprefixer],
debug: true,
devtool: 'cheap-module-eval-source-map',
output: {
path: path.join(process.cwd(), conf.paths.tmp),
filename: 'index.js'
},
entry: `./${conf.path.src('index')}`
};

View file

@ -1,21 +0,0 @@
const gulp = require('gulp');
const browserSync = require('browser-sync');
const spa = require('browser-sync-spa');
const browserSyncConf = require('../conf/browsersync.conf');
const browserSyncDistConf = require('../conf/browsersync-dist.conf');
browserSync.use(spa());
gulp.task('browsersync', browserSyncServe);
gulp.task('browsersync:dist', browserSyncDist);
function browserSyncServe(done) {
browserSync.init(browserSyncConf());
done();
}
function browserSyncDist(done) {
browserSync.init(browserSyncDistConf());
done();
}

View file

@ -1,25 +0,0 @@
const path = require('path');
const gulp = require('gulp');
const karma = require('karma');
gulp.task('karma:single-run', karmaSingleRun);
gulp.task('karma:auto-run', karmaAutoRun);
function karmaFinishHandler(done) {
return failCount => {
done(failCount ? new Error(`Failed ${failCount} tests.`) : null);
};
}
function karmaSingleRun(done) {
const configFile = path.join(process.cwd(), 'conf', 'karma.conf.js');
const karmaServer = new karma.Server({configFile}, karmaFinishHandler(done));
karmaServer.start();
}
function karmaAutoRun(done) {
const configFile = path.join(process.cwd(), 'conf', 'karma-auto.conf.js');
const karmaServer = new karma.Server({configFile}, karmaFinishHandler(done));
karmaServer.start();
}

View file

@ -1,25 +0,0 @@
const path = require('path');
const gulp = require('gulp');
const del = require('del');
const filter = require('gulp-filter');
const conf = require('../conf/gulp.conf');
gulp.task('clean', clean);
gulp.task('other', other);
function clean() {
return del([conf.paths.tmp]);
}
function other() {
const fileFilter = filter(file => file.stat.isFile());
return gulp.src([
path.join(conf.paths.src, '/**/*'),
path.join(`!${conf.paths.src}`, '/**/*.{scss,js,html}')
])
.pipe(fileFilter)
.pipe(gulp.dest(conf.paths.dist));
}

View file

@ -1,48 +0,0 @@
/* eslint angular/module-getter:0 */
const gulp = require('gulp');
const gutil = require('gulp-util');
const webpack = require('webpack');
const webpackConf = require('../conf/webpack.conf');
const webpackDistConf = require('../conf/webpack-dist.conf');
const browsersync = require('browser-sync');
gulp.task('webpack:dev', done => {
webpackWrapper(false, webpackConf, done);
});
gulp.task('webpack:watch', done => {
webpackWrapper(true, webpackConf, done);
});
gulp.task('webpack:dist', done => {
webpackWrapper(false, webpackDistConf, done);
});
function webpackWrapper(watch, conf, done) {
const webpackBundler = webpack(conf);
const webpackChangeHandler = (err, stats) => {
if (err) {
conf.errorHandler('Webpack')(err);
}
gutil.log(stats.toString({
colors: true,
chunks: false,
hash: false,
version: false
}));
if (done) {
done();
done = null;
} else {
browsersync.reload();
}
};
if (watch) {
webpackBundler.watch(200, webpackChangeHandler);
} else {
webpackBundler.run(webpackChangeHandler);
}
}

View file

@ -1,29 +0,0 @@
const gulp = require('gulp');
const HubRegistry = require('gulp-hub');
const browserSync = require('browser-sync');
const conf = require('./conf/gulp.conf');
// Load some files into the registry
const hub = new HubRegistry([conf.path.tasks('*.js')]);
// Tell gulp to use the tasks just loaded
gulp.registry(hub);
gulp.task('build', gulp.series(gulp.parallel('other', 'webpack:dist')));
gulp.task('test', gulp.series('karma:single-run'));
gulp.task('test:auto', gulp.series('karma:auto-run'));
gulp.task('serve', gulp.series('webpack:watch', 'watch', 'browsersync'));
gulp.task('serve:dist', gulp.series('default', 'browsersync:dist'));
gulp.task('default', gulp.series('clean', 'build'));
gulp.task('watch', watch);
function reloadBrowserSync(cb) {
browserSync.reload();
cb();
}
function watch(done) {
gulp.watch(conf.path.src('app/**/*.html'), reloadBrowserSync);
done();
}

33
webui/karma.conf.js Normal file
View file

@ -0,0 +1,33 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View file

@ -1,103 +1,58 @@
{
"name": "traefik",
"version": "2.0.0",
"homepage": "http://traefik.io",
"version": "3.0.0",
"authors": [
"Fernandez Ludovic <lfernandez.dev@gmail.com>",
"Micaël Mbagira <micael.mbagira@icloud.com>"
"Micaël Mbagira <micael.mbagira@icloud.com>",
"Jan Kuri <jan@bleenco.com>"
],
"description": "Front end for Træfik",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build --prod --no-delete-output-path --output-path ../static/",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"dependencies": {
"angular": "^1.4.2",
"angular-animate": "^1.5.8",
"angular-aria": "^1.5.8",
"angular-cookies": "^1.5.8",
"angular-messages": "^1.5.8",
"angular-nvd3": "^1.0.8",
"angular-resource": "^1.5.8",
"angular-sanitize": "^1.5.8",
"angular-ui-bootstrap": "^2.0.0",
"angular-ui-router": "^0.3.1",
"animate.css": "^3.4.0",
"bootstrap": "^3.3.6",
"http-status-codes": "^1.3.0",
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"@fortawesome/fontawesome": "^1.1.5",
"@fortawesome/fontawesome-free-solid": "^5.0.10",
"bulma": "^0.6.2",
"core-js": "^2.4.1",
"d3": "^4.13.0",
"date-fns": "^1.29.0",
"lodash": "^4.17.5",
"moment": "^2.14.1",
"nvd3": "^1.8.4"
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
"devDependencies": {
"angular-mocks": "^1.4.2",
"autoprefixer": "^6.2.2",
"babel-core": "^6.24.1",
"babel-loader": "^7.0.0",
"babel-preset-es2015": "^6.24.1",
"browser-sync": "^2.9.11",
"browser-sync-spa": "^1.0.3",
"css-loader": "^0.23.1",
"del": "^2.0.2",
"es6-shim": "^0.35.0",
"eslint": "^2.11.0",
"eslint-config-angular": "^0.5.0",
"eslint-config-xo-space": "^0.12.0",
"eslint-loader": "^1.3.0",
"eslint-plugin-angular": "^1.3.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"gulp": "gulpjs/gulp#4ed9a4a3275559c73a396eff7e1fde3824951ebb",
"gulp-angular-filesort": "^1.1.1",
"gulp-angular-templatecache": "^1.8.0",
"gulp-filter": "^4.0.0",
"gulp-htmlmin": "^1.3.0",
"gulp-hub": "frankwallis/gulp-hub#d461b9c700df9010d0a8694e4af1fb96d9f38bf4",
"gulp-insert": "^0.5.0",
"gulp-ng-annotate": "^1.1.0",
"gulp-sass": "^2.1.1",
"gulp-util": "^3.0.7",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.9.0",
"http-proxy-middleware": "^0.17.4",
"isparta-loader": "^2.0.0",
"jasmine": "^2.4.1",
"json-loader": "^0.5.4",
"karma": "^0.13.14",
"karma-angular-filesort": "^1.0.0",
"karma-coverage": "^0.5.3",
"karma-jasmine": "^0.3.8",
"karma-junit-reporter": "^0.4.2",
"karma-ng-html2js-preprocessor": "^0.2.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-phantomjs-shim": "^1.1.2",
"karma-webpack": "^1.7.0",
"ng-annotate-loader": "^0.0.10",
"node-sass": "^3.4.2",
"phantomjs-prebuilt": "^2.1.6",
"postcss-loader": "^0.8.0",
"sass-loader": "^3.1.2",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7",
"webpack": "2.1.0-beta.15",
"webpack-split-by-path": "^0.0.10"
},
"scripts": {
"build": "gulp",
"serve": "gulp serve",
"serve:dist": "gulp serve:dist",
"test": "gulp test",
"test:auto": "gulp test:auto"
},
"eslintConfig": {
"globals": {
"expect": true
},
"root": true,
"env": {
"browser": true,
"jasmine": true
},
"extends": [
"angular",
"xo-space"
]
"@angular/cli": "~1.7.2",
"@angular/compiler-cli": "^5.2.0",
"@angular/language-service": "^5.2.0",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.5.3"
}
}

28
webui/protractor.conf.js Normal file
View file

@ -0,0 +1,28 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

10
webui/proxy.conf.json Normal file
View file

@ -0,0 +1,10 @@
{
"/api": {
"target": "http://localhost:8080",
"secure": false
},
"/health": {
"target": "http://localhost:8080",
"secure": false
}
}

View file

@ -19,7 +19,7 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
## How to build (only for frontends developer)
- prerequisite: [Node 4+](https://nodejs.org) [yarn](https://yarnpkg.com/)
- prerequisite: [Node 6+](https://nodejs.org) [yarn](https://yarnpkg.com/)
Note: In case of conflict with the Apache Hadoop Yarn Command Line Interface, use the `yarnpkg`
alias.
@ -51,29 +51,15 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
- Go to the directory `webui`
- Edit files in `webui/src`
- Run in development mode :
- `yarn run serve`
- Træfik API connections are defined in:
- `webui/src/app/core/health.resource.js`
- `webui/src/app/core/providers.resource.js`
- The pages contents are in the directory `webui/src/app/sections`.
- `yarn start`
## Libraries
- [Node](https://nodejs.org)
- [Yarn](https://yarnpkg.com/)
- [Generator FountainJS](https://github.com/FountainJS/generator-fountain-webapp)
- [Webpack](https://github.com/webpack/webpack)
- [AngularJS](https://docs.angularjs.org/api)
- [UI Router](https://github.com/angular-ui/ui-router)
- [UI Router - Documentation](https://github.com/angular-ui/ui-router/wiki)
- [Bootstrap](https://getbootstrap.com)
- [Angular Bootstrap](https://angular-ui.github.io/bootstrap)
- [Angular](https://angular.io)
- [Bulma](https://bulma.io)
- [D3](https://d3js.org)
- [D3 - Documentation](https://github.com/mbostock/d3/wiki)
- [NVD3](http://nvd3.org)
- [Angular nvD3](https://krispo.github.io/angular-nvd3)
- [D3 - Documentation](https://github.com/mbostock/d3/wiki)

View file

@ -1,13 +0,0 @@
{
"extends": "eslint:recommended",
"plugins": ["angular"],
"env": {
"browser": true,
"jasmine": true
},
"globals": {
"angular": true,
"module": true,
"inject": true
}
}

View file

@ -0,0 +1,32 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});

View file

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-header></app-header>
<router-outlet></router-outlet>
`
})
export class AppComponent { }

View file

@ -0,0 +1,43 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { ApiService } from './services/api.service';
import { WindowService } from './services/window.service';
import { AppComponent } from './app.component';
import { HeaderComponent } from './components/header/header.component';
import { ProvidersComponent } from './components/providers/providers.component';
import { HealthComponent } from './components/health/health.component';
import { LineChartComponent } from './charts/line-chart/line-chart.component';
import { BarChartComponent } from './charts/bar-chart/bar-chart.component';
import { KeysPipe } from './pipes/keys.pipe';
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
ProvidersComponent,
HealthComponent,
LineChartComponent,
BarChartComponent,
KeysPipe
],
imports: [
BrowserModule,
CommonModule,
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: ProvidersComponent, pathMatch: 'full' },
{ path: 'status', component: HealthComponent }
])
],
providers: [
ApiService,
WindowService
],
bootstrap: [AppComponent]
})
export class AppModule { }

View file

@ -0,0 +1,7 @@
<div class="bar-chart" [class.is-hidden]="loading"></div>
<div class="loading-text" [class.is-hidden]="!loading">
<span>
<span>Loading, please wait...</span>
<img src="./assets/images/loader.svg" class="main-loader">
</span>
</div>

View file

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BarChartComponent } from './bar-chart.component';
describe('BarChartComponent', () => {
let component: BarChartComponent;
let fixture: ComponentFixture<BarChartComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BarChartComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BarChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,114 @@
import { Component, Input, OnInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
import { WindowService } from '../../services/window.service';
import {
min,
max,
easeLinear,
select,
axisLeft,
axisBottom,
scaleBand,
scaleLinear
} from 'd3';
@Component({
selector: 'app-bar-chart',
templateUrl: './bar-chart.component.html'
})
export class BarChartComponent implements OnInit, OnChanges {
@Input() value: any;
barChartEl: HTMLElement;
svg: any;
x: any;
y: any;
g: any;
bars: any;
width: number;
height: number;
margin = { top: 40, right: 40, bottom: 40, left: 40 };
loading: boolean;
data: any[];
constructor(public elementRef: ElementRef, public windowService: WindowService) {
this.loading = true;
}
ngOnInit() {
this.barChartEl = this.elementRef.nativeElement.querySelector('.bar-chart');
this.setup();
setTimeout(() => this.loading = false, 4000);
this.windowService.resize.subscribe(w => this.draw());
}
ngOnChanges(changes: SimpleChanges) {
if (!this.value || !this.svg) {
return;
}
this.data = this.value;
this.draw();
}
setup(): void {
this.width = 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).append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom);
this.g = this.svg.append('g')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
this.x = scaleBand().padding(0.05);
this.y = scaleLinear();
this.g.append('g')
.attr('class', 'axis axis--x');
this.g.append('g')
.attr('class', 'axis axis--y');
}
draw(): void {
this.x.domain(this.data.map((d: any) => d.code));
this.y.domain([0, max(this.data, (d: any) => d.count)]);
this.width = this.barChartEl.clientWidth - this.margin.left - this.margin.right;
this.height = this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
this.svg
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom);
this.x.rangeRound([0, this.width]);
this.y.rangeRound([this.height, 0]);
this.g.select('.axis--x')
.attr('transform', `translate(0, ${this.height})`)
.call(axisBottom(this.x));
this.g.select('.axis--y')
.call(axisLeft(this.y).tickSize(-this.width));
const bars = this.g.selectAll('.bar').data(this.data);
bars.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d: any) => d.code)
.attr('y', (d: any) => d.count)
.attr('width', this.x.bandwidth())
.attr('height', (d: any) => (this.height - this.y(d.count)) < 0 ? 0 : this.height - this.y(d.count));
bars.attr('x', (d: any) => this.x(d.code))
.attr('y', (d: any) => this.y(d.count))
.attr('width', this.x.bandwidth())
.attr('height', (d: any) => (this.height - this.y(d.count)) < 0 ? 0 : this.height - this.y(d.count));
bars.exit().remove();
}
}

View file

@ -0,0 +1,7 @@
<div class="line-chart" [class.is-hidden]="loading"></div>
<div class="loading-text" [class.is-hidden]="!loading">
<span>
<span>Loading, please wait...</span>
<img src="./assets/images/loader.svg" class="main-loader">
</span>
</div>

View file

@ -0,0 +1,162 @@
import { Component, Input, OnInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
import { WindowService } from '../../services/window.service';
import {
range,
scaleTime,
scaleLinear,
min,
max,
curveLinear,
line,
easeLinear,
select,
axisLeft,
axisBottom,
timeSecond,
timeFormat
} from 'd3';
@Component({
selector: 'app-line-chart',
templateUrl: 'line-chart.component.html'
})
export class LineChartComponent implements OnChanges, OnInit {
@Input() value: { count: number, date: string };
lineChartEl: HTMLElement;
svg: any;
g: any;
line: any;
path: any;
x: any;
y: any;
data: number[];
now: Date;
duration: number;
limit: number;
options: any;
xAxis: any;
yAxis: any;
height: number;
width: number;
margin = { top: 40, right: 40, bottom: 60, left: 60 };
loading = true;
constructor(private elementRef: ElementRef, public windowService: WindowService) { }
ngOnInit() {
this.lineChartEl = this.elementRef.nativeElement.querySelector('.line-chart');
this.limit = 40;
this.duration = 3000;
this.now = new Date(Date.now() - this.duration);
this.options = {
title: '',
color: '#3A84C5'
};
this.render();
setTimeout(() => this.loading = false, 4000);
this.windowService.resize.subscribe(w => {
if (this.svg) {
const el = this.lineChartEl.querySelector('svg');
el.parentNode.removeChild(el);
this.render();
}
});
}
render() {
this.width = this.lineChartEl.clientWidth - this.margin.left - this.margin.right;
this.height = this.lineChartEl.clientHeight - this.margin.top - this.margin.bottom;
this.svg = select(this.lineChartEl).append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
if (!this.data) {
this.data = range(this.limit).map(i => 0);
}
this.x = scaleTime().range([0, this.width]);
this.y = scaleLinear().range([this.height, 0]);
this.x.domain([<any>this.now - (this.limit - 2), <any>this.now - this.duration]);
this.y.domain([0, max(this.data, (d: any) => d)]);
this.line = line()
.x((d: any, i: number) => this.x(<any>this.now - (this.limit - 1 - i) * this.duration))
.y((d: any) => this.y(d))
.curve(curveLinear);
this.svg.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', this.width)
.attr('height', this.height);
this.xAxis = this.svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${this.height})`)
.call(axisBottom(this.x).tickSize(-this.height).ticks(timeSecond, 5).tickFormat(timeFormat('%H:%M:%S')));
this.yAxis = this.svg.append('g')
.attr('class', 'y axis')
.call(axisLeft(this.y).tickSize(-this.width));
this.path = this.svg.append('g')
.attr('clip-path', 'url(#clip)')
.append('path')
.data([this.data])
.attr('class', 'line');
}
ngOnChanges(changes: SimpleChanges) {
if (!this.value || !this.svg) {
return;
}
this.updateData(this.value.count);
}
updateData = (value: number) => {
this.data.push(value * 1000000);
this.now = new Date();
this.x.domain([<any>this.now - (this.limit - 2) * this.duration, <any>this.now - 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;
this.y.domain([minv, maxv]);
this.xAxis
.transition()
.duration(this.duration)
.ease(easeLinear)
.call(axisBottom(this.x).tickSize(-this.height).ticks(timeSecond, 5).tickFormat(timeFormat('%H:%M:%S')))
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
this.yAxis
.transition()
.duration(500)
.ease(easeLinear)
.call(axisLeft(this.y).tickSize(-this.width));
this.path
.transition()
.duration(0)
.attr('d', this.line(this.data))
.attr('transform', null)
.transition()
.duration(this.duration)
.ease(easeLinear)
.attr('transform', `translate(${this.x(<any>this.now - (this.limit - 1) * this.duration)})`);
this.data.shift();
}
}

View file

@ -0,0 +1,29 @@
<nav class="navbar is-fixed-top" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-menu">
<div class="navbar-brand">
<a class="navbar-item" routerLink="/">
<img src="./assets/images/traefik.logo.svg" alt="Traefik" class="navbar-logo">
</a>
</div>
<div class="navbar-start">
<div class="navbar-menu">
<a class="navbar-item" routerLink="/" routerLinkActive="is-active" [routerLinkActiveOptions]="{ exact: true }">
Providers
</a>
<a class="navbar-item" routerLink="/status" routerLinkActive="is-active">
Health
</a>
</div>
</div>
<div class="navbar-end is-hidden-mobile">
<a class="navbar-item" [href]="releaseLink" target="_blank">
{{ version }} / {{ codename }}
</a>
<a class="navbar-item" href="https://docs.traefik.io" target="_blank">
Documentation
</a>
</div>
</div>
</div>
</nav>

View file

@ -0,0 +1,23 @@
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../services/api.service';
@Component({
selector: 'app-header',
templateUrl: 'header.component.html'
})
export class HeaderComponent implements OnInit {
version: string;
codename: string;
releaseLink: string;
constructor(private apiService: ApiService) { }
ngOnInit() {
this.apiService.fetchVersion()
.subscribe(data => {
this.version = data.Version;
this.codename = data.Codename;
this.releaseLink = 'https://github.com/containous/traefik/tree/' + data.Version;
});
}
}

View file

@ -0,0 +1,104 @@
<div class="container">
<div class="content">
<div class="columns is-multiline">
<div class="column is-12">
<div class="content-item">
<div class="content-item-data">
<div class="columns">
<div class="column is-4">
<div class="item-data border-right">
<span class="data-grey">Total Response Time</span>
<span class="data-blue">{{ totalResponseTime }}</span>
</div>
</div>
<div class="column is-4">
<div class="item-data border-right">
<span class="data-grey">Total Code Count</span>
<span class="data-blue">{{ totalCodeCount }}</span>
</div>
</div>
<div class="column is-4">
<div class="item-data">
<span class="data-grey">Uptime Since <br/>{{ uptimeSince }}</span>
<span class="data-blue">{{ uptime }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="content-item">
<div class="content-item-data">
<div class="columns">
<div class="column is-4">
<div class="item-data border-right">
<span class="data-grey">Average Response Time</span>
<span class="data-blue">{{ averageResponseTime }}</span>
</div>
</div>
<div class="column is-4">
<div class="item-data border-right">
<span class="data-grey">Code Count</span>
<span class="data-blue">{{ codeCount }}</span>
</div>
</div>
<div class="column is-4">
<div class="item-data">
<span class="data-grey">PID</span>
<span class="data-blue">{{ pid }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="column is-12">
<div class="columns">
<div class="column is-6">
<div class="content-item">
<h2>Average Response Time (µs)</h2>
<app-line-chart [value]="chartValue"></app-line-chart>
</div>
</div>
<div class="column is-6">
<div class="content-item">
<h2>Total Status Code Count</h2>
<app-bar-chart [value]="statusCodeValue"></app-bar-chart>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="content" *ngIf="recentErrors">
<div class="content-item">
<h2>Recent HTTP Errors</h2>
<table class="table is-fullwidth">
<tr>
<td>Status</td>
<td>Request</td>
<td>Time</td>
</tr>
<tr *ngFor="let entry of recentErrors">
<td>
<span class="tag is-info">{{ entry.status_code }}</span>&nbsp;<span>{{ entry.status }}</span>
</td>
<td>
<span class="tag">{{ entry.method }}</span>&nbsp;<a>{{ entry.host }}{{ entry.path }}</a>
</td>
<td>
<span>{{ entry.time }}</span>
</td>
</tr>
<tr *ngIf="!recentErrors?.length">
<td colspan="3">
<p class="text-muted text-center">No entries</p>
</td>
</tr>
</table>
</div>
</div>
</div>

View file

@ -0,0 +1,57 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ApiService } from '../../services/api.service';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/timeInterval';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/map';
import { format, distanceInWordsStrict, subSeconds } from 'date-fns';
@Component({
selector: 'app-health',
templateUrl: 'health.component.html'
})
export class HealthComponent implements OnInit, OnDestroy {
sub: Subscription;
recentErrors: any;
pid: number;
uptime: string;
uptimeSince: string;
averageResponseTime: string;
totalResponseTime: string;
codeCount: number;
totalCodeCount: number;
chartValue: any;
statusCodeValue: any;
constructor(private apiService: ApiService) { }
ngOnInit() {
this.sub = Observable.timer(0, 3000)
.timeInterval()
.mergeMap(() => this.apiService.fetchHealthStatus())
.subscribe(data => {
if (data) {
this.recentErrors = data.recent_errors;
this.chartValue = { count: data.average_response_time_sec, 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.uptime = distanceInWordsStrict(subSeconds(new Date(), data.uptime_sec), new Date());
this.uptimeSince = format(subSeconds(new Date(), data.uptime_sec), 'MM/DD/YYYY HH:mm:ss');
this.totalResponseTime = data.total_response_time;
this.averageResponseTime = data.average_response_time;
this.codeCount = data.count;
this.totalCodeCount = data.total_count;
}
});
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
}

View file

@ -0,0 +1,599 @@
<div class="container">
<div class="content">
<div class="columns is-multiline" *ngIf="keys?.length">
<div class="column is-12">
<div class="search-container">
<span class="icon"><i class="fas fa-search"></i></span>
<input type="text" placeholder="Filter by name or id ..." [(ngModel)]="keyword" (ngModelChange)="filter()">
</div>
<div class="tabs" *ngIf="keys?.length">
<ul>
<li *ngFor="let provider of keys" [class.is-active]="tab === provider" (click)="tab = provider">
<a>{{ provider }}</a>
</li>
</ul>
</div>
<div *ngIf="keys?.length">
<div class="columns">
<!-- Frontends -->
<div class="column is-6">
<h2 class="subtitle"><span class="tag is-info">{{ providers[tab]?.frontends.length }}</span> Frontends</h2>
<div class="message" *ngFor="let p of providers[tab]?.frontends; let i = index;">
<div class="message-header">
<h2>
<div>
<i class="icon fas fa-globe"></i>
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">{{ p.id }}</span>
</div>
</div>
</div>
</div>
<div *ngIf="p.backend">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<a class="tags has-addons" [href]="'#' + p.backend">
<span class="tag is-light">Backend</span>
<span class="tag is-primary">{{ p.backend }}</span>
</a>
</div>
</div>
</div>
</h2>
</div>
<div class="message-body">
<div class="tabs is-fullwidth is-small is-boxed">
<ul>
<li [class.is-active]="p.section !== 'details'" (click)="p.section = 'main'"><a>Main</a></li>
<li [class.is-active]="p.section === 'details'" (click)="p.section = 'details'"><a>Details</a></li>
</ul>
</div>
<!-- Main -->
<div *ngIf="p.section !== 'details'">
<div *ngIf="p.routes && p.routes.length">
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr>
<td>Route Rule</td>
</tr>
<tr *ngFor="let route of p.routes; let ri = index;">
<td><code class="has-text-grey" title="{{ route.title }}">{{ route.rule }}</code></td>
</tr>
</tbody>
</table>
</div>
<div *ngIf="p.entryPoints && p.entryPoints.length">
<hr>
<div class="columns">
<div class="column is-3">
<h2>Entry Points</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags">
<span class="tag is-info" *ngFor="let ep of p.entryPoints; let ri = index;">{{ ep }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Details -->
<div *ngIf="p.section === 'details'">
<div>
<div class="columns">
<div class="column is-3">
<h2>Misc.</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Priority</span>
<span class="tag is-info">{{ p.priority }}</span>
</div>
</div>
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Host Header</span>
<span class="tag is-info">{{ p.passHostHeader }}</span>
</div>
</div>
<div class="control" *ngIf="p.passTLSCert">
<div class="tags has-addons">
<span class="tag is-light">TLS Cert</span>
<span class="tag is-info">{{ p.passTLSCert }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.redirect">
<hr>
<div class="columns">
<div class="column is-3">
<h2>Redirect</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline" *ngIf="p.redirect.entryPoint">
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">{{ p.redirect.permanent?"Permanent":"Temporary" }}</span>
<span class="tag is-light">to</span>
<span class="tag is-info">{{ p.redirect.entryPoint }}</span>
</div>
</div>
</div>
<div *ngIf="!p.redirect.entryPoint">
<div class="padding-5-10">
<span class="tag is-info">{{ p.redirect.permanent?"Permanent":"Temporary" }}</span>
</div>
<div class="padding-5-10">
<span class="tag is-info">From</span>
<code class="has-text-grey">{{ p.redirect.regex }}</code>
</div>
<div class="padding-5-10">
<span class="tag is-info">To</span>
<code class="has-text-grey">{{ p.redirect.replacement }}</code>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.basicAuth && p.basicAuth.length">
<hr/>
<h2>Basic Authentication</h2>
<div class="tags padding-5-10">
<span class="tag is-info" *ngFor="let auth of p.basicAuth; let ri = index;">{{ auth }}</span>
</div>
</div>
<div *ngIf="p.errors">
<hr/>
<h2>Error Pages</h2>
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr>
<td>Backend</td>
<td>Query</td>
<td>Status</td>
</tr>
<tr *ngFor="let key of p.errors | keys">
<td><span class="has-text-grey-light">{{ p.errors[key].backend }}</span></td>
<td><span class="has-text-grey">{{ p.errors[key].query }}</span></td>
<td>
<span class="tag is-light" *ngFor="let state of p.errors[key].status">{{ state }}</span>
</td>
</tr>
</tbody>
</table>
</div>
<div *ngIf="p.whiteList">
<hr/>
<div class="columns is-gapless is-multiline is-mobile">
<div class="column is-half">
<h2>Whitelist</h2>
</div>
<div class="column is-half">
<div class="field">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">useXForwardedFor</span>
<span class="tag is-info">{{ p.whiteList.useXForwardedFor }}</span>
</div>
</div>
</div>
</div>
<div class="column">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags">
<span class="tag is-info" *ngFor="let wlRange of p.whiteList.sourceRange; let ri = index;">{{ wlRange }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.headers">
<hr/>
<h2>Headers</h2>
<div class="columns is-multiline">
<div class="column is-12" *ngIf="p.headers.customRequestHeaders">
<h2>Custom Request Headers</h2>
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr *ngFor="let key of p.headers.customRequestHeaders | keys">
<td><span class="has-text-grey-light">{{ key }}</span></td>
<td><span class="has-text-grey">{{ p.headers.customRequestHeaders[key] }}</span></td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12" *ngIf="p.headers.customResponseHeaders">
<h2>Custom Response Headers</h2>
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr *ngFor="let key of p.headers.customResponseHeaders | keys">
<td><span class="has-text-grey-light">{{ key }}</span></td>
<td><span class="has-text-grey">{{ p.headers.customResponseHeaders[key] }}</span></td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<h2>Secure</h2>
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr *ngIf="p.headers.browserXssFilter">
<td><span class="has-text-grey">Browser XSS Filter</span></td>
<td><span class="has-text-grey">{{ p.headers.browserXssFilter }}</span></td>
</tr>
<tr *ngIf="p.headers.contentSecurityPolicy">
<td><span class="has-text-grey">Content Security Policy</span></td>
<td><span class="has-text-grey">{{ p.headers.contentSecurityPolicy }}</span></td>
</tr>
<tr *ngIf="p.headers.contentTypeNoSniff">
<td><span class="has-text-grey">Content Type (No sniff)</span></td>
<td><span class="has-text-grey">{{ p.headers.contentTypeNoSniff }}</span></td>
</tr>
<tr *ngIf="p.headers.customFrameOptionsValue">
<td><span class="has-text-grey">Custom Frame Options Value</span></td>
<td><span class="has-text-grey">{{ p.headers.customFrameOptionsValue }}</span></td>
</tr>
<tr *ngIf="p.headers.forceSTSHeader">
<td><span class="has-text-grey">Force STS Header</span></td>
<td><span class="has-text-grey">{{ p.headers.forceSTSHeader }}</span></td>
</tr>
<tr *ngIf="p.headers.frameDeny">
<td><span class="has-text-grey">Frame Deny</span></td>
<td><span class="has-text-grey">{{ p.headers.frameDeny }}</span></td>
</tr>
<tr *ngIf="p.headers.isDevelopment">
<td><span class="has-text-grey">Is Development</span></td>
<td><span class="has-text-grey">{{ p.headers.isDevelopment }}</span></td>
</tr>
<tr *ngIf="p.headers.publicKey">
<td><span class="has-text-grey">Public Key</span></td>
<td><span class="has-text-grey">{{ p.headers.publicKey }}</span></td>
</tr>
<tr *ngIf="p.headers.referrerPolicy">
<td><span class="has-text-grey">Referrer Policy</span></td>
<td><span class="has-text-grey">{{ p.headers.referrerPolicy }}</span></td>
</tr>
<tr *ngIf="p.headers.sslHost">
<td><span class="has-text-grey">SSL Host</span></td>
<td><span class="has-text-grey">{{ p.headers.sslHost }}</span></td>
</tr>
<tr *ngIf="p.headers.sslRedirect">
<td><span class="has-text-grey">SSL Redirect</span></td>
<td><span class="has-text-grey">{{ p.headers.sslRedirect }}</span></td>
</tr>
<tr *ngIf="p.headers.sslTemporaryRedirect">
<td><span class="has-text-grey">SSL Temporary Redirect</span></td>
<td><span class="has-text-grey">{{ p.headers.sslTemporaryRedirect }}</span></td>
</tr>
<tr *ngIf="p.headers.stsIncludeSubdomains">
<td><span class="has-text-grey">STS Include Subdomains</span></td>
<td><span class="has-text-grey">{{ p.headers.stsIncludeSubdomains }}</span></td>
</tr>
<tr *ngIf="p.headers.stsPreload">
<td><span class="has-text-grey">STS Preload</span></td>
<td><span class="has-text-grey">{{ p.headers.stsPreload }}</span></td>
</tr>
<tr *ngIf="p.headers.stsSeconds">
<td><span class="has-text-grey">STS Seconds</span></td>
<td><span class="has-text-grey">{{ p.headers.stsSeconds }}</span></td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12" *ngIf="p.headers.allowedHosts">
<h2>Allowed Hosts</h2>
<div class="tags-list">
<span class="tag is-light" *ngFor="let host of p.headers.allowedHosts">{{ host }}</span>
</div>
</div>
<div class="column is-12" *ngIf="p.headers.sslProxyHeaders">
<h2>SSL Proxy Headers</h2>
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr *ngFor="let key of p.headers.sslProxyHeaders | keys">
<td><span class="has-text-grey-light">{{ key }}</span></td>
<td><span class="has-text-grey">{{ p.headers.sslProxyHeaders[key] }}</span></td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12" *ngIf="p.headers.hostsProxyHeaders">
<h2>Hosts Proxy Headers</h2>
<div class="tags-list">
<span class="tag is-light" *ngFor="let h of p.headers.hostsProxyHeaders">{{ h }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Backends -->
<div class="column is-6">
<h2 class="subtitle"><span class="tag is-primary">{{ providers[tab]?.backends.length }}</span> Backends</h2>
<div class="message" *ngFor="let p of providers[tab]?.backends; let i = index;">
<div class="message-header">
<h2 [id]="p.id">
<div>
<i class="icon fas fa-server"></i>
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-primary">{{ p.id }}</span>
</div>
</div>
</div>
</div>
</h2>
</div>
<div class="message-body">
<div class="tabs is-fullwidth is-small is-boxed">
<ul>
<li [class.is-active]="p.section !== 'details'" (click)="p.section = 'main'"><a>Main</a></li>
<li [class.is-active]="p.section === 'details'" (click)="p.section = 'details'"><a>Details</a></li>
</ul>
</div>
<!-- Main -->
<div *ngIf="p.section !== 'details'">
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr>
<td>Server</td>
<td>Weight</td>
</tr>
<tr *ngFor="let server of p.servers; let ri = index;">
<td><a href="{{ server.url }}" title="{{ server.title }}">{{ server.url }}</a></td>
<td><span class="has-text-grey">{{ server.weight }}</span></td>
</tr>
</tbody>
</table>
</div>
<!-- Details -->
<div *ngIf="p.section === 'details'">
<div *ngIf="p.loadBalancer">
<div class="columns">
<div class="column is-3">
<h2>Load Balancer</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Method</span>
<span class="tag is-info">{{ p.loadBalancer.method }}</span>
</div>
</div>
<div class="control">
<div class="tags has-addons" *ngIf="p.loadBalancer.stickiness || p.loadBalancer.sticky">
<span class="tag is-light">Stickiness</span>
<span class="tag is-info">true</span>
</div>
</div>
<div class="control" *ngIf="p.loadBalancer.stickiness">
<div class="tags has-addons">
<span class="tag is-light">Cookie Name</span>
<span class="tag is-info">{{ p.loadBalancer.stickiness.cookieName }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.maxConn">
<hr/>
<div class="columns">
<div class="column is-3">
<h2>Max Connections</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Amount</span>
<span class="tag is-info">{{ p.maxConn.amount }}</span>
</div>
</div>
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Extractor Function</span>
<span class="tag is-info">{{ p.maxConn.extractorFunc }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.circuitBreaker">
<hr/>
<div class="columns">
<div class="column is-3">
<h2>Circuit Breaker</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Expression</span>
<span class="tag is-info">{{ p.circuitBreaker.expression }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.healthCheck">
<hr/>
<div class="columns">
<div class="column is-3">
<h2>Health Check</h2>
</div>
<div class="column is-9">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Path</span>
<span class="tag is-info">{{ p.healthCheck.path }}</span>
</div>
</div>
<div class="control" *ngIf="p.healthCheck.port">
<div class="tags has-addons">
<span class="tag is-light">Port</span>
<span class="tag is-info">{{ p.healthCheck.port }}</span>
</div>
</div>
<div class="control" *ngIf="p.healthCheck.interval">
<div class="tags has-addons">
<span class="tag is-light">Interval</span>
<span class="tag is-info">{{ p.healthCheck.interval }}</span>
</div>
</div>
<div class="control" *ngIf="p.healthCheck.hostname">
<div class="tags has-addons">
<span class="tag is-light">Hostname</span>
<span class="tag is-info">{{ p.healthCheck.hostname }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="p.buffering">
<hr>
<div class="columns list-title">
<div class="column is-12">
<h2>Buffering</h2>
</div>
</div>
<div class="list-item">
<div class="columns">
<div class="column is-4">
<span>Request Body Bytes</span>
</div>
<div class="column is-4">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Max</span>
<span class="tag is-info">{{ p.buffering.maxRequestBodyBytes }}</span>
</div>
</div>
</div>
</div>
<div class="column is-4">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Men</span>
<span class="tag is-info">{{ p.buffering.memRequestBodyBytes }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="list-item">
<div class="columns">
<div class="column is-4">
<span>Response Body Bytes</span>
</div>
<div class="column is-4">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Max</span>
<span class="tag is-info">{{ p.buffering.maxResponseBodyBytes }}</span>
</div>
</div>
</div>
</div>
<div class="column is-4">
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-light">Men</span>
<span class="tag is-info">{{ p.buffering.memResponseBodyBytes }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="list-item">
<div class="columns">
<div class="column is-4">
<span>Retry Expression</span>
</div>
<div class="column is-8">
<span class="tag is-info">{{ p.buffering.retryExpression }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="columns" *ngIf="!keys || !keys.length">
<div class="column is-12">
<div class="notification">
No providers found.
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,59 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ApiService } from '../../services/api.service';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
import * as _ from "lodash";
@Component({
selector: 'app-providers',
templateUrl: 'providers.component.html'
})
export class ProvidersComponent implements OnInit, OnDestroy {
sub: Subscription;
keys: string[];
data: any;
previousData: any;
providers: any;
tab: string;
keyword: string;
constructor(private apiService: ApiService) { }
ngOnInit() {
this.keyword = '';
this.sub = Observable.timer(0, 2000)
.timeInterval()
.mergeMap(() => this.apiService.fetchProviders())
.subscribe(data => {
if (!_.isEqual(this.previousData, data)) {
this.previousData = _.cloneDeep(data);
this.data = data;
this.providers = data;
this.keys = Object.keys(this.providers);
this.tab = this.keys[0];
}
});
}
filter(): void {
const keyword = this.keyword.toLowerCase();
this.providers = Object.keys(this.data)
.filter(value => value !== 'acme' && value !== 'ACME')
.reduce((acc, curr) => {
return Object.assign(acc, {
[curr]: {
backends: this.data[curr].backends.filter(d => d.id.toLowerCase().includes(keyword)),
frontends: this.data[curr].frontends.filter(d => {
return d.id.toLowerCase().includes(keyword) || d.backend.toLowerCase().includes(keyword);
})
}
});
}, {});
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
}

View file

@ -1,14 +0,0 @@
'use strict';
var angular = require('angular');
var traefikCoreHealth = 'traefik.core.health';
module.exports = traefikCoreHealth;
angular
.module(traefikCoreHealth, ['ngResource'])
.factory('Health', Health);
/** @ngInject */
function Health($resource) {
return $resource('../health');
}

View file

@ -1,52 +0,0 @@
'use strict';
var angular = require('angular');
var traefikCoreProvider = 'traefik.core.provider';
module.exports = traefikCoreProvider;
angular
.module(traefikCoreProvider, ['ngResource'])
.factory('Providers', Providers);
/** @ngInject */
function Providers($resource, $q) {
const resourceProvider = $resource('../api/providers');
return {
get: function () {
return $q((resolve, reject) => {
resourceProvider.get()
.$promise
.then((rawProviders) => {
delete rawProviders.acme;
delete rawProviders.ACME;
for (let providerName in rawProviders) {
if (rawProviders.hasOwnProperty(providerName)) {
if (!providerName.startsWith('$')) {
// BackEnds mapping
let bckends = rawProviders[providerName].backends || {};
rawProviders[providerName].backends = Object.keys(bckends)
.map(key => {
const goodBackend = bckends[key];
goodBackend.backendId = key;
return goodBackend;
});
// FrontEnds mapping
let frtends = rawProviders[providerName].frontends || {};
rawProviders[providerName].frontends = Object.keys(frtends)
.map(key => {
const goodFrontend = frtends[key];
goodFrontend.frontendId = key;
return goodFrontend;
});
}
}
}
resolve(rawProviders);
})
.catch(reject);
});
}
};
}

View file

@ -1,14 +0,0 @@
'use strict';
var angular = require('angular');
var traefikCoreVersion = 'traefik.core.version';
module.exports = traefikCoreVersion;
angular
.module(traefikCoreVersion, ['ngResource'])
.factory('Version', Version);
/** @ngInject */
function Version($resource) {
return $resource('../api/version');
}

View file

@ -1,34 +0,0 @@
/**
* If you want to override some bootstrap variables, you have to change values here.
* The list of variables are listed here bower_components/bootstrap-sass/assets/stylesheets/bootstrap/_variables.scss
*/
$navbar-inverse-link-color: #5AADBB;
/**
* Do not remove the comments below. It's the markers used by wiredep to inject
* sass dependencies when defined in the bower.json of your dependencies
*/
// bower:scss
// endbower
.browsehappy {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
.thumbnail {
height: 200px;
img.pull-right {
width: 50px;
}
}
/**
* Do not remove the comments below. It's the markers used by gulp-inject to inject
* all your sass files automatically
*/
// injector
// endinjector

View file

@ -0,0 +1,8 @@
import { PipeTransform, Pipe } from '@angular/core';
@Pipe({ name: 'keys' })
export class KeysPipe implements PipeTransform {
transform(value, args: string[]): any {
return Object.keys(value);
}
}

View file

@ -1,238 +0,0 @@
'use strict';
var d3 = require('d3'),
moment = require('moment'),
HttpStatus = require('http-status-codes');
/** @ngInject */
function HealthController($scope, $interval, $log, Health) {
var vm = this;
vm.graph = {
averageResponseTime: {},
totalStatusCodeCount: {}
};
vm.graph.totalStatusCodeCount.options = {
"chart": {
type: 'discreteBarChart',
tooltip: {
contentGenerator: function (e) {
var d = e.data;
return d.label + " " + d.text;
}
},
height: 200,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 55
},
x: function (d) {
return d.label;
},
y: function (d) {
return d.value;
},
showValues: true,
valueFormat: function (d) {
return d3.format('d')(d);
},
yAxis: {
axisLabelDistance: 30,
tickFormat: d3.format('d')
}
},
"title": {
"enable": true,
"text": "Total Status Code Count",
"css": {
"textAlign": "center"
}
}
};
vm.graph.totalStatusCodeCount.data = [
{
key: "Total Status Code Count",
values: [
{
"label": "200",
"value": 0
}
]
}
];
/**
* Update Total Status Code Count graph
*
* @param {Object} totalStatusCodeCount Object from API
*/
function updateTotalStatusCodeCount(totalStatusCodeCount) {
// extract values
vm.graph.totalStatusCodeCount.data[0].values = [];
for (var code in totalStatusCodeCount) {
if (totalStatusCodeCount.hasOwnProperty(code)) {
var statusCodeText = "";
try {
statusCodeText = HttpStatus.getStatusText(code);
} catch (e) {
// HttpStatus.getStatusText throws error on unknown codes
statusCodeText = "Unknown status code";
}
vm.graph.totalStatusCodeCount.data[0].values.push({
label: code,
value: totalStatusCodeCount[code],
text: statusCodeText
});
}
}
// Update Total Status Code Count graph render
if (vm.graph.totalStatusCodeCount.api) {
vm.graph.totalStatusCodeCount.api.update();
} else {
$log.error('fail');
}
}
vm.graph.averageResponseTime.options = {
chart: {
type: 'lineChart',
height: 200,
margin: {
top: 20,
right: 40,
bottom: 40,
left: 55
},
x: function (d) {
return d.x;
},
y: function (d) {
return d.y;
},
useInteractiveGuideline: true,
xAxis: {
tickFormat: function (d) {
return d3.time.format('%X')(new Date(d));
}
},
yAxis: {
tickFormat: function (d) {
return d3.format(',.1f')(d);
}
},
forceY: [0., 1.], // This prevents the chart from showing -1 on Oy when all the input data points
// have y = 0. It won't disable the automatic adjustment of the max value.
duration: 0 // Bug: Markers will not be drawn if you set this to some other value...
},
"title": {
"enable": true,
"text": "Average response time",
"css": {
"textAlign": "center"
}
}
};
var initialPoint = {
x: Date.now() - 3000,
y: 0
};
vm.graph.averageResponseTime.data = [
{
values: [initialPoint],
key: 'Average response time (ms)',
type: 'line',
color: '#2ca02c'
}
];
/**
* Update average response time graph
*
* @param {Number} x Coordinate X
* @param {Number} y Coordinate Y
*/
function updateAverageResponseTimeGraph(x, y) {
// x multiply 1000 by because unix time is in seconds and JS Date are in milliseconds
var data = {
x: x * 1000,
y: y * 1000
};
vm.graph.averageResponseTime.data[0].values.push(data);
// limit graph entries
if (vm.graph.averageResponseTime.data[0].values.length > 100) {
vm.graph.averageResponseTime.data[0].values.shift();
}
// Update Average Response Time graph render
if (vm.graph.averageResponseTime.api) {
vm.graph.averageResponseTime.api.update();
}
}
/**
* Format the timestamp as "x seconds ago", etc.
*
* @param {String} t Timestamp returned from the API
*/
function formatTimestamp(t) {
return moment(t, "YYYY-MM-DDTHH:mm:ssZ").fromNow();
}
/**
* Load all graph's datas
*
* @param {Object} health Health data from server
*/
function loadData(health) {
// Load datas and update Average Response Time graph render
updateAverageResponseTimeGraph(health.unixtime, health.average_response_time_sec);
// Load datas and update Total Status Code Count graph render
updateTotalStatusCodeCount(health.total_status_code_count);
// Format the timestamps
if (health.recent_errors) {
angular.forEach(health.recent_errors, function(i) {
i.time_formatted = formatTimestamp(i.time);
});
}
// set data's view
vm.health = health;
}
/**
* Action when load datas failed
*
* @param {Object} error Error state object
*/
function erroData(error) {
vm.health = {};
$log.error(error);
}
// first load
Health.get(loadData, erroData);
// Auto refresh data
var intervalId = $interval(function () {
Health.get(loadData, erroData);
}, 3000);
// Stop auto refresh when page change
$scope.$on('$destroy', function () {
$interval.cancel(intervalId);
});
}
module.exports = HealthController;

View file

@ -1,73 +0,0 @@
<div>
<h1 class="text-danger">
<span class="glyphicon glyphicon-heart" aria-hidden="true"></span> Health
</h1>
<div class="row">
<div class="col-md-6">
<div>
<nvd3 options="healthCtrl.graph.averageResponseTime.options" data="healthCtrl.graph.averageResponseTime.data" api="healthCtrl.graph.averageResponseTime.api"></nvd3>
</div>
<ul class="list-group">
<li class="list-group-item">
<span>Total response time :</span><span class="badge">{{healthCtrl.health.total_response_time}}</span>
</li>
</ul>
<ul class="list-group">
<li class="list-group-item">
<span>PID :</span><span class="badge">{{healthCtrl.health.pid}}</span>
</li>
<li class="list-group-item">
<span>Uptime :</span><span class="badge">{{healthCtrl.health.uptime}}</span>
</li>
</ul>
</div>
<div class="col-md-6">
<div>
<nvd3 options="healthCtrl.graph.totalStatusCodeCount.options" data="healthCtrl.graph.totalStatusCodeCount.data" api="healthCtrl.graph.totalStatusCodeCount.api"></nvd3>
</div>
<ul class="list-group">
<li class="list-group-item">
<span>Total count :</span><span class="badge">{{healthCtrl.health.total_count}}</span>
</li>
<li class="list-group-item">
<span>Count :</span><span class="badge">{{healthCtrl.health.count}}</span>
</li>
</ul>
</div>
</div>
<div ng-if="healthCtrl.health.recent_errors">
<h3>Recent HTTP Errors</h3>
<table class="table table-striped table-bordered">
<tr>
<td>Status</td>
<td>Request</td>
<td>Time</td>
</tr>
<tr ng-repeat="entry in healthCtrl.health.recent_errors"
ng-class="{'text-danger': entry.status_code >= 500}">
<td>{{ entry.status_code }} &mdash; {{ entry.status }}</td>
<td>
<span class="badge">{{ entry.method }}</span>
&nbsp;
{{ entry.host }}{{ entry.path }}
</td>
<td>
<span title="{{ entry.time }}">
{{ entry.time_formatted }}
</span>
</td>
</tr>
<tr ng-if="healthCtrl.health.recent_errors.length == 0">
<td colspan="3">
<p class="text-muted text-center">No entries</p>
</td>
</tr>
</table>
</div>
</div>

View file

@ -1,24 +0,0 @@
'use strict';
var angular = require('angular');
var traefikCoreHealth = require('../../core/health.resource');
var HealthController = require('./health.controller');
var traefikSectionHealth = 'traefik.section.health';
module.exports = traefikSectionHealth;
angular
.module(traefikSectionHealth, [traefikCoreHealth])
.controller('HealthController', HealthController)
.config(config);
/** @ngInject */
function config($stateProvider) {
$stateProvider.state('health', {
url: '/health',
template: require('./health.html'),
controller: 'HealthController',
controllerAs: 'healthCtrl'
});
}

View file

@ -1,20 +0,0 @@
'use strict';
function backendMonitor() {
return {
restrict: 'EA',
template: require('./backend-monitor.html'),
controller: BackendMonitorController,
controllerAs: 'backendCtrl',
bindToController: true,
scope: {
backend: '='
}
};
}
function BackendMonitorController() {
// Nothing
}
module.exports = backendMonitor;

View file

@ -1,23 +0,0 @@
<div class="panel panel-success">
<div class="panel-heading">
<strong><span class="glyphicon glyphicon-tasks" aria-hidden="true"></span> {{backendCtrl.backend.backendId}}</strong>
</div>
<div class="panel-body">
<table class="panel-table__servers table table-striped table-hover">
<tr>
<td><em>Server</em></td>
<td><em>URL</em></td>
<td><em>Weight</em></td>
</tr>
<tr data-ng-repeat="(serverId, server) in backendCtrl.backend.servers">
<td>{{serverId}}</td>
<td><code><a data-ng-href="{{server.url}}">{{server.url}}</a></code></td>
<td>{{server.weight}}</td>
</tr>
</table>
</div>
<div class="panel-footer" data-ng-show="backendCtrl.backend.loadBalancer || backendCtrl.backend.circuitBreaker">
<span data-ng-show="backendCtrl.backend.loadBalancer" class="label label-success">Load Balancer: {{backendCtrl.backend.loadBalancer.method}}</span>
<span data-ng-show="backendCtrl.backend.circuitBreaker" class="label label-success">Circuit Breaker: {{backendCtrl.backend.circuitBreaker.expression}}</span>
</div>
</div>

View file

@ -1,10 +0,0 @@
'use strict';
var angular = require('angular');
var backendMonitor = require('./backend-monitor.directive');
var traefikBackendMonitor = 'traefik.section.providers.backend-monitor';
module.exports = traefikBackendMonitor;
angular
.module(traefikBackendMonitor, [])
.directive('backendMonitor', backendMonitor);

View file

@ -1,20 +0,0 @@
'use strict';
function frontendMonitor() {
return {
restrict: 'EA',
template: require('./frontend-monitor.html'),
controller: FrontendMonitorController,
controllerAs: 'frontendCtrl',
bindToController: true,
scope: {
frontend: '='
}
};
}
function FrontendMonitorController() {
// Nothing
}
module.exports = frontendMonitor;

View file

@ -1,29 +0,0 @@
<div class="panel panel-warning">
<div class="panel-heading">
<strong><span class="glyphicon glyphicon-globe" aria-hidden="true"></span> {{frontendCtrl.frontend.frontendId}}</strong>
</div>
<div class="panel-body">
<table class="panel-table__routes table table-striped table-hover">
<tr>
<td><em>Route</em></td>
<td><em>Rule</em></td>
</tr>
<tr data-ng-repeat="(routeId, route) in frontendCtrl.frontend.routes">
<td>{{routeId}}</td>
<td><code>{{route.rule}}</code></td>
</tr>
</table>
</div>
<div data-bg-show="frontendCtrl.frontend.backend" class="panel-footer">
<span data-ng-repeat="entryPoint in frontendCtrl.frontend.entryPoints">
<span class="label label-primary">{{entryPoint}}</span><span data-ng-hide="$last">&nbsp;</span>
</span>
<span data-ng-show="frontendCtrl.frontend.redirect" class="label label-success">Redirect to {{frontendCtrl.frontend.redirect}}</span>
<span class="label label-warning" role="button" data-toggle="collapse" href="#{{frontendCtrl.frontend.backend}}" aria-expanded="false">Backend:{{frontendCtrl.frontend.backend}}</span>
<span data-ng-show="frontendCtrl.frontend.passHostHeader" class="label label-warning">PassHostHeader</span>
<span data-ng-repeat="whitelistSourceRange in frontendCtrl.frontend.whitelistSourceRange">
<span class="label label-warning">Whitelist {{ whitelistSourceRange }}</span>
</span>
<span data-ng-show="frontendCtrl.frontend.priority" class="label label-warning">Priority:{{frontendCtrl.frontend.priority}}</span>
</div>
</div>

View file

@ -1,10 +0,0 @@
'use strict';
var angular = require('angular');
var frontendMonitor = require('./frontend-monitor.directive');
var traefikFrontendMonitor = 'traefik.section.providers.frontend-monitor';
module.exports = traefikFrontendMonitor;
angular
.module(traefikFrontendMonitor, [])
.directive('frontendMonitor', frontendMonitor);

View file

@ -1,33 +0,0 @@
'use strict';
var _ = require('lodash');
/** @ngInject */
function ProvidersController($scope, $interval, $log, Providers) {
const vm = this;
function loadProviders() {
Providers
.get()
.then(providers => {
if (!_.isEqual(vm.previousProviders, providers)) {
vm.providers = providers;
vm.previousProviders = _.cloneDeep(providers);
}
})
.catch(error => {
vm.providers = {};
$log.error(error);
});
}
loadProviders();
const intervalId = $interval(loadProviders, 2000);
$scope.$on('$destroy', function () {
$interval.cancel(intervalId);
});
}
module.exports = ProvidersController;

View file

@ -1,22 +0,0 @@
<div>
<div><input type="text" data-ng-model="providersCtrl.providerFilter" placeholder="Filter" class="form-control"></div>
<br>
<uib-tabset>
<uib-tab data-ng-repeat="(providerId, provider) in providersCtrl.providers" heading="{{providerId}}">
<div class="row tabset-row__providers">
<div class="col-md-6">
<div data-ng-repeat="frontend in provider.frontends | filter: providersCtrl.providerFilter ">
<frontend-monitor data-provider-id="providerId" data-frontend="frontend"></frontend-monitor>
</div>
</div>
<div class="col-md-6">
<div data-ng-repeat="backend in provider.backends | filter: providersCtrl.providerFilter">
<backend-monitor data-provider-id="providerId" data-backend="backend"></backend-monitor>
</div>
</div>
</div>
</uib-tab>
</uib-tabset>
</div>

View file

@ -1,30 +0,0 @@
'use strict';
var angular = require('angular');
var traefikCoreProvider = require('../../core/providers.resource');
var ProvidersController = require('./providers.controller');
var traefikBackendMonitor = require('./backend-monitor/backend-monitor.module');
var traefikFrontendMonitor = require('./frontend-monitor/frontend-monitor.module');
var traefikSectionProviders = 'traefik.section.providers';
module.exports = traefikSectionProviders;
angular
.module(traefikSectionProviders, [
traefikCoreProvider,
traefikBackendMonitor,
traefikFrontendMonitor
])
.config(config)
.controller('ProvidersController', ProvidersController);
/** @ngInject */
function config($stateProvider) {
$stateProvider.state('provider', {
url: '/',
template: require('./providers.html'),
controller: 'ProvidersController',
controllerAs: 'providersCtrl'
});
}

View file

@ -1,24 +0,0 @@
'use strict';
var angular = require('angular');
require('nvd3');
var ndv3 = require('angular-nvd3');
var traefikSectionHealth = require('./health/health.module');
var traefikSectionProviders = require('./providers/providers.module');
var traefikSection = 'traefik.section';
module.exports = traefikSection;
angular
.module(traefikSection, [
'ui.router',
'ui.bootstrap',
ndv3,
traefikSectionProviders,
traefikSectionHealth
])
.config(config);
/** @ngInject */
function config($urlRouterProvider) {
$urlRouterProvider.otherwise('/');
}

View file

@ -0,0 +1,88 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/empty';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/retry';
export interface ProviderType {
[provider: string]: {
backends: any;
frontends: any;
};
}
@Injectable()
export class ApiService {
headers: HttpHeaders;
constructor(private http: HttpClient) {
this.headers = new HttpHeaders({
'Access-Control-Allow-Origin': '*'
});
}
fetchVersion(): Observable<any> {
return this.http.get(`/api/version`, { headers: this.headers })
.retry(4)
.catch((err: HttpErrorResponse) => {
console.error(`[version] returned code ${err.status}, body was: ${err.error}`);
return Observable.empty<any>();
});
}
fetchHealthStatus(): Observable<any> {
return this.http.get(`/health`, { headers: this.headers })
.retry(2)
.catch((err: HttpErrorResponse) => {
console.error(`[health] returned code ${err.status}, body was: ${err.error}`);
return Observable.empty<any>();
});
}
fetchProviders(): Observable<any> {
return this.http.get(`/api/providers`, { headers: this.headers })
.retry(2)
.catch((err: HttpErrorResponse) => {
console.error(`[providers] returned code ${err.status}, body was: ${err.error}`);
return Observable.of<any>({});
})
.map(this.parseProviders);
}
parseProviders(data: any): ProviderType {
return Object.keys(data)
.filter(value => value !== 'acme' && value !== 'ACME')
.reduce((acc, curr) => {
acc[curr] = {
backends: Object.keys(data[curr].backends || {}).map(key => {
data[curr].backends[key].id = key;
data[curr].backends[key].servers = Object.keys(data[curr].backends[key].servers || {}).map(server => {
return {
title: server,
url: data[curr].backends[key].servers[server].url,
weight: data[curr].backends[key].servers[server].weight
};
});
return data[curr].backends[key];
}),
frontends: Object.keys(data[curr].frontends || {}).map(key => {
data[curr].frontends[key].id = key;
data[curr].frontends[key].routes = Object.keys(data[curr].frontends[key].routes || {}).map(route => {
return {
title: route,
rule: data[curr].frontends[key].routes[route].rule
};
});
return data[curr].frontends[key];
}),
};
return acc;
}, {});
}
}

View file

@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { EventManager } from '@angular/platform-browser';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class WindowService {
resize: Subject<any>;
constructor(private eventManager: EventManager) {
this.resize = new Subject();
this.eventManager.addGlobalEventListener('window', 'resize', this.onResize);
}
onResize = (event: UIEvent) => {
this.resize.next(event.target);
}
}

View file

@ -1,34 +0,0 @@
@font-face {
font-family: 'charterregular';
src: url('./assets/fonts/charter_regular-webfont.eot');
src: url('./assets/fonts/charter_regular-webfont.eot?#iefix') format('embedded-opentype'),
url('./assets/fonts/charter_regular-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
.traefik-blue {
color: #00B1FF;
}
.traefik-text {
font-family: 'charterregular', Arial, sans-serif;
}
.panel-body .panel-table__servers,
.panel-body .panel-table__routes {
margin-bottom: 0;
}
.tabset-row__providers {
margin-top: 3rem;
}
table {
table-layout: fixed;
}
td, th {
word-wrap: break-word;
}

View file

@ -1,10 +0,0 @@
'use strict';
/** @ngInject */
function VersionController($scope, Version) {
Version.get(function (version) {
$scope.version = version;
});
}
module.exports = VersionController;

View file

@ -1,11 +0,0 @@
'use strict';
var angular = require('angular');
var traefikCoreVersion = require('../core/version.resource');
var VersionController = require('./version.controller');
var traefikVersion = 'traefik.version';
module.exports = traefikVersion;
angular
.module(traefikVersion, [traefikCoreVersion])
.controller('VersionController', VersionController);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,30 @@
<svg version="1.1" id="L5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
<circle fill="#0294FF" stroke="none" cx="6" cy="50" r="6">
<animateTransform
attributeName="transform"
dur="1s"
type="translate"
values="0 15 ; 0 -15; 0 15"
repeatCount="indefinite"
begin="0.1"/>
</circle>
<circle fill="#0294FF" stroke="none" cx="30" cy="50" r="6">
<animateTransform
attributeName="transform"
dur="1s"
type="translate"
values="0 10 ; 0 -10; 0 10"
repeatCount="indefinite"
begin="0.2"/>
</circle>
<circle fill="#0294FF" stroke="none" cx="54" cy="50" r="6">
<animateTransform
attributeName="transform"
dur="1s"
type="translate"
values="0 5 ; 0 -5; 0 5"
repeatCount="indefinite"
begin="0.3"/>
</circle>
</svg>

After

Width:  |  Height:  |  Size: 973 B

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
enable-background="new 0 0 595.28 841.89"
viewBox="0 0 340 456.33044"
height="486.75247"
width="362.66666"
y="0px"
x="0px"
id="Calque_1"
version="1.1"><metadata
id="metadata3280"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs3278" /><path
style="fill:#c9781f"
id="path3156"
d="m 65.412121,155.07154 c 0,0 24.66784,-21.47409 97.677319,-21.47409 66.97804,0 85.80047,15.19398 104.91761,21.47409 l -99.73639,48.73542 z" /><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3158"><path
style="fill:#f6d2a2"
id="path3160"
d="m 118.946,476.458 c 0.707,14.572 15.264,7.83 21.858,3.274 6.259,-4.325 8.089,-0.73 8.638,-9.266 0.36,-5.61 1.007,-11.22 0.688,-16.853 -9.464,-0.858 -19.759,1.396 -27.518,7.033 -3.996,2.905 -11.49,12.174 -3.666,15.812" /><path
style="fill:#c6b198"
id="path3162"
d="m 118.946,476.458 c 2.119,-0.788 4.364,-1.348 5.802,-3.264" /><path
style="fill:#37abc8"
id="path3164"
d="M 152.588,302.861 C 96.804,287.174 138.284,216.207 183.08,245.397 Z" /><path
style="fill:#37abc8"
id="path3166"
d="m 400.436,240.071 c 44.155,-31.014 84.056,38.959 32.74,56.565 z" /><path
style="fill:#f6d2a2"
id="path3168"
d="m 409.934,655.8 c 11.216,6.94 31.716,27.923 14.891,38.098 -16.166,14.802 -25.214,-16.247 -39.403,-20.549 6.111,-8.298 13.856,-15.865 24.512,-17.549 z" /><path
style="fill:none"
id="path3170"
d="m 424.825,693.897 c -2.494,-4.96 -3.332,-10.748 -7.496,-14.746" /><path
style="fill:#f6d2a2"
id="path3172"
d="m 209.561,679.514 c -13.164,2.037 -20.574,13.914 -31.548,19.945 -10.341,6.166 -14.297,-1.974 -15.229,-3.627 -1.621,-0.739 -1.485,0.688 -3.987,-1.831 -9.587,-15.13 9.989,-26.189 20.182,-33.705 14.198,-2.871 23.096,9.438 30.582,19.218 z" /><path
style="fill:none"
id="path3174"
d="m 162.785,695.831 c 0.501,-5.766 5.074,-9.628 7.251,-14.504" /><path
style="fill:#077e91"
id="path3176"
d="m 154.916,283.26 c -7.36,-3.893 -12.759,-9.18 -8.257,-17.693 4.168,-7.88 11.911,-7.025 19.271,-3.132 z" /><path
style="fill:#077e91"
id="path3178"
d="m 421.549,275.859 c 7.36,-3.893 12.759,-9.18 8.257,-17.693 -4.168,-7.881 -11.91,-7.025 -19.271,-3.132 z" /><path
style="fill:#f6d2a2"
id="path3180"
d="m 472.21,474.607 c -0.707,14.572 -15.264,7.83 -21.858,3.274 -6.259,-4.325 -8.089,-0.73 -8.638,-9.265 -0.36,-5.61 -1.007,-11.22 -0.688,-16.853 9.464,-0.858 19.759,1.396 27.518,7.033 3.996,2.904 11.49,12.174 3.666,15.811" /><path
style="fill:#c6b198"
id="path3182"
d="m 472.21,474.607 c -2.119,-0.788 -4.364,-1.348 -5.802,-3.264" /><g
id="g3184"><path
style="fill:#37abc8"
id="path3186"
d="m 289.988,210.595 c 55.847,0 108.2,7.987 135.492,61.642 24.496,60.141 15.785,124.993 19.521,188.553 3.208,54.577 10.322,117.629 -14.997,168.205 -26.635,53.21 -93.191,66.595 -148.026,64.634 -43.071,-1.541 -95.101,-15.593 -119.409,-54.944 -28.519,-46.165 -15.017,-114.81 -12.946,-166.179 2.454,-60.849 -16.482,-121.882 3.508,-181.425 20.737,-61.765 76.665,-75.724 136.857,-80.486" /></g><path
style="fill:#ffffff"
id="path3188"
d="m 299.847,285.567 c 10.027,58.288 105.304,42.877 91.619,-15.91 -12.271,-52.716 -94.951,-38.124 -91.619,15.91" /><path
style="fill:#ffffff"
id="path3190"
d="m 185.992,294.994 c 12.996,50.745 94.24,37.753 91.178,-13.149 -3.669,-60.964 -103.603,-49.2 -91.178,13.149" /><path
style="fill:#ffffff"
id="path3192"
d="m 318.343,353.511 c 0.044,7.79 1.843,15.403 0.289,24.148 -1.935,3.656 -5.729,4.043 -9.001,5.52 -4.524,-0.71 -8.328,-3.68 -10.143,-7.912 -1.161,-9.202 0.433,-18.111 0.726,-27.316 z" /><g
id="g3194"><ellipse
id="ellipse3196"
ry="14.86"
rx="13.719"
cy="286.71799"
cx="208.39999" /><ellipse
style="fill:#ffffff"
id="ellipse3198"
ry="3.777"
rx="3.234"
cy="290.07101"
cx="214.64" /></g><g
id="g3200"><ellipse
id="ellipse3202"
ry="14.86"
rx="13.491"
cy="283.017"
cx="323.34799" /><ellipse
style="fill:#ffffff"
id="ellipse3204"
ry="3.777"
rx="3.181"
cy="286.371"
cx="329.48499" /></g><path
style="fill:#ffffff"
id="path3206"
d="m 279.137,354.685 c -5.986,14.507 3.338,43.515 19.579,22.119 -1.161,-9.202 0.433,-18.111 0.726,-27.316 z" /><g
id="g3208"><path
style="fill:#f6d2a2"
id="path3210"
d="m 278.185,326.748 c -11.156,0.951 -20.276,14.216 -14.475,24.71 7.682,13.9 24.828,-1.23 35.507,0.188 12.291,0.252 22.361,12.996 32.233,2.304 10.979,-11.892 -4.727,-23.474 -17.002,-28.652 z" /></g></g><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3212"><path
style="fill:#ef9325"
id="path3214"
d="m 135.236,375.349 c 0,0 3.532,38.261 3.29,54.754 -0.242,16.492 15.56,6.079 16.38,32.016 0.82,25.937 -9.572,21.018 -15.047,33.713 -5.475,12.694 -9.196,72.145 -9.196,72.145 0,0 5.618,11.108 23.166,21.11 17.548,10.002 45.765,15.62 70.198,14.513 24.433,-1.107 41.885,-6.341 46.249,-9.236 4.363,-2.894 11.605,-12.05 14.329,-19.131 2.724,-7.081 8.415,-65.402 8.018,-98.775 -0.397,-33.373 -5.911,-64.166 -5.911,-64.166 z" /><path
style="fill:#e5e5e5"
id="path3216"
d="m 290.007,537.841 c -71.952,8.032 -155.439,-14.172 -155.439,-14.172 0,0 1.226,-22.521 6.626,-30.447 74.833,23.185 150.329,15.834 150.329,15.834 1.12,10.514 0.331,18.872 -1.516,28.785 z" /><path
style="fill:#e5e5e5"
id="path3218"
d="m 280.95,582.228 c -71.952,8.032 -150.287,-15.064 -150.287,-15.064 0,0 -0.251,-21.631 2.607,-31.13 74.833,23.185 154.763,17.407 154.763,17.407 -0.673,11.413 -2.278,22.646 -7.083,28.787 z" /></g><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3220"><path
style="fill:#ef9325"
id="path3222"
d="m 452.156,375.349 c 0,0 -0.488,38.261 -0.243,54.754 0.245,16.492 -9.578,6.079 -10.409,32.016 -0.831,25.937 9.692,21.018 15.236,33.713 5.544,12.694 9.312,72.145 9.312,72.145 0,0 -5.689,11.108 -23.457,21.11 -17.768,10.002 -46.34,15.62 -71.08,14.513 -24.74,-1.107 -42.411,-6.341 -46.83,-9.236 -4.419,-2.895 -11.751,-12.05 -14.509,-19.131 -2.758,-7.081 -8.521,-65.402 -8.119,-98.775 0.402,-33.373 5.985,-64.166 5.985,-64.166 z" /><path
style="fill:#e5e5e5"
id="path3224"
d="m 304.706,537.841 c 72.856,8.032 157.392,-14.172 157.392,-14.172 0,0 -1.242,-22.521 -6.709,-30.447 -75.774,23.185 -152.218,15.834 -152.218,15.834 -1.134,10.514 -0.335,18.872 1.535,28.785 z" /><path
style="fill:#e5e5e5"
id="path3226"
d="m 313.876,582.228 c 72.856,8.032 152.175,-15.064 152.175,-15.064 0,0 0.254,-21.631 -2.64,-31.13 -75.774,23.185 -156.707,17.407 -156.707,17.407 0.682,11.413 2.307,22.646 7.172,28.787 z" /></g><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3228"><path
style="fill:#d2e261"
id="path3230"
d="m 481.221,445.525 c -2.264,4.272 -6.745,6.334 -10.009,4.604 l -2.615,-1.385 c -3.264,-1.729 -4.076,-6.595 -1.812,-10.867 l 50.985,-96.234 c 2.263,-4.272 6.745,-6.334 10.009,-4.604 l 2.615,1.385 c 3.264,1.729 4.076,6.595 1.812,10.867 z" /><path
id="path3232"
d="m 457.143,490.972 c -1.319,2.489 -5.034,3.105 -8.298,1.375 l -2.615,-1.385 c -3.264,-1.729 -4.842,-5.149 -3.523,-7.638 l 23.92,-45.149 c 1.319,-2.489 5.034,-3.105 8.298,-1.375 l 2.615,1.385 c 3.264,1.729 4.842,5.149 3.523,7.638 z" /><path
style="fill:#9b9b9b"
id="path3234"
d="m 478.411,436.54 -2.615,-1.385 c -3.264,-1.729 -6.604,-1.823 -7.459,-0.21 l -1.529,2.886 c 0.855,-1.614 4.194,-1.52 7.459,0.21 l 2.615,1.385 c 3.264,1.729 5.218,4.44 4.363,6.053 l 1.529,-2.886 c 0.855,-1.613 -1.099,-4.323 -4.363,-6.053 z" /></g><ellipse
style="fill:#f6d2a2"
id="ellipse3236"
ry="8.6829996"
rx="11.224"
cy="462.66101"
cx="456.83801"
transform="matrix(0.59911638,0.2229746,-0.2229746,0.59911638,100.48325,-168.1724)" /><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3238"><path
style="fill:#d2e261"
id="path3240"
d="m 111.779,447.078 c 2.065,4.372 6.447,6.637 9.787,5.059 l 2.675,-1.264 c 3.34,-1.578 4.374,-6.401 2.309,-10.773 L 80.032,341.629 c -2.065,-4.372 -6.447,-6.637 -9.787,-5.059 l -2.675,1.264 c -3.34,1.578 -4.374,6.401 -2.309,10.773 z" /><path
id="path3242"
d="m 133.748,493.582 c 1.203,2.547 4.886,3.332 8.227,1.755 l 2.675,-1.264 c 3.34,-1.578 5.073,-4.922 3.869,-7.469 l -21.825,-46.199 c -1.203,-2.547 -4.886,-3.332 -8.227,-1.755 l -2.675,1.264 c -3.34,1.578 -5.073,4.922 -3.869,7.469 z" /><path
style="fill:#9b9b9b"
id="path3244"
d="m 114.998,438.232 2.675,-1.264 c 3.34,-1.578 6.68,-1.519 7.46,0.132 l 1.395,2.954 c -0.78,-1.651 -4.12,-1.71 -7.46,-0.133 l -2.675,1.264 c -3.34,1.578 -5.416,4.196 -4.636,5.847 l -1.395,-2.954 c -0.779,-1.65 1.296,-4.268 4.636,-5.846 z" /></g><ellipse
style="fill:#f6d2a2"
id="ellipse3246"
ry="8.6829996"
rx="11.224"
cy="463.922"
cx="137.56"
transform="matrix(-0.59911638,0.2229746,-0.2229746,-0.59911638,252.76813,458.95304)" /><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3248"><g
id="g3250"><path
style="fill:#960000"
id="path3252"
d="m 159.132,324.732 c -2.218,4.033 -7.45,5.414 -11.687,3.084 l -4.873,-2.679 c -4.237,-2.33 -5.873,-7.488 -3.656,-11.521 l 50.714,-92.23 c 2.218,-4.033 7.45,-5.414 11.687,-3.084 l 4.873,2.679 c 4.237,2.33 5.33,6.689 3.656,11.521 -17.765,51.258 -50.714,92.23 -50.714,92.23 z" /><path
style="fill:#595959"
id="path3254"
d="m 172.547,272.051 c 15.422,-28.047 25.555,-52.169 23.141,-54.905 l 0.057,-0.103 c -0.066,-0.036 -0.136,-0.06 -0.202,-0.096 -0.008,-0.005 -0.01,-0.021 -0.019,-0.026 l -0.007,0.014 c -23.699,-12.841 -55.583,-0.124 -71.334,28.522 -15.751,28.646 -9.41,62.381 14.128,75.514 l -0.007,0.014 c 0.009,0.005 0.023,-0.002 0.032,0.002 0.066,0.037 0.123,0.083 0.189,0.119 l 0.057,-0.103 c 3.602,0.573 18.543,-20.905 33.965,-48.952 z" /></g><g
id="g3256"><path
style="fill:#960000"
id="path3258"
d="m 426.693,324.925 c 2.1,4.095 7.291,5.627 11.593,3.42 l 4.948,-2.538 c 4.302,-2.206 6.087,-7.315 3.987,-11.41 L 399.19,220.742 c -2.1,-4.095 -7.291,-5.627 -11.593,-3.42 l -4.948,2.538 c -4.302,2.206 -5.521,6.533 -3.987,11.41 16.279,51.749 48.031,93.655 48.031,93.655 z" /><path
style="fill:#595959"
id="path3260"
d="m 414.804,271.879 c -14.606,-28.48 -24.039,-52.885 -21.547,-55.55 l -0.054,-0.105 c 0.067,-0.034 0.138,-0.056 0.205,-0.09 0.009,-0.005 0.011,-0.021 0.02,-0.025 l 0.007,0.014 c 24.059,-12.152 55.563,1.48 70.481,30.569 14.918,29.089 7.606,62.627 -16.301,75.075 l 0.007,0.014 c -0.009,0.004 -0.023,-0.003 -0.032,10e-4 -0.067,0.035 -0.125,0.08 -0.192,0.114 l -0.054,-0.105 c -3.618,0.468 -17.934,-21.432 -32.54,-49.912 z" /></g><path
style="fill:#353535"
id="path3262"
d="m 462.36,259.869 c 0,0 -17.746,-44.446 -38.326,-67.945 -20.58,-23.498 -221.937,-31.512 -255.003,0 -33.066,31.512 -45.533,67.945 -45.533,67.945 v -9.844 c 0,0 15.295,-43.268 45.533,-67.945 30.238,-24.677 228.946,-23.378 254.582,0 25.635,23.378 38.747,67.945 38.747,67.945 z" /><ellipse
style="fill:#960000"
id="ellipse3264"
ry="16.975"
rx="6.9489999"
cy="248.211"
cx="462.09201"
transform="matrix(-0.8898,0.4563,-0.4563,-0.8898,986.5333,258.1984)" /><ellipse
style="fill:#960000"
id="ellipse3266"
ry="16.975"
rx="6.9489999"
cy="247.02901"
cx="125.962"
transform="matrix(0.8763,0.4818,-0.4818,0.8763,134.6121,-30.1261)" /></g><g
transform="matrix(0.63926204,0,0,0.63926204,-21.039129,-84.874827)"
id="g3268"><path
style="opacity:0.6;fill:#ffffff"
id="path3270"
d="M 386.49,250.492 H 349.285 319.791 274.555 249 198.68 c -33.72,0 -33.604,89.606 -1.945,94.771 l 65.466,-4.21 c 7.915,0 23.719,-30.655 26.684,-33.065 2.965,-2.41 12.729,-2.704 16.575,0 3.846,2.704 12.593,29.976 20.507,29.976 l 65.466,7.298 c 30.579,-14.072 22.87,-94.77 -4.943,-94.77 z" /><path
style="opacity:0.5;fill:#ffffff"
id="path3272"
d="m 248.545,269.019 c -20.685,-0.462 -53.05,-0.274 -70.729,-0.13 -2.964,9.719 -6.269,22.024 -4.63,33.972 16.056,0 50.96,0.078 75.782,0.078 14.946,0 27.512,2.82 39.198,5.875 0.288,-0.368 0.53,-0.639 0.719,-0.792 2.965,-2.41 12.729,-2.704 16.575,0 0.995,0.7 2.323,3.058 3.874,6.149 5.967,1.258 11.943,2.127 18.153,2.205 16.915,0.214 56.557,0.146 82.066,0.074 3.249,-10.857 0.853,-22.495 0.125,-33.432 -24.906,0 -64.487,-0.562 -83.463,-0.562 -26.738,-0.001 -50.083,-12.821 -77.67,-13.437 z" /></g><path
id="path3274"
d="m 155.89646,123.63135 c -0.52803,-12.35565 23.02878,-13.90011 25.81085,-3.55813 2.77504,10.31833 -24.65122,12.71492 -25.81085,3.55813 -0.92565,-7.31124 0,0 0,0 z" /><g
transform="matrix(0.16363642,0,0,0.16363643,2.8667513,26.432443)"
id="g4788"><g
transform="matrix(4.1803662,0,0,4.1803662,-117.56255,1825.0688)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none"
id="text3009"><path
d="m 119.09119,162.63228 h 4.14844 c 1.68746,0 3.04684,-0.0469 4.07813,-0.14063 1.07808,-0.0937 2.13277,-0.30468 3.16406,-0.63281 1.07808,-0.32812 1.87495,-0.84375 2.39062,-1.54687 0.51558,-0.75 0.77339,-1.71094 0.77344,-2.88282 -5e-5,-1.31249 -0.44536,-2.46093 -1.33594,-3.44531 -0.89067,-0.98437 -2.10942,-1.47655 -3.65625,-1.47656 h -0.42187 l -4.78125,0.21094 h -0.77344 c -2.95316,0 -5.10941,-1.05468 -6.46875,-3.16407 -1.3594,-2.15623 -2.03909,-5.64842 -2.03906,-10.47656 v -38.32031 h 12.9375 c 3.0937,6e-5 4.64058,-1.335877 4.64062,-4.007814 -4e-5,-1.265559 -0.39848,-2.296808 -1.19531,-3.09375 -0.75004,-0.796806 -1.89848,-1.195243 -3.44531,-1.195313 h -12.9375 V 72.069778 c -3e-5,-1.640533 -0.46878,-2.88272 -1.40625,-3.726562 -0.93753,-0.890531 -2.1094,-1.335843 -3.51563,-1.335938 -1.50002,9.5e-5 -2.88283,0.539157 -4.14843,1.617188 -1.21877,1.078217 -1.87502,2.367278 -1.96875,3.867187 l -1.68751,19.96875 h -8.64844 c -1.546881,7e-5 -2.718754,0.398507 -3.515625,1.195313 -0.796878,0.750067 -1.195315,1.734441 -1.195313,2.953125 -2e-6,1.265689 0.398435,2.2735 1.195313,3.023437 0.843745,0.750062 2.039057,1.125062 3.585937,1.125002 h 8.578128 v 40.5 c -1e-5,7.26564 1.59374,12.65626 4.78126,16.17187 3.18747,3.46875 7.47653,5.20313 12.86718,5.20313"
style="font-size:144px;font-family:Nunito;-inkscape-font-specification:Nunito;fill:#333333"
id="path3014" /><path
d="m 154.80994,161.6479 c 1.73436,0 3.23436,-0.51562 4.5,-1.54687 1.31248,-1.03125 1.96873,-2.48437 1.96875,-4.35938 v -36.21093 c -2e-5,-5.48433 1.73435,-9.86714 5.20313,-13.14844 3.51559,-3.28119 8.43746,-4.92182 14.76562,-4.92188 1.49996,6e-5 2.6484,-0.49212 3.44532,-1.476559 0.79682,-1.031187 1.19526,-2.249936 1.19531,-3.65625 -5e-5,-1.499933 -0.44536,-2.812432 -1.33594,-3.9375 -0.84379,-1.12493 -2.01567,-1.687429 -3.51562,-1.6875 -4.96879,7.1e-5 -9.23441,1.476632 -12.79688,4.429687 -3.51565,2.906314 -5.8594,6.539122 -7.03125,10.898442 l 0.0703,-8.437504 c -2e-5,-1.781184 -0.63283,-3.163995 -1.89843,-4.148438 -1.21877,-1.03118 -2.69534,-1.546805 -4.42969,-1.546875 -1.73439,7e-5 -3.23439,0.515695 -4.5,1.546875 -1.26564,0.984443 -1.89845,2.414129 -1.89844,4.289063 v 58.007809 c -1e-5,1.87501 0.60936,3.32813 1.82813,4.35938 1.21873,1.03125 2.69529,1.54687 4.42968,1.54687"
style="font-size:144px;font-family:Nunito;-inkscape-font-specification:Nunito;fill:#333333"
id="path3016" /><path
d="m 326.8058,161.6479 c 1.73435,0 3.23435,-0.53906 4.5,-1.61718 1.2656,-1.125 1.89841,-2.67187 1.89844,-4.64063 v -54.63281 h 11.88281 c 2.99996,6e-5 4.49996,-1.359314 4.5,-4.078127 -4e-5,-1.218683 -0.37504,-2.226495 -1.125,-3.023437 -0.70317,-0.796806 -1.82817,-1.195243 -3.375,-1.195313 h -11.88281 v -5.484375 c -3e-5,-3.796796 0.14059,-6.796793 0.42187,-9 0.3281,-2.249914 0.91404,-3.960849 1.75782,-5.132812 0.89059,-1.21866 1.8984,-1.992097 3.02343,-2.320313 1.12497,-0.374908 2.69528,-0.562408 4.71094,-0.5625 h 5.97656 c 1.40621,9.2e-5 2.48433,-0.468657 3.23438,-1.40625 0.79682,-0.937406 1.19526,-2.062404 1.19531,-3.375 -5e-5,-1.312402 -0.39849,-2.437401 -1.19531,-3.375 -0.75005,-0.984274 -1.80474,-1.476461 -3.16406,-1.476562 h -8.57813 c -6.28128,1.01e-4 -11.20315,1.781349 -14.76562,5.34375 -3.56252,3.515717 -5.34377,9.468836 -5.34375,17.859375 v 8.929687 h -9.21094 c -1.54688,7e-5 -2.69532,0.398507 -3.44531,1.195313 -0.75001,0.796942 -1.12501,1.804754 -1.125,3.023437 -1e-5,1.171939 0.37499,2.156313 1.125,2.953125 0.74999,0.750062 1.89843,1.125062 3.44531,1.125002 h 9.21094 v 54.63281 c -2e-5,1.96876 0.60935,3.51563 1.82812,4.64063 1.26561,1.07812 2.76561,1.61718 4.5,1.61718"
style="font-size:144px;font-family:Nunito;-inkscape-font-specification:Nunito;fill:#333333"
id="path3020" /><path
d="m 371.17299,75.515091 c 2.48435,8.6e-5 4.47654,-0.703038 5.97656,-2.109375 1.49998,-1.453035 2.24998,-3.328033 2.25,-5.625 -2e-5,-2.343654 -0.75002,-4.218652 -2.25,-5.625 -1.50002,-1.453024 -3.46877,-2.179586 -5.90625,-2.179688 -2.48439,1.02e-4 -4.50001,0.726664 -6.04687,2.179688 -1.50001,1.453223 -2.25001,3.328221 -2.25,5.625 -1e-5,2.296967 0.74999,4.171965 2.25,5.625 1.49998,1.406337 3.49217,2.109461 5.97656,2.109375 m -0.0703,86.132809 c 1.73435,0 3.23435,-0.5625 4.5,-1.6875 1.31247,-1.17187 1.96872,-2.74218 1.96875,-4.71093 V 98.085403 c -3e-5,-1.968684 -0.63284,-3.515557 -1.89844,-4.640625 -1.21877,-1.12493 -2.6719,-1.68743 -4.35938,-1.6875 -1.73439,7e-5 -3.25782,0.56257 -4.57031,1.6875 -1.26564,1.125068 -1.89845,2.671941 -1.89844,4.640625 v 57.164067 c -1e-5,2.0625 0.60937,3.65625 1.82813,4.78125 1.21873,1.07812 2.6953,1.61718 4.42969,1.61718"
style="font-size:144px;font-family:Nunito;-inkscape-font-specification:Nunito;fill:#333333"
id="path3022" /><path
d="m 404.92299,161.6479 c 1.73436,0 3.23435,-0.5625 4.5,-1.6875 1.31248,-1.12499 1.96873,-2.6953 1.96875,-4.71093 v -30.65625 l 33.04687,35.15625 c 1.17182,1.3125 2.57807,1.96875 4.21875,1.96875 1.54682,0 2.90619,-0.58594 4.07813,-1.75782 1.21868,-1.17187 1.82806,-2.5078 1.82812,-4.00781 -6e-5,-1.35937 -0.51569,-2.57812 -1.54687,-3.65625 l -27.91406,-29.39062 25.52343,-22.85157 c 1.12494,-1.078059 1.68744,-2.27337 1.6875,-3.585934 -6e-5,-1.453058 -0.586,-2.765557 -1.75781,-3.9375 -1.12506,-1.171805 -2.43756,-1.757742 -3.9375,-1.757813 -1.17193,7.1e-5 -2.27349,0.445383 -3.30469,1.335938 L 411.39174,121.35884 V 67.569778 c -2e-5,-1.968654 -0.63284,-3.515527 -1.89844,-4.640625 -1.21877,-1.1249 -2.67189,-1.687399 -4.35937,-1.6875 -1.73439,1.01e-4 -3.25783,0.5626 -4.57032,1.6875 -1.26563,1.125098 -1.89844,2.671971 -1.89843,4.640625 v 87.679692 c -1e-5,2.0625 0.60936,3.65625 1.82812,4.78125 1.21874,1.07812 2.6953,1.61718 4.42969,1.61718"
style="font-size:144px;font-family:Nunito;-inkscape-font-specification:Nunito;fill:#333333"
id="path3024" /></g><path
style="display:inline;fill:#37abc8;fill-opacity:1;stroke:none"
d="m 776.62606,2500.7168 c -33.86547,-2.9196 -59.74328,-13.1348 -78.56521,-31.0137 -19.14975,-18.1909 -27.36664,-41.9069 -24.12384,-69.6286 3.73164,-31.9013 18.89136,-54.4293 45.47029,-67.5719 20.48497,-10.1294 40.92086,-13.9825 74.2015,-13.9904 23.6457,-0.01 43.45939,1.9263 73.67896,7.1839 7.4724,1.3001 13.94181,2.3644 14.37649,2.3653 0.63211,0 1.13342,-2.3782 2.5045,-11.8865 2.48071,-17.2034 2.92533,-23.1396 2.24949,-30.0309 -0.78666,-8.0217 -1.81332,-11.8425 -4.85232,-18.0592 -7.8409,-16.0392 -26.4539,-25.8526 -56.3431,-29.7057 -8.54805,-1.1019 -28.27642,-1.2561 -36.83948,-0.2884 -14.34651,1.622 -22.35237,3.6887 -40.20696,10.3786 -10.44586,3.9141 -10.4936,3.9262 -14.66034,3.6816 -7.16306,-0.4197 -12.05304,-3.6628 -15.18945,-10.0734 -1.29168,-2.6399 -1.5188,-3.7297 -1.51885,-7.2868 0,-5.3399 1.39069,-8.1747 6.23314,-12.7045 10.85909,-10.1583 29.13255,-18.8091 47.57591,-22.5234 11.80945,-2.3782 17.62271,-2.8782 33.18165,-2.8543 16.09952,0.042 26.39396,0.9338 40.7011,3.5942 23.79706,4.4245 41.86649,12.2234 55.49189,23.9501 4.04288,3.4794 10.13915,10.3276 12.75137,14.3237 0.79841,1.2215 1.6209,2.221 1.8277,2.221 0.20693,0 1.52767,-1.4677 2.9352,-3.2611 20.63182,-26.2903 55.38062,-41.1712 96.0916,-41.1503 28.7948,0 55.5598,7.2994 75.5111,20.5528 15.8247,10.5123 28.6803,26.7063 33.8736,42.6702 2.9819,9.1659 3.4352,12.5529 3.4627,25.8661 0.016,8.2838 -0.2116,13.8713 -0.6736,16.4602 -4.6692,26.1661 -15.7909,44.2934 -35.0951,57.2012 -15.53,10.384 -35.0343,16.4601 -60.5435,18.8601 -12.8411,1.2081 -36.32755,1.094 -51.54251,-0.2509 -12.9128,-1.1411 -28.76889,-3.149 -35.53309,-4.4992 -5.6619,-1.1304 -25.0506,-4.3677 -25.21024,-4.2092 -0.0703,0.071 -0.99285,5.8692 -2.04985,12.888 -1.68766,11.2067 -1.92673,14.003 -1.96214,22.9511 -0.0376,9.0977 0.0915,10.6938 1.18915,14.8925 3.52969,13.5013 11.30818,22.5326 25.42034,29.5146 13.88254,6.8684 28.43586,9.8406 50.42569,10.2988 9.73545,0.2048 14.72115,0.062 20.81185,-0.5827 14.7651,-1.5656 21.4933,-3.2833 41.1098,-10.4948 4.8858,-1.7964 9.872,-3.4309 11.0805,-3.6324 2.5898,-0.4318 7.4198,0.4599 10.2348,1.89 5.081,2.5805 9.3499,9.4627 9.3233,15.0301 -0.029,6.5042 -2.2232,10.4438 -8.6626,15.5672 -15.3968,12.2502 -35.0556,19.7627 -58.5228,22.3646 -8.3681,0.928 -27.4738,1.0647 -36.70232,0.2633 -25.98905,-2.2573 -44.03272,-6.7024 -60.52259,-14.9089 -12.86662,-6.4034 -22.68346,-14.3474 -30.01545,-24.2895 -1.80157,-2.443 -3.33187,-4.5027 -3.40069,-4.578 -0.069,-0.075 -1.26635,1.3361 -2.66126,3.1353 -6.16583,7.9536 -15.59468,16.5025 -24.80897,22.4937 -13.30221,8.6492 -32.39892,15.2475 -51.72224,17.8715 -5.76268,0.7822 -24.88234,1.4276 -29.78512,1.005 z m 33.18166,-37.2321 c 15.77633,-3.4964 29.83536,-11.3772 40.04156,-22.4447 9.30988,-10.0952 16.94591,-25.2189 21.14838,-41.8852 2.36889,-9.395 6.30655,-35.5428 5.43369,-36.082 -0.34447,-0.2132 -2.42546,-0.5497 -4.62386,-0.7487 -2.19846,-0.2007 -8.46495,-0.9389 -13.92555,-1.645 -32.43526,-4.195 -52.33389,-5.8249 -63.48931,-5.1995 -21.20826,1.188 -29.85413,3.0395 -42.58748,9.1207 -6.5233,3.1152 -11.02568,6.4959 -14.5881,10.953 -5.11092,6.3951 -8.19582,12.7501 -10.34612,21.3132 -1.5648,6.2312 -1.72448,22.5471 -0.27214,27.804 2.44652,8.8552 5.35078,13.8671 11.88143,20.5034 8.94252,9.0869 20.73103,15.1488 35.35353,18.18 7.47705,1.5501 7.25577,1.535 19.77505,1.3465 9.08402,-0.1379 12.48275,-0.393 16.19892,-1.2157 z M 1016.4745,2340.883 c 13.1943,-1.1642 21.3072,-3.2126 31.2752,-7.8955 8.2903,-3.8952 13.1088,-7.8762 17.6686,-14.5978 6.7131,-9.8962 9.3272,-19.2614 9.2601,-33.1758 -0.048,-9.9175 -0.8885,-14.0431 -4.3122,-21.1631 -7.6497,-15.908 -25.6199,-27.3028 -49.1887,-31.1902 -5.8812,-0.9698 -21.66658,-0.7976 -27.69493,0.3011 -11.5973,2.116 -21.40043,5.9729 -30.04639,11.8207 -15.97364,10.8042 -26.99764,27.0905 -33.13643,48.9538 -2.41127,8.5877 -3.05405,11.884 -5.20388,26.687 l -1.98309,13.6552 2.65642,0.2926 c 8.02308,0.8896 26.11491,3.0802 36.62189,4.4346 6.6102,0.852 14.72275,1.7612 18.02786,2.0208 9.55476,0.75 26.68545,0.6802 36.05555,-0.1463 v 0 z"
id="path4507" /></g></svg>

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,3 @@
export const environment = {
production: true
};

View file

@ -0,0 +1,8 @@
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false
};

BIN
webui/src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Some files were not shown because too many files have changed in this diff Show more