Merge branch 'v1.6' into master
1
.gitignore
vendored
|
@ -6,6 +6,7 @@
|
|||
/traefik
|
||||
/traefik.toml
|
||||
/static/
|
||||
/webui/.tmp/
|
||||
.vscode/
|
||||
/site/
|
||||
*.log
|
||||
|
|
180
CHANGELOG.md
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"})))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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||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||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||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).
|
||||
|
|
|
@ -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. |
|
||||
|
|
|
@ -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||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
|
||||
|
||||
|
|
|
@ -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||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||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||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` |
|
||||
|
|
|
@ -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. |
|
||||
|
|
|
@ -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` |
|
||||
|
|
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 218 KiB |
|
@ -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"
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
@ -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": {}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"presets": ["es2015"]
|
||||
}
|
|
@ -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
|
||||
|
|
1
webui/.gitattributes
vendored
|
@ -1 +0,0 @@
|
|||
* text=auto
|
48
webui/.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,4 +19,4 @@ RUN yarn install
|
|||
|
||||
COPY . $WEBUI_DIR/
|
||||
|
||||
EXPOSE 3000 3001 8080
|
||||
EXPOSE 8080
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
const conf = require('./gulp.conf');
|
||||
|
||||
module.exports = function () {
|
||||
return {
|
||||
server: {
|
||||
baseDir: [
|
||||
conf.paths.dist
|
||||
]
|
||||
},
|
||||
open: false
|
||||
};
|
||||
};
|
|
@ -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
|
||||
};
|
||||
};
|
|
@ -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');
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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')}`
|
||||
}
|
||||
};
|
|
@ -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'
|
||||
};
|
|
@ -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')}`
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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
|
||||
});
|
||||
};
|
|
@ -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
|
@ -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
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:8080",
|
||||
"secure": false
|
||||
},
|
||||
"/health": {
|
||||
"target": "http://localhost:8080",
|
||||
"secure": false
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"plugins": ["angular"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"jasmine": true
|
||||
},
|
||||
"globals": {
|
||||
"angular": true,
|
||||
"module": true,
|
||||
"inject": true
|
||||
}
|
||||
}
|
32
webui/src/app/app.component.spec.ts
Normal 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!');
|
||||
}));
|
||||
});
|
10
webui/src/app/app.component.ts
Normal 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 { }
|
43
webui/src/app/app.module.ts
Normal 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 { }
|
7
webui/src/app/charts/bar-chart/bar-chart.component.html
Normal 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>
|
25
webui/src/app/charts/bar-chart/bar-chart.component.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
114
webui/src/app/charts/bar-chart/bar-chart.component.ts
Normal 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
162
webui/src/app/charts/line-chart/line-chart.component.ts
Normal 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();
|
||||
}
|
||||
}
|
29
webui/src/app/components/header/header.component.html
Normal 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>
|
23
webui/src/app/components/header/header.component.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
104
webui/src/app/components/health/health.component.html
Normal 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> <span>{{ entry.status }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="tag">{{ entry.method }}</span> <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>
|
57
webui/src/app/components/health/health.component.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
599
webui/src/app/components/providers/providers.component.html
Normal 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>
|
59
webui/src/app/components/providers/providers.component.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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');
|
||||
}
|
|
@ -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
|
8
webui/src/app/pipes/keys.pipe.ts
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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 }} — {{ entry.status }}</td>
|
||||
<td>
|
||||
<span class="badge">{{ entry.method }}</span>
|
||||
|
||||
{{ 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>
|
|
@ -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'
|
||||
});
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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>
|
|
@ -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);
|
|
@ -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;
|
|
@ -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"> </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>
|
|
@ -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);
|
|
@ -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;
|
|
@ -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>
|
|
@ -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'
|
||||
});
|
||||
|
||||
}
|
|
@ -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('/');
|
||||
}
|
88
webui/src/app/services/api.service.ts
Normal 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;
|
||||
}, {});
|
||||
}
|
||||
}
|
17
webui/src/app/services/window.service.ts
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/** @ngInject */
|
||||
function VersionController($scope, Version) {
|
||||
Version.get(function (version) {
|
||||
$scope.version = version;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = VersionController;
|
|
@ -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);
|
BIN
webui/src/assets/fonts/OpenSans-Bold.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-BoldItalic.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-ExtraBold.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-ExtraBoldItalic.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-Italic.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-Light.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-LightItalic.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-Regular.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-Semibold.ttf
Executable file
BIN
webui/src/assets/fonts/OpenSans-SemiboldItalic.ttf
Executable file
30
webui/src/assets/images/loader.svg
Normal 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 |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
227
webui/src/assets/images/traefik.logo.svg
Normal 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 |
3
webui/src/environments/environment.prod.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const environment = {
|
||||
production: true
|
||||
};
|
8
webui/src/environments/environment.ts
Normal 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
After Width: | Height: | Size: 5.3 KiB |