diff --git a/.gitignore b/.gitignore
index 88a5343fe..77a760ab5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-dist
+/dist
gen.go
.idea
log
diff --git a/README.md b/README.md
index 890005799..fa1e92913 100644
--- a/README.md
+++ b/README.md
@@ -10,74 +10,79 @@ Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microse
It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), Rest API, file...) to manage its configuration automatically and dynamically.
-# Features
+## Features
-* No dependency hell, single binary made with go
-* Simple json Rest API
-* Simple TOML file configuration
-* Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
-* Watchers for backends, can listen change in backends to apply a new configuration automatically
-* Hot-reloading of configuration. No need to restart the process
-* Graceful shutdown http connections during hot-reloads
-* Circuit breakers on backends
-* Round Robin, rebalancer load-balancers
-* Rest Metrics
-* Tiny docker image included
-* SSL backends support
-* SSL frontend support
-* WebUI
+- No dependency hell, single binary made with go
+- Simple json Rest API
+- Simple TOML file configuration
+- Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
+- Watchers for backends, can listen change in backends to apply a new configuration automatically
+- Hot-reloading of configuration. No need to restart the process
+- Graceful shutdown http connections during hot-reloads
+- Circuit breakers on backends
+- Round Robin, rebalancer load-balancers
+- Rest Metrics
+- Tiny docker image included
+- SSL backends support
+- SSL frontend support
+- WebUI
-# Demo
+## Demo
Here is a demo of Træfɪk using Docker backend, showing a load-balancing between two servers, hot reloading of configuration, and graceful shutdown.
[![asciicast](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko.png)](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko)
-# Plumbing
+## Plumbing
-* [Oxy](https://github.com/mailgun/oxy/): an awsome proxy library made by Mailgun guys
-* [Gorilla mux](https://github.com/gorilla/mux): famous request router
-* [Negroni](https://github.com/codegangsta/negroni): web middlewares made simple
-* [Manners](https://github.com/mailgun/manners): graceful shutdown of http.Handler servers
+- [Oxy](https://github.com/mailgun/oxy/): an awsome proxy library made by Mailgun guys
+- [Gorilla mux](https://github.com/gorilla/mux): famous request router
+- [Negroni](https://github.com/codegangsta/negroni): web middlewares made simple
+- [Manners](https://github.com/mailgun/manners): graceful shutdown of http.Handler servers
-# Quick start
+## Quick start
-* The simple way: grab the latest binary from the [releases](https://github.com/emilevauge/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/EmileVauge/traefik/master/traefik.sample.toml):
+- The simple way: grab the latest binary from the [releases](https://github.com/emilevauge/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/EmileVauge/traefik/master/traefik.sample.toml):
-```
+```shell
./traefik traefik.toml
```
-* Use the tiny Docker image:
+- Use the tiny Docker image:
-```
+```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/traefik.toml emilevauge/traefik
```
-* From sources:
+- From sources:
-```
+```shell
git clone https://github.com/EmileVauge/traefik
```
-# Documentation
+## Documentation
You can find the complete documentation [here](docs/index.md).
-# Benchmarks
+## Benchmarks
Refer to the [benchmarks section](docs/index.md#benchmarks) in the documentation.
-# Contributing
+## Web UI
-## Building
+You can access to a simple HTML frontend of Træfik.
-You need either [Docker](https://github.com/docker/docker) and
-``make``, or `go` and `godep` in order to build traefik.
+![HTML frontend](docs/img/web.frontend.png)
-### Using Docker and Makefile
+## Contributing
-You need to run the ``binary`` target. This will create binaries for
+### Building
+
+You need either [Docker](https://github.com/docker/docker) and `make`, or `go` and `godep` in order to build traefik.
+
+#### Using Docker and Makefile
+
+You need to run the `binary` target. This will create binaries for
linux and darwin platforms in the `dist` folder.
```bash
@@ -101,7 +106,7 @@ $ ls dist/
traefik* traefik_darwin-386* traefik_darwin-amd64* traefik_linux-386* traefik_linux-amd64* traefik_linux-arm*
```
-### Using `godep`
+#### Using `godep`
The idea behind `godep` is the following :
@@ -125,7 +130,7 @@ $ godep go test ./...
ok _/home/vincent/src/github/vdemeester/traefik 0.004s
```
-## Tests
+### Tests
You can run unit tests using the `test-unit` target and the
integration test using the `test-integration` target.
diff --git a/docs/img/web.frontend.png b/docs/img/web.frontend.png
index ffbdd64cb..31cd35425 100644
Binary files a/docs/img/web.frontend.png and b/docs/img/web.frontend.png differ
diff --git a/docs/index.md b/docs/index.md
index 03bb16eb0..09e463963 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -29,12 +29,12 @@ Basically, Træfɪk is a http router, which sends traffic from frontends to http
Frontends can be defined using the following rules:
- * ```Headers```: Headers adds a matcher for request header values. It accepts a sequence of key/value pairs to be matched. For example: ```application/json```
- * ```HeadersRegexp```: Regular expressions can be used with headers as well. It accepts a sequence of key/value pairs, where the value has regex support. For example: ```application/(text|json)```
- * ```Host```: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by {}. Variables can define an optional regexp pattern to be matched: ```www.traefik.io```, ```{subdomain:[a-z]+}.traefik.io```
- * ```Methods```: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: ```GET```, ```POST```, ```PUT```
- * ```Path```: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by {}. The template must start with a "/". For exemple ```/products/``` ```/articles/{category}/{id:[0-9]+}```
- * ```PathPrefix```: PathPrefix adds a matcher for the URL path prefix. This matches if the given template is a prefix of the full URL path.
+ * `Headers`: Headers adds a matcher for request header values. It accepts a sequence of key/value pairs to be matched. For example: `application/json`
+ * `HeadersRegexp`: Regular expressions can be used with headers as well. It accepts a sequence of key/value pairs, where the value has regex support. For example: `application/(text|json)`
+ * `Host`: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched: `www.traefik.io`, `{subdomain:[a-z]+}.traefik.io`
+ * `Methods`: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: `GET`, `POST`, `PUT`
+ * `Path`: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by `{}`. The template must start with a `/`. For exemple `/products/` `/articles/{category}/{id:[0-9]+}`
+ * `PathPrefix`: PathPrefix adds a matcher for the URL path prefix. This matches if the given template is a prefix of the full URL path.
A frontend is a set of rules that forwards the incoming http traffic to a backend.
@@ -44,19 +44,19 @@ Frontends can be defined using the following rules:
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
Various methods of load-balancing is supported:
-* ```wrr```: Weighted Round Robin
-* ```drr```: Dynamic Round Robin: increases weights on servers that perform better than others. It also rolls back to original weights if the servers have changed.
+* `wrr`: Weighted Round Robin
+* `drr`: Dynamic Round Robin: increases weights on servers that perform better than others. It also rolls back to original weights if the servers have changed.
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
It can be configured using:
-* Methods: ```LatencyAtQuantileMS```, ```NetworkErrorRatio```, ```ResponseCodeRatio```
-* Operators: ```AND```, ```OR```, ```EQ```, ```NEQ```, ```LT```, ```LE```, ```GT```, ```GE```
+* Methods: `LatencyAtQuantileMS`, `NetworkErrorRatio`, `ResponseCodeRatio`
+* Operators: `AND`, `OR`, `EQ`, `NEQ`, `LT`, `LE`, `GT`, `GE`
For example:
-* ```NetworkErrorRatio() > 0.5```
-* ```LatencyAtQuantileMS(50.0) > 50```
-* ```ResponseCodeRatio(500, 600, 0, 600) > 0.5```
+* `NetworkErrorRatio() > 0.5`
+* `LatencyAtQuantileMS(50.0) > 50`
+* `ResponseCodeRatio(500, 600, 0, 600) > 0.5`
## Global configuration
@@ -114,7 +114,7 @@ For example:
Like any other reverse proxy, Træfɪk can be configured with a file. You have two choices:
-* simply add your configuration at the end of the global configuration file ```traefik.toml``` :
+* simply add your configuration at the end of the global configuration file `traefik.toml` :
```toml
# traefik.toml
@@ -156,11 +156,9 @@ logLevel = "DEBUG"
[frontends.frontend2.routes.test_2]
rule = "Path"
value = "/test"
-
-
```
-* or put your rules in a separate file, for example ```rules.tml```:
+* or put your rules in a separate file, for example `rules.tml`:
```toml
# traefik.toml
@@ -232,11 +230,11 @@ address = ":8080"
# KeyFile = "traefik.key"
```
-* ```/```: provides a simple HTML frontend of Træfik
+* `/`: provides a simple HTML frontend of Træfik
![HTML frontend](img/web.frontend.png)
-* ```/health```: ```GET``` json metrics
+* `/health`: `GET` json metrics
```sh
$ curl -s "http://localhost:8080/health" | jq .
@@ -257,7 +255,7 @@ $ curl -s "http://localhost:8080/health" | jq .
}
```
-* ```/api```: ```GET``` configuration for all providers
+* `/api`: `GET` configuration for all providers
```sh
$ curl -s "http://localhost:8080/api" | jq .
@@ -323,14 +321,14 @@ $ curl -s "http://localhost:8080/api" | jq .
}
```
-* ```/api/providers```: ```GET``` providers
-* ```/api/providers/{provider}```: ```GET``` or ```PUT``` provider
-* ```/api/providers/{provider}/backends```: ```GET``` backends
-* ```/api/providers/{provider}/backends/{backend}```: ```GET``` a backend
-* ```/api/providers/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend
-* ```/api/providers/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend
-* ```/api/providers/{provider}/frontends```: ```GET``` frontends
-* ```/api/providers/{provider}/frontends/{frontend}```: ```GET``` a frontend
+* `/api/providers`: `GET` providers
+* `/api/providers/{provider}`: `GET` or `PUT` provider
+* `/api/providers/{provider}/backends`: `GET` backends
+* `/api/providers/{provider}/backends/{backend}`: `GET` a backend
+* `/api/providers/{provider}/backends/{backend}/servers`: `GET` servers in a backend
+* `/api/providers/{provider}/backends/{backend}/servers/{server}`: `GET` a server in a backend
+* `/api/providers/{provider}/frontends`: `GET` frontends
+* `/api/providers/{provider}/frontends/{frontend}`: `GET` a frontend
## Docker backend
@@ -377,11 +375,11 @@ watch = true
Labels can be used on containers to override default behaviour:
-* ```traefik.backend=foo```: assign the container to ```foo``` backend
-* ```traefik.port=80```: register this port. Useful when the container exposes multiples ports.
-* ```traefik.weight=10```: assign this weight to the container
-* ```traefik.enable=false```: disable this container in Træfɪk
-* ```traefik.host=bar```: override the default routing from {containerName}.{domain} to bar.{domain}
+* `traefik.backend=foo`: assign the container to `foo` backend
+* `traefik.port=80`: register this port. Useful when the container exposes multiples ports.
+* `traefik.weight=10`: assign this weight to the container
+* `traefik.enable=false`: disable this container in Træfɪk
+* `traefik.host=bar`: override the default routing from {containerName}.{domain} to bar.{domain}
## Marathon backend
@@ -435,12 +433,12 @@ domain = "marathon.localhost"
Labels can be used on containers to override default behaviour:
-* ```traefik.backend=foo```: assign the application to ```foo``` backend
-* ```traefik.port=80```: register this port. Useful when the application exposes multiples ports.
-* ```traefik.weight=10```: assign this weight to the application
-* ```traefik.enable=false```: disable this application in Træfɪk
-* ```traefik.host=bar```: override the default routing from {appName}.{domain} to bar.{domain}
-* ```traefik.prefixes=pf1,pf2```: use PathPrefix(es) instead of hostname for routing, use filename="providerTemplates/marathon-prefix.tmpl" with this option
+* `traefik.backend=foo`: assign the application to `foo` backend
+* `traefik.port=80`: register this port. Useful when the application exposes multiples ports.
+* `traefik.weight=10`: assign this weight to the application
+* `traefik.enable=false`: disable this application in Træfɪk
+* `traefik.host=bar`: override the default routing from {appName}.{domain} to bar.{domain}
+* `traefik.prefixes=pf1,pf2`: use PathPrefix(es) instead of hostname for routing, use filename="providerTemplates/marathon-prefix.tmpl" with this option
## Consul backend
diff --git a/static/.editorconfig b/static/.editorconfig
new file mode 100644
index 000000000..203ab4f98
--- /dev/null
+++ b/static/.editorconfig
@@ -0,0 +1,20 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+# Change these settings to your own preference
+indent_style = space
+indent_size = 2
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+indent_size = 2
diff --git a/static/.gitignore b/static/.gitignore
new file mode 100644
index 000000000..0b09c594b
--- /dev/null
+++ b/static/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+# bower_components
diff --git a/static/.jshintignore b/static/.jshintignore
new file mode 100644
index 000000000..a088b6f05
--- /dev/null
+++ b/static/.jshintignore
@@ -0,0 +1,2 @@
+node_modules
+bower_components
diff --git a/static/.jshintrc b/static/.jshintrc
new file mode 100644
index 000000000..49cd8b839
--- /dev/null
+++ b/static/.jshintrc
@@ -0,0 +1,120 @@
+{
+ // JSHint Default Configuration File (as on JSHint website)
+ // See http://jshint.com/docs/ for more details
+
+ "maxerr" : 500, // {int} Maximum error before stopping (default:50)
+
+ // Enforcing
+ "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
+ "camelcase" : false, // true: Identifiers must be in camelCase
+ "curly" : true, // true: Require {} for every new block or scope
+ "eqeqeq" : true, // true: Require triple equals (===) for comparison
+ "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
+ "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
+ "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
+ "indent" : 4, // {int} Number of spaces to use for indentation
+ "latedef" : true, // true: Require variables/functions to be defined before being used
+ "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
+ "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
+ "noempty" : true, // true: Prohibit use of empty blocks
+ "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
+ "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
+ "plusplus" : false, // true: Prohibit use of `++` & `--`
+ "quotmark" : false, // Quotation mark consistency:
+ // false : do nothing (default)
+ // true : ensure whatever is used is consistent
+ // "single" : require single quotes
+ // "double" : require double quotes
+ "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions.
+ "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
+ "unused" : true, // true: Require all defined variables be used
+ "strict" : true, // true: Requires all functions run in ES5 Strict Mode
+ "trailing" : true, // Prohibit trailing whitespaces.
+ "maxparams" : false, // {int} Max number of formal params allowed per function
+ "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
+ "maxstatements" : false, // {int} Max number statements per function
+ "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
+ "maxlen" : false, // {int} Max number of characters per line
+
+ // Relaxing
+ "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
+ "boss" : false, // true: Tolerate assignments where comparisons would be expected
+ "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
+ "eqnull" : false, // true: Tolerate use of `== null`
+ //"es5" : true, // true: Allow ES5 syntax (ex: getters and setters) [default: true] if override to true -> generate error
+ "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
+ "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
+ // (ex: `for each`, multiple try/catch, function expression…)
+ "evil" : false, // true: Tolerate use of `eval` and `new Function()`
+ "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
+ "funcscope" : false, // true: Tolerate defining variables inside control statements
+ "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
+ "iterator" : false, // true: Tolerate using the `__iterator__` property
+ "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
+ "laxbreak" : true, // true: Tolerate possibly unsafe line breakings
+ "laxcomma" : true, // true: Tolerate comma-first style coding
+ "loopfunc" : false, // true: Tolerate functions being defined in loops
+ "multistr" : false, // true: Tolerate multi-line strings
+ "noyield" : false, // true: Tolerate generator functions with no yield statement in them.
+ "notypeof" : false, // true: Tolerate invalid typeof operator values
+ "proto" : false, // true: Tolerate using the `__proto__` property
+ "scripturl" : false, // true: Tolerate script-targeted URLs
+ "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
+ "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignment only.
+ "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
+ "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
+ "validthis" : false, // true: Tolerate using this in a non-constructor function
+
+ // JSLint Legacy
+ "nomen" : false, // Prohibit use of initial or trailing underbars in names.
+ "onevar" : false, // Allow only one `var` statement per function.
+ "passfail" : false, // Stop on first error.
+ "white" : false, // Check against strict whitespace and indentation rules.
+
+ // Environments
+ "browser" : true, // Web Browser (window, document, etc)
+ //"browserify" : false, // Browserify (node.js code in the browser)
+ "couch" : false, // CouchDB
+ "devel" : false, // Development/debugging (alert, confirm, etc)
+ "dojo" : false, // Dojo Toolkit
+ "jasmine" : true, // Jasmine
+ "jquery" : true, // jQuery
+ "mocha" : true, // Mocha
+ "mootools" : false, // MooTools
+ "node" : true, // Node.js
+ "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
+ "prototypejs" : false, // Prototype and Scriptaculous
+ "qunit" : false, // QUnit
+ "rhino" : false, // Rhino
+ "shelljs" : false, // ShellJS
+ "worker" : false, // Web Workers
+ "wsh" : false, // Windows Scripting Host
+ "yui" : false, // Yahoo User Interface
+
+ // Custom Globals
+ // additional predefined global variables
+ "globals" : {
+ "angular": false,
+ "require": false,
+ "process": false,
+ "module": false,
+ "__dirname": false,
+ "browser": false,
+ "repeater": false,
+ "element": false,
+ "inject": false,
+ "afterEach": false,
+ "beforeEach": false,
+ "confirm": false,
+ "describe": false,
+ "expect": false,
+ "it": false,
+ "jasmine": false,
+ "by": false,
+ "runs": false,
+ "spyOn": false,
+ "spyOnEvent": false,
+ "waitsFor": false,
+ "xdescribe": false
+ }
+}
diff --git a/static/app/core/health.resource.js b/static/app/core/health.resource.js
new file mode 100644
index 000000000..1648f2eb3
--- /dev/null
+++ b/static/app/core/health.resource.js
@@ -0,0 +1,10 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('traefik.core.health', ['ngResource'])
+ .factory('Health', ['$resource', function ($resource) {
+ return $resource('/health');
+ }]);
+
+})();
diff --git a/static/app/core/providers.resource.js b/static/app/core/providers.resource.js
new file mode 100644
index 000000000..fb74329d9
--- /dev/null
+++ b/static/app/core/providers.resource.js
@@ -0,0 +1,10 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('traefik.core.provider', ['ngResource'])
+ .factory('Providers', ['$resource', function ($resource) {
+ return $resource('/api/providers');
+ }]);
+
+})();
diff --git a/static/app/sections/health/health.controller.js b/static/app/sections/health/health.controller.js
new file mode 100644
index 000000000..3f2a4ab7c
--- /dev/null
+++ b/static/app/sections/health/health.controller.js
@@ -0,0 +1,26 @@
+(function () {
+ 'use strict';
+
+ angular.module('traefik.section.health')
+ .controller('HealthController', ['$scope', '$interval', '$log', 'Health', function ($scope, $interval, $log, Health) {
+
+ var vm = this;
+
+ vm.health = Health.get();
+
+ var intervalId = $interval(function () {
+ Health.get(function (health) {
+ vm.health = health;
+ }, function (error) {
+ vm.health = {};
+ $log.error(error);
+ });
+ }, 3000);
+
+ $scope.$on('$destroy', function () {
+ $interval.cancel(intervalId);
+ });
+
+ }]);
+
+})();
diff --git a/static/app/sections/health/health.html b/static/app/sections/health/health.html
new file mode 100644
index 000000000..98e50955d
--- /dev/null
+++ b/static/app/sections/health/health.html
@@ -0,0 +1,46 @@
+
+
+ {{frontendCtrl.frontendId}} [{{frontendCtrl.providerId}}]
+
+
+
+
+ Route |
+ Rule |
+ Value |
+
+
+ {{routeId}} |
+ {{route.Rule}} |
+ {{route.Value}} |
+
+
+
+
+
diff --git a/static/app/sections/providers/frontend-monitor/frontend-monitor.module.js b/static/app/sections/providers/frontend-monitor/frontend-monitor.module.js
new file mode 100644
index 000000000..6257a7768
--- /dev/null
+++ b/static/app/sections/providers/frontend-monitor/frontend-monitor.module.js
@@ -0,0 +1,6 @@
+(function () {
+ 'use strict';
+
+ angular.module('traefik.section.providers.frontend-monitor', []);
+
+})();
diff --git a/static/app/sections/providers/providers.controller.js b/static/app/sections/providers/providers.controller.js
new file mode 100644
index 000000000..92b717053
--- /dev/null
+++ b/static/app/sections/providers/providers.controller.js
@@ -0,0 +1,27 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('traefik.section.providers')
+ .controller('ProvidersController', ['$scope', '$interval', '$log', 'Providers', function ($scope, $interval, $log, Providers) {
+
+ var vm = this;
+
+ vm.providers = Providers.get();
+
+ var intervalId = $interval(function () {
+ Providers.get(function (providers) {
+ vm.providers = providers;
+ }, function (error) {
+ vm.providers = {};
+ $log.error(error);
+ });
+ }, 2000);
+
+ $scope.$on('$destroy', function () {
+ $interval.cancel(intervalId);
+ });
+
+ }]);
+
+})();
diff --git a/static/app/sections/providers/providers.html b/static/app/sections/providers/providers.html
new file mode 100644
index 000000000..5db7daa10
--- /dev/null
+++ b/static/app/sections/providers/providers.html
@@ -0,0 +1,14 @@
+
+