Add more pages in the WebUI

This commit is contained in:
Jorge Gonzalez 2019-09-10 14:40:05 +02:00 committed by Traefiker Bot
parent 2b828765e3
commit fcc1109e76
82 changed files with 5005 additions and 249 deletions

View file

@ -90,8 +90,7 @@ To get your hands on Traefik, you can use the [5-Minute Quickstart](http://docs.
You can access the simple HTML frontend of Traefik. You can access the simple HTML frontend of Traefik.
![Web UI Providers](docs/content/assets/img/dashboard-main.png) ![Web UI Providers](docs/content/assets/img/webui-dashboard.png)
![Web UI Health](docs/content/assets/img/dashboard-health.png)
## Documentation ## Documentation

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View file

@ -3,7 +3,9 @@
Adding Client Certificates in a Header Adding Client Certificates in a Header
{: .subtitle } {: .subtitle }
`TODO add schema` <!--
TODO: add schema
-->
PassTLSClientCert adds in header the selected data from the passed client tls certificate. PassTLSClientCert adds in header the selected data from the passed client tls certificate.

View file

@ -3,7 +3,9 @@
Redirecting the Client to a Different Location Redirecting the Client to a Different Location
{: .subtitle } {: .subtitle }
`TODO: add schema` <!--
TODO: add schema
-->
RegexRedirect redirect a request from an url to another with regex matching and replacement. RegexRedirect redirect a request from an url to another with regex matching and replacement.

View file

@ -3,7 +3,9 @@
Redirecting the Client to a Different Scheme/Port Redirecting the Client to a Different Scheme/Port
{: .subtitle } {: .subtitle }
`TODO: add schema` <!--
TODO: add schema
-->
RegexRedirect redirect request from a scheme to another. RegexRedirect redirect request from a scheme to another.

View file

@ -3,7 +3,9 @@
Updating the Path Before Forwarding the Request Updating the Path Before Forwarding the Request
{: .subtitle } {: .subtitle }
`TODO: add schema` <!--
TODO: add schema
-->
Replace the path of the request url. Replace the path of the request url.

View file

@ -3,7 +3,9 @@
Updating the Path Before Forwarding the Request (Using a Regex) Updating the Path Before Forwarding the Request (Using a Regex)
{: .subtitle } {: .subtitle }
`TODO: add schema` <!--
TODO: add schema
-->
The ReplaceRegex replace a path from an url to another with regex matching and replacement. The ReplaceRegex replace a path from an url to another with regex matching and replacement.

View file

@ -3,7 +3,9 @@
Retrying until it Succeeds Retrying until it Succeeds
{: .subtitle } {: .subtitle }
`TODO: add schema` <!--
TODO: add schema
-->
The Retry middleware is in charge of reissuing a request a given number of times to a backend server if that server does not reply. The Retry middleware is in charge of reissuing a request a given number of times to a backend server if that server does not reply.
To be clear, as soon as the server answers, the middleware stops retrying, regardless of the response status. To be clear, as soon as the server answers, the middleware stops retrying, regardless of the response status.

View file

@ -3,7 +3,9 @@
Removing Prefixes From the Path Before Forwarding the Request Removing Prefixes From the Path Before Forwarding the Request
{: .subtitle } {: .subtitle }
`TODO: add schema` <!--
TODO: add schema
-->
Remove the specified prefixes from the URL path. Remove the specified prefixes from the URL path.

View file

@ -5,25 +5,16 @@ See What's Going On
The dashboard is the central place that shows you the current active routes handled by Traefik. The dashboard is the central place that shows you the current active routes handled by Traefik.
!!! warning "Dashboard WIP"
Currently, the dashboard is in a Work In Progress State while being reconstructed for v2.
Therefore, the dashboard is currently not working.
<figure> <figure>
<img src="../../assets/img/dashboard-main.png" alt="Dashboard - Providers" /> <img src="../../assets/img/webui-dashboard.png" alt="Dashboard - Providers" />
<figcaption>The dashboard in action with Traefik listening to 3 different providers</figcaption> <figcaption>The dashboard in action</figcaption>
</figure>
<figure>
<img src="../../assets/img/dashboard-health.png" alt="Dashboard - Health" />
<figcaption>The dashboard shows the health of the system.</figcaption>
</figure> </figure>
By default, the dashboard is available on `/` on port `:8080`. By default, the dashboard is available on `/` on port `:8080`.
!!! tip "Did You Know?" !!! tip "Did You Know?"
It is possible to customize the dashboard endpoint. It is possible to customize the dashboard endpoint.
To learn how, refer to the `Traefik's API documentation`(TODO: add doc and link). To learn how, refer to the [API documentation](./api.md)
## Enabling the Dashboard ## Enabling the Dashboard
@ -64,4 +55,4 @@ api:
!!! tip "Did You Know?" !!! tip "Did You Know?"
The API provides more features than the Dashboard. The API provides more features than the Dashboard.
To learn more about it, refer to the `Traefik's API documentation`(TODO: add doc and link). To learn more about it, refer to the [API documentation](./api.md)

137
webui/package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "traefik-ui", "name": "traefik-ui",
"version": "0.1.0", "version": "2.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -1465,6 +1465,7 @@
"version": "1.3.7", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dev": true,
"requires": { "requires": {
"mime-types": "~2.1.24", "mime-types": "~2.1.24",
"negotiator": "0.6.2" "negotiator": "0.6.2"
@ -1619,7 +1620,8 @@
"array-flatten": { "array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
}, },
"array-includes": { "array-includes": {
"version": "3.0.3", "version": "3.0.3",
@ -2091,6 +2093,7 @@
"version": "1.19.0", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dev": true,
"requires": { "requires": {
"bytes": "3.1.0", "bytes": "3.1.0",
"content-type": "~1.0.4", "content-type": "~1.0.4",
@ -2108,6 +2111,7 @@
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
} }
@ -2115,7 +2119,8 @@
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
} }
} }
}, },
@ -2297,7 +2302,8 @@
"bytes": { "bytes": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"dev": true
}, },
"cacache": { "cacache": {
"version": "11.3.3", "version": "11.3.3",
@ -2929,7 +2935,8 @@
"connect-history-api-fallback": { "connect-history-api-fallback": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
"dev": true
}, },
"console-browserify": { "console-browserify": {
"version": "1.1.0", "version": "1.1.0",
@ -2971,6 +2978,7 @@
"version": "0.5.3", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dev": true,
"requires": { "requires": {
"safe-buffer": "5.1.2" "safe-buffer": "5.1.2"
} }
@ -2978,7 +2986,8 @@
"content-type": { "content-type": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"dev": true
}, },
"convert-source-map": { "convert-source-map": {
"version": "1.6.0", "version": "1.6.0",
@ -2992,12 +3001,14 @@
"cookie": { "cookie": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"dev": true
}, },
"cookie-signature": { "cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
"dev": true
}, },
"copy-concurrently": { "copy-concurrently": {
"version": "1.0.5", "version": "1.0.5",
@ -3575,7 +3586,8 @@
"depd": { "depd": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"dev": true
}, },
"des.js": { "des.js": {
"version": "1.0.0", "version": "1.0.0",
@ -3590,7 +3602,8 @@
"destroy": { "destroy": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"dev": true
}, },
"detect-node": { "detect-node": {
"version": "2.0.4", "version": "2.0.4",
@ -3751,7 +3764,8 @@
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"dev": true
}, },
"ejs": { "ejs": {
"version": "2.6.2", "version": "2.6.2",
@ -3812,7 +3826,8 @@
"encodeurl": { "encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
}, },
"end-of-stream": { "end-of-stream": {
"version": "1.4.1", "version": "1.4.1",
@ -3895,7 +3910,8 @@
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
"dev": true
}, },
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
@ -4296,7 +4312,8 @@
"etag": { "etag": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
}, },
"eventemitter3": { "eventemitter3": {
"version": "3.1.2", "version": "3.1.2",
@ -4398,6 +4415,7 @@
"version": "4.17.1", "version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dev": true,
"requires": { "requires": {
"accepts": "~1.3.7", "accepts": "~1.3.7",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@ -4435,6 +4453,7 @@
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
} }
@ -4442,7 +4461,8 @@
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
} }
} }
}, },
@ -4681,6 +4701,7 @@
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dev": true,
"requires": { "requires": {
"debug": "2.6.9", "debug": "2.6.9",
"encodeurl": "~1.0.2", "encodeurl": "~1.0.2",
@ -4695,6 +4716,7 @@
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
} }
@ -4702,7 +4724,8 @@
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
} }
} }
}, },
@ -4844,7 +4867,8 @@
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"dev": true
}, },
"fragment-cache": { "fragment-cache": {
"version": "0.2.1", "version": "0.2.1",
@ -4858,7 +4882,8 @@
"fresh": { "fresh": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"dev": true
}, },
"friendly-errors-webpack-plugin": { "friendly-errors-webpack-plugin": {
"version": "1.7.0", "version": "1.7.0",
@ -5515,6 +5540,7 @@
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dev": true,
"requires": { "requires": {
"depd": "~1.1.2", "depd": "~1.1.2",
"inherits": "2.0.3", "inherits": "2.0.3",
@ -5526,7 +5552,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
} }
} }
}, },
@ -5700,6 +5727,7 @@
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
@ -5873,7 +5901,8 @@
"ipaddr.js": { "ipaddr.js": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==",
"dev": true
}, },
"is-absolute-url": { "is-absolute-url": {
"version": "2.1.0", "version": "2.1.0",
@ -6704,7 +6733,8 @@
"media-typer": { "media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
}, },
"mem": { "mem": {
"version": "4.3.0", "version": "4.3.0",
@ -6846,7 +6876,8 @@
"merge-descriptors": { "merge-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
"dev": true
}, },
"merge-source-map": { "merge-source-map": {
"version": "1.1.0", "version": "1.1.0",
@ -6874,7 +6905,8 @@
"methods": { "methods": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"dev": true
}, },
"micromatch": { "micromatch": {
"version": "4.0.2", "version": "4.0.2",
@ -6899,17 +6931,20 @@
"mime": { "mime": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true
}, },
"mime-db": { "mime-db": {
"version": "1.40.0", "version": "1.40.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
"dev": true
}, },
"mime-types": { "mime-types": {
"version": "2.1.24", "version": "2.1.24",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
"dev": true,
"requires": { "requires": {
"mime-db": "1.40.0" "mime-db": "1.40.0"
} }
@ -7128,7 +7163,8 @@
"negotiator": { "negotiator": {
"version": "0.6.2", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"dev": true
}, },
"neo-async": { "neo-async": {
"version": "2.6.1", "version": "2.6.1",
@ -7540,6 +7576,7 @@
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dev": true,
"requires": { "requires": {
"ee-first": "1.1.1" "ee-first": "1.1.1"
} }
@ -7803,7 +7840,8 @@
"parseurl": { "parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true
}, },
"pascalcase": { "pascalcase": {
"version": "0.1.1", "version": "0.1.1",
@ -7856,7 +7894,8 @@
"path-to-regexp": { "path-to-regexp": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
"dev": true
}, },
"path-type": { "path-type": {
"version": "3.0.0", "version": "3.0.0",
@ -8719,6 +8758,7 @@
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
"dev": true,
"requires": { "requires": {
"forwarded": "~0.1.2", "forwarded": "~0.1.2",
"ipaddr.js": "1.9.0" "ipaddr.js": "1.9.0"
@ -8804,7 +8844,8 @@
"qs": { "qs": {
"version": "6.7.0", "version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"dev": true
}, },
"quasar": { "quasar": {
"version": "1.0.5", "version": "1.0.5",
@ -8861,12 +8902,14 @@
"range-parser": { "range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dev": true
}, },
"raw-body": { "raw-body": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dev": true,
"requires": { "requires": {
"bytes": "3.1.0", "bytes": "3.1.0",
"http-errors": "1.7.2", "http-errors": "1.7.2",
@ -9382,7 +9425,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
}, },
"safe-regex": { "safe-regex": {
"version": "1.1.0", "version": "1.1.0",
@ -9396,7 +9440,8 @@
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
}, },
"sass-graph": { "sass-graph": {
"version": "2.2.4", "version": "2.2.4",
@ -9713,6 +9758,7 @@
"version": "0.17.1", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dev": true,
"requires": { "requires": {
"debug": "2.6.9", "debug": "2.6.9",
"depd": "~1.1.2", "depd": "~1.1.2",
@ -9733,6 +9779,7 @@
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
}, },
@ -9740,14 +9787,16 @@
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
} }
} }
}, },
"ms": { "ms": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
} }
} }
}, },
@ -9817,6 +9866,7 @@
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dev": true,
"requires": { "requires": {
"encodeurl": "~1.0.2", "encodeurl": "~1.0.2",
"escape-html": "~1.0.3", "escape-html": "~1.0.3",
@ -9862,7 +9912,8 @@
"setprototypeof": { "setprototypeof": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"dev": true
}, },
"sha.js": { "sha.js": {
"version": "2.4.11", "version": "2.4.11",
@ -10321,7 +10372,8 @@
"statuses": { "statuses": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"dev": true
}, },
"stdout-stream": { "stdout-stream": {
"version": "1.4.1", "version": "1.4.1",
@ -10792,7 +10844,8 @@
"toidentifier": { "toidentifier": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true
}, },
"toposort": { "toposort": {
"version": "1.0.7", "version": "1.0.7",
@ -10885,6 +10938,7 @@
"version": "1.6.18", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"requires": { "requires": {
"media-typer": "0.3.0", "media-typer": "0.3.0",
"mime-types": "~2.1.24" "mime-types": "~2.1.24"
@ -10993,7 +11047,8 @@
"unpipe": { "unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
}, },
"unquote": { "unquote": {
"version": "1.1.1", "version": "1.1.1",
@ -11163,7 +11218,8 @@
"utils-merge": { "utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true
}, },
"uuid": { "uuid": {
"version": "3.3.2", "version": "3.3.2",
@ -11184,7 +11240,8 @@
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"dev": true
}, },
"vendors": { "vendors": {
"version": "1.0.3", "version": "1.0.3",

View file

@ -20,8 +20,6 @@
"axios": "^0.19.0", "axios": "^0.19.0",
"bowser": "^2.5.2", "bowser": "^2.5.2",
"chart.js": "^2.8.0", "chart.js": "^2.8.0",
"connect-history-api-fallback": "^1.6.0",
"express": "^4.17.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"moment": "^2.24.0", "moment": "^2.24.0",
"quasar": "^1.0.0", "quasar": "^1.0.0",

View file

@ -85,7 +85,8 @@ module.exports = function (ctx) {
'QDialog', 'QDialog',
'QUploader', 'QUploader',
'QTree', 'QTree',
'QChip' 'QChip',
'QBtnToggle'
], ],
directives: [ directives: [

View file

@ -1,4 +1,5 @@
import { APP } from '../_helpers/APP' import { APP } from '../_helpers/APP'
import Helps from '../_helpers/Helps'
const Boot = { const Boot = {
install (Vue, options) { install (Vue, options) {
@ -13,11 +14,23 @@ const Boot = {
}, },
env () { env () {
return APP.config.env return APP.config.env
},
appThumbStyle () {
return {
right: '2px',
borderRadius: '2px',
backgroundColor: '#dcdcdc',
width: '6px',
opacity: 0.75
}
} }
}, },
methods: { methods: {
}, },
filters: { filters: {
capFirstLetter (value) {
return Helps.capFirstLetter(value)
}
}, },
created () { created () {
} }

View file

@ -10,6 +10,15 @@ function getAll () {
}) })
} }
export default { function getByName (name) {
getAll return APP.api.get(`${apiBase}/${name}`)
.then(body => {
console.log('Success -> EntrypointsService -> getByName', body.data)
return body.data
})
}
export default {
getAll,
getByName
} }

View file

@ -0,0 +1,75 @@
import { APP } from '../_helpers/APP'
const apiBase = '/http'
function getAllRouters (params) {
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllRouters', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
})
}
function getRouterByName (name) {
return APP.api.get(`${apiBase}/routers/${name}`)
.then(body => {
console.log('Success -> HttpService -> getRouterByName', body.data)
return body.data
})
}
function getAllServices (params) {
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllServices', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
})
}
function getServiceByName (name) {
return APP.api.get(`${apiBase}/services/${name}`)
.then(body => {
console.log('Success -> HttpService -> getServiceByName', body.data)
return body.data
})
}
function getAllMiddlewares (params) {
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
})
}
function getMiddlewareByName (name) {
return APP.api.get(`${apiBase}/middlewares/${name}`)
.then(body => {
console.log('Success -> HttpService -> getMiddlewareByName', body.data)
return body.data
})
}
export default {
getAllRouters,
getRouterByName,
getAllServices,
getServiceByName,
getAllMiddlewares,
getMiddlewareByName
}

View file

@ -0,0 +1,52 @@
import { APP } from '../_helpers/APP'
const apiBase = '/tcp'
function getAllRouters (params) {
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllRouters', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
})
}
function getRouterByName (name) {
return APP.api.get(`${apiBase}/routers/${name}`)
.then(body => {
console.log('Success -> HttpService -> getRouterByName', body.data)
return body.data
})
}
function getAllServices (params) {
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllServices', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
})
}
function getServiceByName (name) {
return APP.api.get(`${apiBase}/services/${name}`)
.then(body => {
console.log('Success -> HttpService -> getServiceByName', body.data)
return body.data
})
}
export default {
getAllRouters,
getRouterByName,
getAllServices,
getServiceByName
}

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="88" height="96" viewBox="0 0 88 96">
<g fill="none" fill-rule="nonzero" stroke="#DCDCDC" stroke-width="4">
<path fill="#F2F3F5" d="M41.05 42.807zm0 0l-.006.003 1.917 3.508-1.876-3.532c1.866-1.05 4.007-1.044 5.82-.006 8.058 4.62 22.413 13.097 37.112 21.84C85.14 65.299 86 66.39 86 68c0 1.612-.866 2.71-1.993 3.383-15.424 9.173-29.305 17.368-37.1 21.836a5.813 5.813 0 0 1-5.812 0C33.322 88.765 19.88 80.83 3.983 71.38 2.86 70.7 2 69.61 2 67.999c0-1.615.869-2.72 1.993-3.382a8052.42 8052.42 0 0 1 17.004-10.023l.05-.03a33723.823 33723.823 0 0 1 18.288-10.75l1.27-.746.334-.196.085-.05.022-.013.004-.002z"/>
<path fill="#FFF" stroke-dasharray="0,10" stroke-linecap="round" d="M4 48c0-.96.707-1.483 1.012-1.662 9.254-5.48 37.058-21.81 37.054-21.809 1.274-.717 2.664-.69 3.844-.014 10.211 5.854 30.961 18.182 37.074 21.819.305.183 1.016.71 1.016 1.666 0 .955-.71 1.482-1.016 1.664-7.914 4.707-27.37 16.26-37.07 21.818a3.813 3.813 0 0 1-3.824.002c-9.703-5.56-29.16-17.113-37.074-21.818C4.71 49.482 4 48.955 4 48z"/>
<path fill="#F2F3F5" d="M41.05 2.807zm0 0l-.006.003 1.917 3.508-1.876-3.532c1.866-1.05 4.007-1.044 5.82-.006 8.058 4.62 22.413 13.097 37.112 21.84C85.14 25.299 86 26.39 86 28c0 1.612-.866 2.71-1.993 3.383-15.424 9.173-29.305 17.368-37.1 21.836a5.813 5.813 0 0 1-5.812 0C33.322 48.765 19.88 40.83 3.983 31.38 2.86 30.7 2 29.61 2 27.999c0-1.615.869-2.72 1.993-3.382a8052.42 8052.42 0 0 1 17.004-10.023l.05-.03a33723.375 33723.375 0 0 1 18.288-10.75l1.27-.746.334-.196.085-.05.022-.013.004-.002z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,53 @@
<template>
<div class="block-right-text">
<q-avatar :color="value ? 'positive' : 'negative'" text-color="white">
<q-icon v-if="value" name="eva-toggle-right" />
<q-icon v-if="!value" name="eva-toggle-left" />
</q-avatar>
<div v-bind:class="['block-right-text-label', `block-right-text-label-${!!value}`]">{{value ? 'True' : 'False'}}</div>
</div>
</template>
<script>
export default {
name: 'BooleanState',
props: ['value']
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.q-avatar{
font-size: 32px;
border-radius: 4px;
.q-icon {
font-size: 22px;
margin: 0 0 0 1px;
}
}
.block-right-text{
height: 32px;
line-height: 32px;
.q-avatar{
float: left;
}
&-label{
font-size: 14px;
font-weight: 600;
color: $app-text-grey;
float: left;
margin-left: 10px;
text-transform: capitalize;
&-true {
color: $positive;
}
&-false {
color: $negative;
}
}
}
</style>

View file

@ -0,0 +1,249 @@
<template>
<div class="table-wrapper">
<q-table
:data="data"
:columns="columns"
row-key="name"
:pagination.sync="syncPagination"
:rows-per-page-options="[10, 20, 40, 80, 100, 0]"
:loading="loading"
:filter="filter"
@request="request"
binary-state-sort
:visible-columns="visibleColumns"
color="primary"
table-header-class="table-header">
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer" @click.native="$router.push({ path: `/${getPath}/${props.row.name}`})">
<q-td key="status" :props="props" auto-width>
<avatar-state :state="props.row.status | status "/>
</q-td>
<q-td key="tls" :props="props" auto-width>
<t-l-s-state :is-t-l-s="props.row.tls"/>
</q-td>
<q-td key="rule" :props="props">
<q-chip
v-if="props.row.rule"
dense
class="app-chip app-chip-rule">
{{ props.row.rule }}
</q-chip>
</q-td>
<q-td key="entryPoints" :props="props">
<div v-if="props.row.using">
<q-chip
v-for="(entryPoints, index) in props.row.using" :key="index"
dense
class="app-chip app-chip-entry-points">
{{ entryPoints }}
</q-chip>
</div>
</q-td>
<q-td key="name" :props="props">
<q-chip
v-if="props.row.name"
dense
class="app-chip app-chip-name">
{{ props.row.name }}
</q-chip>
</q-td>
<q-td key="type" :props="props">
<q-chip
v-if="props.row.type"
dense
class="app-chip app-chip-entry-points">
{{ props.row.type }}
</q-chip>
</q-td>
<q-td key="servers" :props="props">
<span class="servers-label">{{ props.row | servers }}</span>
</q-td>
<q-td key="service" :props="props">
<q-chip
v-if="props.row.service"
dense
class="app-chip app-chip-service">
{{ props.row.service }}
</q-chip>
</q-td>
<q-td key="provider" :props="props" auto-width>
<q-avatar class="provider-logo">
<q-icon :name="`img:statics/providers/${props.row.provider}.svg`" />
</q-avatar>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</template>
<script>
import AvatarState from './AvatarState'
import TLSState from './TLSState'
export default {
name: 'MainTable',
props: ['data', 'request', 'loading', 'pagination', 'filter', 'type'],
components: {
TLSState,
AvatarState
},
data () {
return {
visibleColumnsHttpRouters: ['status', 'rule', 'entryPoints', 'name', 'service', 'tls', 'provider'],
visibleColumnsHttpServices: ['status', 'name', 'type', 'servers', 'provider'],
visibleColumnsHttpMiddlewares: ['status', 'name', 'type', 'provider'],
visibleColumns: ['status', 'name', 'provider'],
columns: [
{
name: 'status',
required: true,
label: 'Status',
align: 'left',
field: row => row.status
},
{
name: 'tls',
align: 'left',
label: 'TLS',
field: row => row
},
{
name: 'rule',
align: 'left',
label: 'Rule',
field: row => row.rule
},
{
name: 'entryPoints',
align: 'left',
label: 'Entrypoints',
field: row => row.entryPoints
},
{
name: 'name',
align: 'left',
label: 'Name',
field: row => row.name
},
{
name: 'type',
align: 'left',
label: 'Type',
field: row => row.type
},
{
name: 'servers',
align: 'right',
label: 'Servers',
field: row => row.servers
},
{
name: 'service',
align: 'left',
label: 'Service',
field: row => row.service
},
{
name: 'provider',
align: 'center',
label: 'Provider',
field: row => row.provider
}
]
}
},
computed: {
syncPagination: {
get () {
return this.pagination
},
set (newValue) {
this.$emit('update:pagination', newValue)
}
},
getPath () {
return this.type.replace('-', '/', 'gi')
}
},
filters: {
status (value) {
if (value === 'enabled') {
return 'positive'
}
if (value === 'disabled') {
return 'negative'
}
return value
},
servers (value) {
if (value.loadBalancer && value.loadBalancer.servers) {
return value.loadBalancer.servers.length
}
return 0
}
},
created () {
if (this.type === 'http-routers' || this.type === 'tcp-routers') {
this.visibleColumns = this.visibleColumnsHttpRouters
}
if (this.type === 'http-services' || this.type === 'tcp-services') {
this.visibleColumns = this.visibleColumnsHttpServices
}
if (this.type === 'http-middlewares') {
this.visibleColumns = this.visibleColumnsHttpMiddlewares
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.table-wrapper {
/deep/ .q-table__container{
border-radius: 8px;
.q-table {
.table-header {
th {
font-size: 14px;
font-weight: 700;
}
}
tbody {
tr:hover {
background: rgba( $accent, 0.04 );
}
}
}
.q-table__bottom {
> .q-table__control {
&:nth-last-child(2) {
display: none;
}
&:nth-last-child(1) {
.q-table__bottom-item {
display: none;
}
}
}
}
}
}
.servers-label{
font-size: 14px;
font-weight: 600;
}
.provider-logo {
width: 32px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
</style>

View file

@ -8,6 +8,8 @@
</div> </div>
<q-tabs align="left" inline-label indicator-color="transparent" active-color="white" stretch> <q-tabs align="left" inline-label indicator-color="transparent" active-color="white" stretch>
<q-route-tab to="/" icon="eva-home-outline" no-caps label="Dashboard" /> <q-route-tab to="/" icon="eva-home-outline" no-caps label="Dashboard" />
<q-route-tab to="/http" icon="eva-globe-outline" no-caps label="HTTP" />
<q-route-tab to="/tcp" icon="eva-globe-2-outline" no-caps label="TCP" />
</q-tabs> </q-tabs>
<q-space /> <q-space />
<q-btn type="a" :href="`https://docs.traefik.io/${parsedVersion}`" target="_blank" stretch flat no-caps label="Documentation" class="btn-menu" /> <q-btn type="a" :href="`https://docs.traefik.io/${parsedVersion}`" target="_blank" stretch flat no-caps label="Documentation" class="btn-menu" />

View file

@ -0,0 +1,125 @@
<template>
<q-card flat bordered v-bind:class="['panel-health-check', {'panel-health-check-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section v-if="data.scheme || data.interval">
<div class="row items-start no-wrap">
<div class="col" v-if="data.scheme">
<div class="text-subtitle2">SCHEME</div>
<q-chip
dense
class="app-chip app-chip-options">
{{ data.scheme }}
</q-chip>
</div>
<div class="col" v-if="data.interval">
<div class="text-subtitle2">INTERVAL</div>
<q-chip
dense
class="app-chip app-chip-interval">
{{ data.interval }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.path || data.timeout">
<div class="row items-start no-wrap">
<div class="col" v-if="data.path">
<div class="text-subtitle2">PATH</div>
<q-chip
dense
class="app-chip app-chip-entry-points">
{{ data.path }}
</q-chip>
</div>
<div class="col" v-if="data.timeout">
<div class="text-subtitle2">TIMEOUT</div>
<q-chip
dense
class="app-chip app-chip-interval">
{{ data.timeout }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.port || data.hostname">
<div class="row items-start no-wrap">
<div class="col" v-if="data.port">
<div class="text-subtitle2">PORT</div>
<q-chip
dense
class="app-chip app-chip-name">
{{ data.port }}
</q-chip>
</div>
<div class="col" v-if="data.hostname">
<div class="text-subtitle2">HOSTNAME</div>
<q-chip
dense
class="app-chip app-chip-rule">
{{ data.hostname }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.headers">
<div class="row items-start">
<div class="col-12">
<div class="text-subtitle2">HEADERS</div>
</div>
<div v-for="(header, index) in data.headers" :key="index" class="col-12">
<q-chip
dense
class="app-chip app-chip-wrap app-chip-service">
{{ index }}: {{ header }}
</q-chip>
</div>
</div>
</q-card-section>
</q-scroll-area>
</q-card>
</template>
<script>
export default {
name: 'PanelHealthCheck',
props: ['data', 'dense'],
components: {
},
computed: {
isDense () {
return this.dense !== undefined
}
},
filters: {
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-health-check {
height: 600px;
&-dense {
height: 400px;
}
.q-card__section {
padding: 24px;
+ .q-card__section {
padding-top: 0;
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
}
</style>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,133 @@
<template>
<q-card flat bordered v-bind:class="['panel-services', {'panel-services-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section>
<div class="row items-start no-wrap">
<div class="col-6">
<div class="text-subtitle2 text-table">Name</div>
</div>
<div class="col-3">
<div class="text-subtitle2 text-table" style="text-align: right">Percent</div>
</div>
<div class="col-3">
<div class="text-subtitle2 text-table" style="text-align: right">Provider</div>
</div>
</div>
</q-card-section>
<q-separator />
<div v-for="(service, index) in data.mirroring.mirrors" :key="index">
<q-card-section>
<div class="row items-center no-wrap">
<div class="col-6">
<q-chip
dense
class="app-chip app-chip-rule">
{{ service.name }}
</q-chip>
</div>
<div class="col-3 text-right">
{{ service.percent }}
</div>
<div class="col-3 text-right">
<q-avatar class="provider-logo">
<q-icon :name="`img:statics/providers/${getProvider(service)}.svg`" />
</q-avatar>
</div>
</div>
</q-card-section>
<q-separator />
</div>
</q-scroll-area>
</q-card>
</template>
<script>
export default {
name: 'PanelMirroringServices',
props: ['data', 'dense'],
components: {},
computed: {
isDense () {
return this.dense !== undefined
}
},
methods: {
getProvider (service) {
const words = service.name.split('@')
if (words.length !== 2) {
return this.provider
}
return words[1]
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-services {
height: 600px;
&-dense{
height: 400px;
}
.q-card__section {
padding: 12px 24px;
+ .q-card__section {
padding-top: 0;
}
}
.block-right-text{
height: 32px;
line-height: 32px;
&-label{
font-size: 14px;
font-weight: 600;
color: $app-text-grey;
float: left;
margin-left: 10px;
text-transform: capitalize;
&-enabled {
color: $positive;
}
&-disabled {
color: $negative;
}
&-warning {
color: $warning;
}
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
.text-table {
font-size: 14px;
font-weight: 700;
letter-spacing: normal;
text-transform: none;
}
.provider-logo {
width: 32px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
}
</style>

View file

@ -0,0 +1,204 @@
<template>
<q-card flat bordered v-bind:class="['panel-router-details']">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">STATUS</div>
<div class="block-right-text">
<avatar-state :state="data.status | status "/>
<div v-bind:class="['block-right-text-label', `block-right-text-label-${data.status}`]">{{data.status | statusLabel}}</div>
</div>
</div>
<div class="col">
<div class="text-subtitle2">PROVIDER</div>
<div class="block-right-text">
<q-avatar class="provider-logo">
<q-icon :name="`img:statics/providers/${data.provider}.svg`" />
</q-avatar>
<div class="block-right-text-label">{{data.provider}}</div>
</div>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.rule">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">RULE</div>
<q-chip
dense
class="app-chip app-chip-rule">
{{ data.rule }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.name">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">NAME</div>
<q-chip
dense
class="app-chip app-chip-name">
{{ data.name }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.using">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">ENTRYPOINTS</div>
<q-chip
v-for="(entryPoint, index) in data.using" :key="index"
dense
class="app-chip app-chip-entry-points">
{{ entryPoint }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.service">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">SERVICE</div>
<q-chip
dense
clickable
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
class="app-chip app-chip-service">
{{ data.service }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.error">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">ERRORS</div>
<q-chip
v-for="(errorMsg, index) in data.error" :key="index"
class="app-chip app-chip-error">
{{ errorMsg }}
</q-chip>
</div>
</div>
</q-card-section>
</q-scroll-area>
</q-card>
</template>
<script>
import AvatarState from './AvatarState'
export default {
name: 'PanelRouterDetails',
props: ['data', 'protocol'],
components: {
AvatarState
},
methods: {
getServiceId () {
const words = this.data.service.split('@')
if (words.length === 2) {
return this.data.service
}
return `${this.data.service}@${this.data.provider}`
}
},
filters: {
status (value) {
if (value === 'enabled') {
return 'positive'
}
if (value === 'disabled') {
return 'negative'
}
return value
},
statusLabel (value) {
if (value === 'enabled') {
return 'success'
}
if (value === 'disabled') {
return 'error'
}
return value
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-router-details {
height: 600px;
.q-card__section {
padding: 24px;
+ .q-card__section {
padding-top: 0;
}
}
.block-right-text{
height: 32px;
line-height: 32px;
.q-avatar{
float: left;
}
&-label{
font-size: 14px;
font-weight: 600;
color: $app-text-grey;
float: left;
margin-left: 10px;
text-transform: capitalize;
&-enabled {
color: $positive;
}
&-disabled {
color: $negative;
}
&-warning {
color: $warning;
}
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
.app-chip {
&-error {
display: flex;
height: 100%;
flex-wrap: wrap;
border-width: 0;
margin-bottom: 8px;
/deep/ .q-chip__content{
white-space: normal;
}
}
}
.provider-logo {
width: 32px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
}
</style>

View file

@ -0,0 +1,123 @@
<template>
<q-card flat bordered v-bind:class="['panel-servers', {'panel-servers-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section>
<div class="row items-start no-wrap">
<div class="col-3">
<div class="text-subtitle2 text-table">Status</div>
</div>
<div class="col-9">
<div class="text-subtitle2 text-table">URL</div>
</div>
</div>
</q-card-section>
<q-separator />
<div v-for="(server, index) in data.loadBalancer.servers" :key="index">
<q-card-section>
<div class="row items-center no-wrap">
<div class="col-3">
<div class="block-right-text">
<avatar-state v-if="data.serverStatus" :state="data.serverStatus[server.url || server.address] | status "/>
<avatar-state v-if="!data.serverStatus" :state="'DOWN' | status"/>
</div>
</div>
<div class="col-9">
<q-chip
dense
class="app-chip app-chip-rule">
{{ server.url || server.address}}
</q-chip>
</div>
</div>
</q-card-section>
<q-separator />
</div>
</q-scroll-area>
</q-card>
</template>
<script>
import AvatarState from './AvatarState'
export default {
name: 'PanelServers',
props: ['data', 'dense'],
components: {
AvatarState
},
computed: {
isDense () {
return this.dense !== undefined
}
},
filters: {
status (value) {
if (value === 'UP') {
return 'positive'
}
return 'negative'
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-servers {
height: 600px;
&-dense{
height: 400px;
}
.q-card__section {
padding: 12px 24px;
+ .q-card__section {
padding-top: 0;
}
}
.block-right-text{
height: 32px;
line-height: 32px;
.q-avatar{
float: left;
}
&-label{
font-size: 14px;
font-weight: 600;
color: $app-text-grey;
float: left;
margin-left: 10px;
text-transform: capitalize;
&-enabled {
color: $positive;
}
&-disabled {
color: $negative;
}
&-warning {
color: $warning;
}
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
.text-table {
font-size: 14px;
font-weight: 700;
letter-spacing: normal;
text-transform: none;
}
}
</style>

View file

@ -0,0 +1,192 @@
<template>
<q-card flat bordered v-bind:class="['panel-service-details', {'panel-service-details-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">TYPE</div>
<q-chip
dense
class="app-chip app-chip-entry-points">
{{ data.type }}
</q-chip>
</div>
<div class="col">
<div class="text-subtitle2">PROVIDER</div>
<div class="block-right-text">
<q-avatar class="provider-logo">
<q-icon :name="`img:statics/providers/${data.provider}.svg`" />
</q-avatar>
<div class="block-right-text-label">{{data.provider}}</div>
</div>
</div>
</div>
</q-card-section>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">STATUS</div>
<div class="block-right-text">
<avatar-state :state="data.status | status "/>
<div v-bind:class="['block-right-text-label', `block-right-text-label-${data.status}`]">{{data.status | statusLabel}}</div>
</div>
</div>
</div>
</q-card-section>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col" v-if="data.mirroring">
<div class="text-subtitle2">Main Service</div>
<q-chip
dense
class="app-chip app-chip-name">
{{ data.mirroring.service }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section >
<div class="row items-start no-wrap">
<div class="col" v-if="data.name">
<div class="text-subtitle2">Pass Host Header</div>
<boolean-state :value="data.passHostHeader"/>
</div>
</div>
</q-card-section>
<q-separator v-if="data.weighted && data.weighted.sticky" />
<q-card-section v-if="data.weighted && data.weighted.sticky" >
<div class="row items-start no-wrap">
<div class="text-subtitle1">Sticky: Cookie</div>
</div>
</q-card-section>
<q-card-section v-if="data.weighted && data.weighted.sticky" >
<div class="row items-start no-wrap">
<div class="col" v-if="data.weighted.sticky.cookie && data.weighted.sticky.cookie.name">
<div class="text-subtitle2">NAME</div>
<q-chip
dense
class="app-chip app-chip-entry-points">
{{ data.weighted.sticky.cookie.name }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.weighted && data.weighted.sticky" >
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">SECURE</div>
<boolean-state :value="data.weighted.sticky.cookie.secure"/>
</div>
<div class="col">
<div class="text-subtitle2">HTTP Only</div>
<boolean-state :value="data.weighted.sticky.cookie.httpOnly"/>
</div>
</div>
</q-card-section>
</q-scroll-area>
</q-card>
</template>
<script>
import AvatarState from './AvatarState'
import BooleanState from './BooleanState'
export default {
name: 'PanelServiceDetails',
props: ['data', 'dense'],
components: {
BooleanState,
AvatarState
},
computed: {
isDense () {
return this.dense !== undefined
}
},
filters: {
status (value) {
if (value === 'enabled') {
return 'positive'
}
if (value === 'disabled') {
return 'negative'
}
return value || 'negative'
},
statusLabel (value) {
if (value === 'enabled') {
return 'success'
}
if (value === 'disabled') {
return 'error'
}
return value || 'error'
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-service-details {
height: 600px;
&-dense{
height: 400px;
}
.q-card__section {
padding: 24px;
+ .q-card__section {
padding-top: 0;
}
}
.block-right-text{
height: 32px;
line-height: 32px;
.q-avatar{
float: left;
}
&-label{
font-size: 14px;
font-weight: 600;
color: $app-text-grey;
float: left;
margin-left: 10px;
text-transform: capitalize;
&-enabled {
color: $positive;
}
&-disabled {
color: $negative;
}
&-warning {
color: $warning;
}
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
.provider-logo {
width: 32px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
}
</style>

View file

@ -0,0 +1,141 @@
<template>
<q-card flat bordered v-bind:class="['panel-tls']">
<q-scroll-area v-if="data" :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section v-if="data">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">TLS</div>
<boolean-state :value="!!data"/>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.options">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">OPTIONS</div>
<q-chip
dense
class="app-chip app-chip-options">
{{ data.options }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="protocol == 'tcp'">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">PASSTHROUGH</div>
<boolean-state :value="data.passthrough"></boolean-state>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.certResolver">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">CERTIFICATE RESOLVER</div>
<q-chip
dense
class="app-chip app-chip-service">
{{ data.certResolver }}
</q-chip>
</div>
</div>
</q-card-section>
<q-card-section v-if="data.domains">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">DOMAINS</div>
<div v-for="(domain, key) in data.domains" :key="key" class="flex">
<q-chip
dense
class="app-chip app-chip-rule">
{{ domain.main }}
</q-chip>
<q-chip
v-for="(domain, key) in domain.sans" :key="key"
dense
class="app-chip app-chip-entry-points">
{{ domain }}
</q-chip>
</div>
</div>
</div>
</q-card-section>
</q-scroll-area>
<q-card-section v-else style="height: 100%">
<div class="row items-center" style="height: 100%">
<div class="col-12">
<div class="block-empty"></div>
<div class="q-pb-lg block-empty-logo">
<img alt="empty" src="~assets/middlewares-empty.svg">
</div>
<div class="block-empty-label">There are no<br>TLS configured</div>
</div>
</div>
</q-card-section>
</q-card>
</template>
<script>
import BooleanState from './BooleanState'
export default {
name: 'PanelTLS',
components: {
BooleanState
},
props: ['data', 'protocol']
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-tls {
height: 600px;
.q-card__section {
padding: 24px;
+ .q-card__section {
padding-top: 0;
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
.app-chip {
&-entry-points {
display: flex;
height: 100%;
flex-wrap: wrap;
border-width: 0;
margin-bottom: 8px;
/deep/ .q-chip__content{
white-space: normal;
}
}
}
.block-empty {
&-logo {
text-align: center;
}
&-label {
font-size: 20px;
font-weight: 700;
color: #b8b8b8;
text-align: center;
line-height: 1.2;
}
}
}
</style>

View file

@ -0,0 +1,124 @@
<template>
<q-card flat bordered v-bind:class="['panel-services', {'panel-services-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card-section>
<div class="row items-start no-wrap">
<div class="col-7">
<div class="text-subtitle2 text-table">Name</div>
</div>
<div class="col-3">
<div class="text-subtitle2 text-table">Weight</div>
</div>
<div class="col-4">
<div class="text-subtitle2 text-table">Provider</div>
</div>
</div>
</q-card-section>
<q-separator />
<div v-for="(service, index) in data.weighted.services" :key="index">
<q-card-section>
<div class="row items-center no-wrap">
<div class="col-7">
<q-chip
dense
class="app-chip app-chip-rule">
{{ service.name }}
</q-chip>
</div>
<div class="col-3">
{{ service.weight }}
</div>
<div class="col-4">
<q-avatar>
<q-icon :name="`img:statics/providers/${getProvider(service)}.svg`" />
</q-avatar>
</div>
</div>
</q-card-section>
<q-separator />
</div>
</q-scroll-area>
</q-card>
</template>
<script>
export default {
name: 'PanelWeightedServices',
props: ['data', 'dense'],
components: {},
computed: {
isDense () {
return this.dense !== undefined
}
},
methods: {
getProvider (service) {
const words = service.name.split('@')
if (words.length !== 2) {
return this.provider
}
return words[1]
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.panel-services {
height: 600px;
&-dense{
height: 400px;
}
.q-card__section {
padding: 12px 24px;
+ .q-card__section {
padding-top: 0;
}
}
.block-right-text{
height: 32px;
line-height: 32px;
&-label{
font-size: 14px;
font-weight: 600;
color: $app-text-grey;
float: left;
margin-left: 10px;
text-transform: capitalize;
&-enabled {
color: $positive;
}
&-disabled {
color: $negative;
}
&-warning {
color: $warning;
}
}
}
.text-subtitle2 {
font-size: 11px;
color: $app-text-grey;
line-height: 16px;
margin-bottom: 4px;
text-align: left;
letter-spacing: 2px;
font-weight: 600;
text-transform: uppercase;
}
.text-table {
font-size: 14px;
font-weight: 700;
letter-spacing: normal;
text-transform: none;
}
}
</style>

View file

@ -0,0 +1,26 @@
<template>
<q-avatar text-color="dark">
<q-icon v-if="isTLS" name="eva-shield" />
</q-avatar>
</template>
<script>
export default {
name: 'TLSState',
props: ['isTLS']
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.q-avatar{
font-size: 32px;
border-radius: 4px;
color: green;
.q-icon {
font-size: 22px;
margin: 0 0 0 1px;
}
}
</style>

View file

@ -0,0 +1,113 @@
<template>
<q-toolbar class="row no-wrap items-center">
<q-tabs align="left" inline-label indicator-color="transparent" stretch>
<q-route-tab :to="`/${protocol}/routers`" no-caps :label="`${protocolLabel} Routers`">
<q-badge v-if="routerTotal !== 0" align="middle" :label="routerTotal" class="q-ml-sm"/>
</q-route-tab>
<q-route-tab :to="`/${protocol}/services`" no-caps :label="`${protocolLabel} Services`">
<q-badge v-if="servicesTotal !== 0" align="middle" :label="servicesTotal" class="q-ml-sm"/>
</q-route-tab>
<q-route-tab v-if="protocol === 'http'" :to="`/${protocol}/middlewares`" no-caps :label="`${protocolLabel} Middlewares`">
<q-badge v-if="middlewaresTotal !== 0" align="middle" :label="middlewaresTotal" class="q-ml-sm"/>
</q-route-tab>
</q-tabs>
</q-toolbar>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'ToolBar',
data () {
return {
loadingOverview: true
}
},
computed: {
...mapGetters('core', { overviewAll: 'allOverview' }),
protocol () {
return this.$route.meta.protocol
},
protocolLabel () {
return this.protocol.toUpperCase()
},
routerTotal () {
const data = this.overviewAll.items && this.overviewAll.items[`${this.protocol}`]
return (data && data.routers && data.routers.total) || 0
},
servicesTotal () {
const data = this.overviewAll.items && this.overviewAll.items[`${this.protocol}`]
return (data && data.services && data.services.total) || 0
},
middlewaresTotal () {
const data = this.overviewAll.items && this.overviewAll.items[`${this.protocol}`]
return (data && data.middlewares && data.middlewares.total) || 0
}
},
methods: {
...mapActions('core', { getOverview: 'getOverview' }),
refreshAll () {
this.onGetAll()
},
onGetAll () {
this.getOverview()
.then(body => {
console.log('Success -> toolbar/overview', body)
if (!body) {
this.loadingOverview = false
}
})
.catch(error => {
console.log('Error -> toolbar/overview', error)
})
}
},
created () {
this.refreshAll()
},
beforeDestroy () {
this.$store.commit('core/getOverviewClear')
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.q-toolbar {
min-height: 48px;
padding: 0;
}
.q-tabs {
/deep/ .q-tabs__content {
.q-tab__label {
color: $app-text-grey;
font-size: 16px;
font-weight: 700;
}
.q-badge {
font-size: 13px;
font-weight: 700;
border-radius: 12px;
text-align: center;
align-items: center;
justify-content: center;
min-width: 30px;
padding: 6px;
color: $app-text-grey;
background-color: rgba( $app-text-grey, .1 );
}
.q-tab--active {
.q-tab__label {
color: $accent;
}
.q-badge {
color: $accent;
background-color: rgba( $accent, .1 );
}
}
}
}
</style>

View file

@ -0,0 +1,92 @@
<template>
<q-toolbar class="row no-wrap items-center">
<q-btn-toggle
v-model="getStatus"
class="bar-toggle"
toggle-color="app-toggle"
text-color="app-grey"
size="14px"
no-caps
rounded
unelevated
:options="[
{label: 'All Status', value: ''},
{label: 'Success', value: 'enabled'},
{label: 'Warnings', value: 'warning'},
{label: 'Errors', value: 'disabled'}
]"
/>
<q-space />
<q-input v-model="getFilter" rounded dense outlined type="search" debounce="500" placeholder="Search" bg-color="white" class="bar-search">
<template v-slot:append>
<q-icon name="eva-search-outline" />
</template>
</q-input>
</q-toolbar>
</template>
<script>
export default {
name: 'ToolBarTable',
props: ['status', 'filter'],
components: {
},
data () {
return {
}
},
computed: {
getStatus: {
get () {
return this.status
},
set (newValue) {
this.$emit('update:status', newValue)
}
},
getFilter: {
get () {
return this.filter
},
set (newValue) {
this.$emit('update:filter', newValue)
}
}
},
methods: {
},
created () {
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.q-toolbar {
padding: 0;
/deep/ .bar-toggle {
.q-btn {
font-weight: 600;
margin-right: 12px;
&.q-btn--rounded {
border-radius: 12px;
}
&.bg-app-toggle {
color: $accent !important;
}
}
}
/deep/ .bar-search {
.q-field__inner {
.q-field__control {
border-radius: 12px;
}
}
}
}
</style>

View file

@ -5,6 +5,9 @@
<div class="col"> <div class="col">
<div class="text-h6 text-weight-bold">{{getName}}</div> <div class="text-h6 text-weight-bold">{{getName}}</div>
</div> </div>
<div class="col-auto">
<q-btn :to="getUrl" color="accent" dense flat icon-right="eva-arrow-forward-outline" no-caps label="Explore" size="md" class="text-weight-bold"/>
</div>
</div> </div>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
@ -104,7 +107,7 @@ export default {
} else { } else {
result = num result = num
} }
return isNaN(result) ? 0 : result return isNaN(result) || result < 0 ? 0 : result
}, },
getWarnings (inPercent = false) { getWarnings (inPercent = false) {
const num = this.data.warnings const num = this.data.warnings
@ -114,7 +117,7 @@ export default {
} else { } else {
result = num result = num
} }
return isNaN(result) ? 0 : result return isNaN(result) || result < 0 ? 0 : result
}, },
getErrors (inPercent = false) { getErrors (inPercent = false) {
const num = this.data.errors const num = this.data.errors
@ -124,7 +127,7 @@ export default {
} else { } else {
result = num result = num
} }
return isNaN(result) ? 0 : result return isNaN(result) || result < 0 ? 0 : result
}, },
getData () { getData () {
return [this.getSuccess(), this.getWarnings(), this.getErrors()] return [this.getSuccess(), this.getWarnings(), this.getErrors()]

View file

@ -1,9 +1,9 @@
<template> <template>
<q-card flat bordered> <q-card flat bordered v-bind:class="['panel-entry', {'panel-entry-detail':type === 'detail'}, {'panel-entry-focus':focus}, {'panel-entry-ex-size':exSize}]">
<q-card-section> <q-card-section>
<div class="row items-center no-wrap"> <div class="row items-center no-wrap">
<div class="col"> <div class="col">
<div class="text-subtitle2 text-uppercase text-center text-app-grey" style="letter-spacing: 3px;">{{name}}</div> <div class="text-subtitle2">{{name}}</div>
</div> </div>
</div> </div>
</q-card-section> </q-card-section>
@ -16,10 +16,41 @@
<script> <script>
export default { export default {
name: 'PanelEntry', name: 'PanelEntry',
props: ['address', 'name'] props: ['address', 'name', 'type', 'focus', 'exSize']
} }
</script> </script>
<style scoped> <style scoped lang="scss">
@import "../../css/sass/variables";
.panel-entry {
.text-subtitle2 {
font-weight: 600;
letter-spacing: 3px;
color: $app-text-grey;
text-transform: uppercase;
text-align: center;
}
&-detail{
.text-subtitle2 {
font-size: 11px;
font-weight: 600;
line-height: 11px;
text-align: left;
}
.text-h3 {
font-size: 16px;
text-align: left;
line-height: 16px;
}
}
&-focus {
border: solid 2px $accent;
}
&-ex-size{
.text-h3 {
font-size: 22px;
}
}
}
</style> </style>

View file

@ -1,18 +1,15 @@
<template> <template>
<q-card flat bordered> <q-card flat bordered v-bind:class="['panel-feature']">
<q-card-section> <q-card-section>
<div class="row items-center no-wrap"> <div class="row items-center no-wrap">
<div class="col"> <div class="col">
<div class="text-subtitle2 text-uppercase text-center text-app-grey" style="letter-spacing: 3px;">{{featureKey}}</div> <div class="text-subtitle2">{{featureKey}}</div>
</div> </div>
</div> </div>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
<div class="text-h3 text-center text-weight-bold"> <div class="text-h3 text-center text-weight-bold">
<q-chip <q-chip
outline
color="primary"
text-color="white"
v-bind:class="['feature-chip', {'feature-chip-string':isString}, {'feature-chip-boolean':isBoolean}, {'feature-chip-boolean-true':isTrue}]"> v-bind:class="['feature-chip', {'feature-chip-string':isString}, {'feature-chip-boolean':isBoolean}, {'feature-chip-boolean-true':isTrue}]">
{{getVal}} {{getVal}}
</q-chip> </q-chip>
@ -51,26 +48,37 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
@import "../../css/sass/variables"; @import "../../css/sass/variables";
.panel-feature {
.text-subtitle2 {
font-weight: 600;
letter-spacing: 3px;
color: $app-text-grey;
text-transform: uppercase;
text-align: center;
}
}
.feature-chip { .feature-chip {
border-radius: 12px; border-radius: 12px;
border-width: 2px; border-width: 2px;
height: 56px; height: 56px;
padding: 12px 24px; padding: 12px 24px;
color: $primary;
&-string{ &-string{
border-color: $app-text-grey; border-color: $app-text-grey;
font-size: 24px; font-size: 20px;
color: $app-text-grey !important; color: $app-text-grey;
background-color: rgba( $app-text-grey, .1 ); background-color: rgba( $app-text-grey, .1 );
} }
&-boolean{ &-boolean{
font-size: 40px; font-size: 40px;
font-weight: 700; font-weight: 700;
border-color: $negative; border-color: $negative;
color: $negative !important; color: $negative;
background-color: rgba( $negative, .1 ); background-color: rgba( $negative, .1 );
&-true{ &-true{
border-color: $positive; border-color: $positive;
color: $positive !important; color: $positive;
background-color: rgba( $positive, .1 ); background-color: rgba( $positive, .1 );
} }
} }

View file

@ -1,65 +0,0 @@
<template>
<q-toolbar class="row no-wrap items-center">
<q-tabs align="left" inline-label indicator-color="transparent" stretch>
<q-route-tab :to="`/http/routers`" no-caps label="HTTP Routers">
<q-badge align="middle" label="10" class="q-ml-sm"/>
</q-route-tab>
<q-route-tab :to="`/http/services`" no-caps label="HTTP Services">
<q-badge align="middle" label="12" class="q-ml-sm"/>
</q-route-tab>
<q-route-tab :to="`/http/middlewares`" no-caps label="HTTP Middlewares">
<q-badge align="middle" label="8" class="q-ml-sm"/>
</q-route-tab>
</q-tabs>
</q-toolbar>
</template>
<script>
export default {
name: 'HTTPToolBar',
data () {
return {
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.q-toolbar {
min-height: 48px;
padding: 0;
}
.q-tabs {
/deep/ .q-tabs__content {
.q-tab__label {
color: $app-text-grey;
font-size: 16px;
font-weight: 700;
}
.q-badge {
font-size: 13px;
font-weight: 700;
border-radius: 12px;
text-align: center;
align-items: center;
justify-content: center;
min-width: 30px;
padding: 6px;
color: $app-text-grey;
background-color: rgba( $app-text-grey, .1 );
}
.q-tab--active {
.q-tab__label {
color: $accent;
}
.q-badge {
color: $accent;
background-color: rgba( $accent, .1 );
}
}
}
}
</style>

View file

@ -1,62 +0,0 @@
<template>
<q-toolbar class="row no-wrap items-center">
<q-tabs align="left" inline-label indicator-color="transparent" stretch>
<q-route-tab :to="`/tcp/routers`" no-caps label="TCP Routers">
<q-badge align="middle" label="10" class="q-ml-sm"/>
</q-route-tab>
<q-route-tab :to="`/tcp/services`" no-caps label="TCP Services">
<q-badge align="middle" label="12" class="q-ml-sm"/>
</q-route-tab>
</q-tabs>
</q-toolbar>
</template>
<script>
export default {
name: 'TCPToolBar',
data () {
return {
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.q-toolbar {
min-height: 48px;
padding: 0;
}
.q-tabs {
/deep/ .q-tabs__content {
.q-tab__label {
color: $app-text-grey;
font-size: 16px;
font-weight: 700;
}
.q-badge {
font-size: 13px;
font-weight: 700;
border-radius: 12px;
text-align: center;
align-items: center;
justify-content: center;
min-width: 30px;
padding: 6px;
color: $app-text-grey;
background-color: rgba( $app-text-grey, .1 );
}
.q-tab--active {
.q-tab__label {
color: $accent;
}
.q-badge {
color: $accent;
background-color: rgba( $accent, .1 );
}
}
}
}
</style>

View file

@ -12,6 +12,10 @@ $app-text-caption-dark: rgba(255,255,255,0.7);
// Custom colors // Custom colors
$app-text-white: #ffffff; $app-text-white: #ffffff;
$app-text-grey: #737373; $app-text-grey: #737373;
$app-text-purple: #9d0fb0;
$app-text-green: #24a1c1;
$app-text-green-2: #06a21d;
$app-text-marine: #06102a;
// Quasar // Quasar
$primary: #06102a; $primary: #06102a;

View file

@ -39,6 +39,10 @@ body {
color: $app-text-grey; color: $app-text-grey;
} }
.bg-app-toggle {
background-color: rgba( $accent, .1 );
}
// Helps // Helps
.xs-text-center { .xs-text-center {
@include respond-to(sm, max) { @include respond-to(sm, max) {
@ -60,6 +64,10 @@ body {
&-label { &-label {
font-size: inherit; font-size: inherit;
font-weight: inherit; font-weight: inherit;
&-sub {
font-size: 16px;
font-weight: 600;
}
} }
.q-icon + &-label { .q-icon + &-label {
margin-left: 8px; margin-left: 8px;
@ -107,3 +115,45 @@ body {
.q-card { .q-card {
border-radius: 8px; border-radius: 8px;
} }
// Chips
.app-chip {
border-radius: 8px;
font-weight: 600;
font-size: 14px;
&-wrap {
height: 100%;
flex-wrap: wrap;
.q-chip__content{
white-space: normal;
}
}
&-accent, &-rule {
color: $accent;
background-color: rgba($accent, 0.1);
}
&-green, &-entry-points {
color: $app-text-green;
background-color: rgba($app-text-green, 0.1);
}
&-purple, &-name {
color: $app-text-purple;
background-color: rgba($app-text-purple, 0.1);
}
&-warning, &-service {
color: $warning;
background-color: rgba($warning, 0.1);
}
&-negative, &-error {
color: $negative;
background-color: rgba($negative, 0.1);
}
&-green-2, &-options {
color: $app-text-green-2;
background-color: rgba($app-text-green-2, 0.1);
}
&-marine, &-interval {
color: $app-text-marine;
background-color: rgba($app-text-grey, 0.1);
}
}

View file

@ -0,0 +1,161 @@
<template>
<page-default>
<section v-if="!loading" class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-sm">
<div v-if="middlewareByName.item" class="row no-wrap items-center app-title">
<div class="app-title-label" style="font-size: 26px">{{ middlewareByName.item.name }}</div>
</div>
</div>
</section>
<section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-sm q-pb-lg">
<div v-if="!loading" class="row items-start q-col-gutter-md">
<div v-if="middlewareByName.item" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-middlewares dense :data="[middlewareByName.item]" />
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else class="row items-start q-mt-xl">
<div class="col-12">
<p v-for="n in 4" :key="n" class="flex">
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/>
</p>
</div>
</div>
</div>
</section>
<section v-if="!loading && allRouters.length" class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl">
<div class="row no-wrap items-center q-mb-lg app-title">
<div class="app-title-label">Used by Routers</div>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allRouters" :request="()=>{}" :loading="routersLoading" :pagination.sync="routersPagination" :filter="routersFilter" :type="`${protocol}-routers`"/>
</div>
</div>
</div>
</section>
</page-default>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import SkeletonBox from '../../components/_commons/SkeletonBox'
import PanelMiddlewares from '../../components/_commons/PanelMiddlewares'
import MainTable from '../../components/_commons/MainTable'
export default {
name: 'PageMiddlewareDetail',
props: ['name', 'type'],
components: {
PageDefault,
SkeletonBox,
PanelMiddlewares,
MainTable
},
data () {
return {
loading: true,
timeOutGetAll: null,
allRouters: [],
routersLoading: true,
routersFilter: '',
routersStatus: '',
routersPagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 1000,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('http', { http_middlewareByName: 'middlewareByName' }),
protocol () {
return this.$route.meta.protocol
},
middlewareByName () {
return this[`${this.protocol}_middlewareByName`]
},
getMiddlewareByName () {
return this[`${this.protocol}_getMiddlewareByName`]
},
getRouterByName () {
return this[`${this.protocol}_getRouterByName`]
}
},
methods: {
...mapActions('http', { http_getMiddlewareByName: 'getMiddlewareByName', http_getRouterByName: 'getRouterByName' }),
refreshAll () {
if (this.middlewareByName.loading) {
return
}
this.onGetAll()
},
onGetAll () {
this.getMiddlewareByName(this.name)
.then(body => {
if (!body) {
this.loading = false
return
}
// Get routers
if (body.usedBy) {
for (const router in body.usedBy) {
if (body.usedBy.hasOwnProperty(router)) {
this.getRouterByName(body.usedBy[router])
.then(body => {
if (body) {
this.routersLoading = false
this.allRouters.push(body)
}
})
.catch(error => {
console.log('Error -> routers/byName', error)
})
}
}
}
clearTimeout(this.timeOutGetAll)
this.timeOutGetAll = setTimeout(() => {
this.loading = false
}, 300)
})
.catch(error => {
console.log('Error -> middleware/byName', error)
})
}
},
created () {
this.refreshAll()
},
mounted () {
},
beforeDestroy () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getMiddlewareByNameClear')
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
</style>

View file

@ -0,0 +1,293 @@
<template>
<page-default>
<section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div v-if="!loading" class="row items-start">
<div v-if="entryPoints.length" class="col-12 col-md-3 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-log-in-outline"></q-icon>
<div class="app-title-label">Entrypoints</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md">
<div v-for="(entryPoint, index) in entryPoints" :key="index" class="col-12">
<panel-entry type="detail" exSize="true" :name="entryPoint.name" :address="entryPoint.address"/>
</div>
</div>
</div>
<div class="col-12 col-md-4 xs-hide sm-hide">
<q-icon name="eva-arrow-forward-outline" class="arrow"></q-icon>
</div>
</div>
</div>
<div v-if="routerByName.item.name" class="col-12 col-md-3 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon>
<div class="app-title-label">{{ routerType }}</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-entry focus="true" type="detail" name="router" :address="routerByName.item.name"/>
</div>
</div>
</div>
<div class="col-12 col-md-4 xs-hide sm-hide">
<q-icon name="eva-arrow-forward-outline" class="arrow"></q-icon>
</div>
</div>
</div>
<div v-if="hasMiddlewares" class="col-12 col-md-3 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-layers"></q-icon>
<div class="app-title-label">HTTP Middlewares</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md">
<div v-for="(middleware, index) in middlewares" :key="index" class="col-12">
<panel-entry type="detail" name="Middleware" :address="middleware.type"/>
</div>
</div>
</div>
<div class="col-12 col-md-4 xs-hide sm-hide">
<q-icon name="eva-arrow-forward-outline" class="arrow"></q-icon>
</div>
</div>
</div>
<div v-if="routerByName.item.service" class="col-12 col-md-3 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-flash"></q-icon>
<div class="app-title-label">Service</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-entry type="detail" name="Service" :address="routerByName.item.service"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else class="row items-start">
<div class="col-12">
<p v-for="n in 4" :key="n" class="flex">
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/>
</p>
</div>
</div>
</div>
</section>
<section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div v-if="!loading" class="row items-start q-col-gutter-md">
<div v-if="routerByName.item" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-info"></q-icon>
<div class="app-title-label">Router Details</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-router-details :data="routerByName.item" :protocol="protocol"/>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-shield"></q-icon>
<div class="app-title-label">TLS</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-t-l-s :data="routerByName.item.tls" :protocol="protocol"/>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4 q-mb-lg path-block" v-if="protocol === 'http'">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-layers"></q-icon>
<div class="app-title-label">Middlewares</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-middlewares :data="middlewares"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else class="row items-start">
<div class="col-12">
<p v-for="n in 4" :key="n" class="flex">
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/>
</p>
</div>
</div>
</div>
</section>
</page-default>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import SkeletonBox from '../../components/_commons/SkeletonBox'
import PanelEntry from '../../components/dashboard/PanelEntry'
import PanelRouterDetails from '../../components/_commons/PanelRouterDetails'
import PanelTLS from '../../components/_commons/PanelTLS'
import PanelMiddlewares from '../../components/_commons/PanelMiddlewares'
export default {
name: 'PageRouterDetail',
props: ['name', 'type'],
components: {
PageDefault,
SkeletonBox,
PanelEntry,
PanelRouterDetails,
PanelTLS,
PanelMiddlewares
},
data () {
return {
loading: true,
entryPoints: [],
middlewares: [],
timeOutGetAll: null
}
},
computed: {
hasTLSConfiguration () {
return this.routerByName.item.tls
},
routerType () {
return this.$route.meta.protocol.toUpperCase() + ' Router'
},
...mapGetters('http', { http_routerByName: 'routerByName' }),
...mapGetters('tcp', { tcp_routerByName: 'routerByName' }),
hasMiddlewares () {
return this.$route.meta.protocol === 'http' && this.middlewares.length > 0
},
protocol () {
return this.$route.meta.protocol
},
routerByName () {
return this[`${this.protocol}_routerByName`]
},
getRouterByName () {
return this[`${this.protocol}_getRouterByName`]
}
},
methods: {
...mapActions('http', { http_getRouterByName: 'getRouterByName', getMiddlewareByName: 'getMiddlewareByName' }),
...mapActions('tcp', { tcp_getRouterByName: 'getRouterByName' }),
...mapActions('entrypoints', { getEntrypointsByName: 'getByName' }),
refreshAll () {
if (this.routerByName.loading) {
return
}
this.onGetAll()
},
onGetAll () {
this.getRouterByName(this.name)
.then(body => {
if (!body) {
this.loading = false
return
}
// Get entryPoints
if (body.using) {
for (const entryPoint in body.using) {
if (body.using.hasOwnProperty(entryPoint)) {
this.getEntrypointsByName(body.using[entryPoint])
.then(body => {
if (body) {
this.entryPoints.push(body)
}
})
.catch(error => {
console.log('Error -> entrypoints/byName', error)
})
}
}
}
// Get middlewares
if (body.middlewares) {
for (const middleware in body.middlewares) {
if (body.middlewares.hasOwnProperty(middleware)) {
this.getMiddlewareByName(body.middlewares[middleware])
.then(body => {
if (body) {
this.middlewares.push(body)
}
})
.catch(error => {
console.log('Error -> middlewares/byName', error)
})
}
}
}
clearTimeout(this.timeOutGetAll)
this.timeOutGetAll = setTimeout(() => {
this.loading = false
}, 300)
})
.catch(error => {
console.log('Error -> routers/byName', error)
})
}
},
created () {
this.refreshAll()
},
mounted () {
},
beforeDestroy () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getRouterByNameClear')
this.$store.commit('tcp/getRouterByNameClear')
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.path-block {
.arrow {
font-size: 40px;
margin-top: 20px;
margin-left: 20px;
color: #b2b2b2;
}
}
</style>

View file

@ -0,0 +1,239 @@
<template>
<page-default>
<section v-if="!loading" class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-lg">
<div v-if="serviceByName.item" class="row no-wrap items-center app-title">
<div class="app-title-label" style="font-size: 26px">{{ serviceByName.item.name }}</div>
</div>
</div>
</section>
<section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg">
<div v-if="!loading" class="row items-start q-col-gutter-md">
<div v-if="serviceByName.item" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-info"></q-icon>
<div class="app-title-label">Service Details</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-service-details dense :data="serviceByName.item" />
</div>
</div>
</div>
</div>
</div>
<div v-if="serviceByName.item.loadBalancer && serviceByName.item.loadBalancer.healthCheck" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-shield"></q-icon>
<div class="app-title-label">Health Check</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-health-check dense :data="serviceByName.item.loadBalancer.healthCheck"/>
</div>
</div>
</div>
</div>
</div>
<div v-if="serviceByName.item.loadBalancer && serviceByName.item.loadBalancer.servers" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon>
<div class="app-title-label">Servers</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-servers dense :data="serviceByName.item"/>
</div>
</div>
</div>
</div>
</div>
<div v-if="serviceByName.item.weighted && serviceByName.item.weighted.services" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon>
<div class="app-title-label">Services</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-weighted-services dense :data="serviceByName.item"/>
</div>
</div>
</div>
</div>
</div>
<div v-if="serviceByName.item.mirroring && serviceByName.item.mirroring.mirrors" class="col-12 col-md-4 q-mb-lg path-block">
<div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon>
<div class="app-title-label">Mirror Services</div>
</div>
<div class="row items-start q-col-gutter-lg">
<div class="col-12">
<div class="row items-start q-col-gutter-md">
<div class="col-12">
<panel-mirroring-services dense :data="serviceByName.item"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else class="row items-start q-mt-xl">
<div class="col-12">
<p v-for="n in 4" :key="n" class="flex">
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/>
</p>
</div>
</div>
</div>
</section>
<section v-if="!loading && allRouters.length" class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl">
<div class="row no-wrap items-center q-mb-lg app-title">
<div class="app-title-label">Used by Routers</div>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allRouters" :request="()=>{}" :loading="routersLoading" :pagination.sync="routersPagination" :filter="routersFilter" :type="`${protocol}-routers`"/>
</div>
</div>
</div>
</section>
</page-default>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import SkeletonBox from '../../components/_commons/SkeletonBox'
import PanelServiceDetails from '../../components/_commons/PanelServiceDetails'
import PanelHealthCheck from '../../components/_commons/PanelHealthCheck'
import PanelServers from '../../components/_commons/PanelServers'
import MainTable from '../../components/_commons/MainTable'
import PanelWeightedServices from '../../components/_commons/PanelWeightedServices'
import PanelMirroringServices from '../../components/_commons/PanelMirroringServices'
export default {
name: 'PageServiceDetail',
props: ['name', 'type'],
components: {
PanelMirroringServices,
PanelWeightedServices,
PageDefault,
SkeletonBox,
PanelServiceDetails,
PanelHealthCheck,
PanelServers,
MainTable
},
data () {
return {
loading: true,
timeOutGetAll: null,
allRouters: [],
routersLoading: true,
routersFilter: '',
routersStatus: '',
routersPagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 1000,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('http', { http_serviceByName: 'serviceByName' }),
...mapGetters('tcp', { tcp_serviceByName: 'serviceByName' }),
protocol () {
return this.$route.meta.protocol
},
serviceByName () {
return this[`${this.protocol}_serviceByName`]
},
getServiceByName () {
return this[`${this.protocol}_getServiceByName`]
},
getRouterByName () {
return this[`${this.protocol}_getRouterByName`]
}
},
methods: {
...mapActions('http', { http_getServiceByName: 'getServiceByName', http_getRouterByName: 'getRouterByName' }),
...mapActions('tcp', { tcp_getServiceByName: 'getServiceByName', tcp_getRouterByName: 'getRouterByName' }),
refreshAll () {
if (this.serviceByName.loading) {
return
}
this.onGetAll()
},
onGetAll () {
this.getServiceByName(this.name)
.then(body => {
if (!body) {
this.loading = false
return
}
// Get routers
if (body.usedBy) {
for (const router in body.usedBy) {
if (body.usedBy.hasOwnProperty(router)) {
this.getRouterByName(body.usedBy[router])
.then(body => {
if (body) {
this.routersLoading = false
this.allRouters.push(body)
}
})
.catch(error => {
console.log('Error -> routers/byName', error)
})
}
}
}
clearTimeout(this.timeOutGetAll)
this.timeOutGetAll = setTimeout(() => {
this.loading = false
}, 300)
})
.catch(error => {
console.log('Error -> service/byName', error)
})
}
},
created () {
this.refreshAll()
},
mounted () {
},
beforeDestroy () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getServiceByNameClear')
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
</style>

View file

@ -1,14 +1,108 @@
<template> <template>
<q-page class="flex flex-center"> <page-default>
<h2>HTTP Middlewares</h2>
</q-page> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allMiddlewares.items" :request="onGetAll" :loading="loading" :pagination.sync="pagination" :filter="filter" type="http-middlewares"/>
</div>
</div>
</div>
</section>
</page-default>
</template> </template>
<style>
</style>
<script> <script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import ToolBarTable from '../../components/_commons/ToolBarTable'
import MainTable from '../../components/_commons/MainTable'
export default { export default {
name: 'PageHTTPMiddlewares' name: 'PageHTTPMiddlewares',
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () {
return {
loading: true,
filter: '',
status: '',
pagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 10,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('http', { allMiddlewares: 'allMiddlewares' })
},
methods: {
...mapActions('http', { getAllMiddlewares: 'getAllMiddlewares' }),
refreshAll () {
if (this.allMiddlewares.loading) {
return
}
this.pagination.page = 1
this.onGetAll({
pagination: this.pagination,
filter: this.filter
})
},
onGetAll (props) {
let { page, rowsPerPage, sortBy, descending } = props.pagination
this.getAllMiddlewares({ query: props.filter, status: this.status, page, limit: rowsPerPage, sortBy, descending })
.then(body => {
if (!body) {
this.loading = false
return
}
this.loading = false
console.log('Success -> http/middlewares', body)
// update rowsNumber with appropriate value
this.pagination.rowsNumber = body.total
// update local pagination object
this.pagination.page = page
this.pagination.rowsPerPage = rowsPerPage
this.pagination.sortBy = sortBy
this.pagination.descending = descending
})
.catch(error => {
console.log('Error -> http/middlewares', error)
})
}
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
created () {
},
mounted () {
this.refreshAll()
},
beforeDestroy () {
this.$store.commit('http/getAllMiddlewaresClear')
}
} }
</script> </script>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template> <template>
<q-page class="flex flex-center"> <page-default>
<h2>HTTP Routers</h2>
</q-page> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allRouters.items" :request="onGetAll" :loading="loading" :pagination.sync="pagination" :filter="filter" type="http-routers"/>
</div>
</div>
</div>
</section>
</page-default>
</template> </template>
<style>
</style>
<script> <script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import ToolBarTable from '../../components/_commons/ToolBarTable'
import MainTable from '../../components/_commons/MainTable'
export default { export default {
name: 'PageHTTPRouters' name: 'PageHTTPRouters',
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () {
return {
loading: true,
filter: '',
status: '',
pagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 10,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('http', { allRouters: 'allRouters' })
},
methods: {
...mapActions('http', { getAllRouters: 'getAllRouters' }),
refreshAll () {
if (this.allRouters.loading) {
return
}
this.pagination.page = 1
this.onGetAll({
pagination: this.pagination,
filter: this.filter
})
},
onGetAll (props) {
let { page, rowsPerPage, sortBy, descending } = props.pagination
this.getAllRouters({ query: props.filter, status: this.status, page, limit: rowsPerPage, sortBy, descending })
.then(body => {
if (!body) {
this.loading = false
return
}
this.loading = false
console.log('Success -> http/routers', body)
// update rowsNumber with appropriate value
this.pagination.rowsNumber = body.total
// update local pagination object
this.pagination.page = page
this.pagination.rowsPerPage = rowsPerPage
this.pagination.sortBy = sortBy
this.pagination.descending = descending
})
.catch(error => {
console.log('Error -> http/router', error)
})
}
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
created () {
},
mounted () {
this.refreshAll()
},
beforeDestroy () {
this.$store.commit('http/getAllRoutersClear')
}
} }
</script> </script>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template> <template>
<q-page class="flex flex-center"> <page-default>
<h2>HTTP Services</h2>
</q-page> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allServices.items" :request="onGetAll" :loading="loading" :pagination.sync="pagination" :filter="filter" type="http-services"/>
</div>
</div>
</div>
</section>
</page-default>
</template> </template>
<style>
</style>
<script> <script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import ToolBarTable from '../../components/_commons/ToolBarTable'
import MainTable from '../../components/_commons/MainTable'
export default { export default {
name: 'PageHTTPServices' name: 'PageHTTPServices',
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () {
return {
loading: true,
filter: '',
status: '',
pagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 10,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('http', { allServices: 'allServices' })
},
methods: {
...mapActions('http', { getAllServices: 'getAllServices' }),
refreshAll () {
if (this.allServices.loading) {
return
}
this.pagination.page = 1
this.onGetAll({
pagination: this.pagination,
filter: this.filter
})
},
onGetAll (props) {
let { page, rowsPerPage, sortBy, descending } = props.pagination
this.getAllServices({ query: props.filter, status: this.status, page, limit: rowsPerPage, sortBy, descending })
.then(body => {
if (!body) {
this.loading = false
return
}
this.loading = false
console.log('Success -> http/services', body)
// update rowsNumber with appropriate value
this.pagination.rowsNumber = body.total
// update local pagination object
this.pagination.page = page
this.pagination.rowsPerPage = rowsPerPage
this.pagination.sortBy = sortBy
this.pagination.descending = descending
})
.catch(error => {
console.log('Error -> http/services', error)
})
}
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
created () {
},
mounted () {
this.refreshAll()
},
beforeDestroy () {
this.$store.commit('http/getAllServicesClear')
}
} }
</script> </script>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template> <template>
<q-page class="flex flex-center"> <page-default>
<h2>TCP Routers</h2>
</q-page> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allRouters.items" :request="onGetAll" :loading="loading" :pagination.sync="pagination" :filter="filter" type="tcp-routers"/>
</div>
</div>
</div>
</section>
</page-default>
</template> </template>
<style>
</style>
<script> <script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import ToolBarTable from '../../components/_commons/ToolBarTable'
import MainTable from '../../components/_commons/MainTable'
export default { export default {
name: 'PageTCPRouters' name: 'PageTCPRouters',
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () {
return {
loading: true,
filter: '',
status: '',
pagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 10,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('tcp', { allRouters: 'allRouters' })
},
methods: {
...mapActions('tcp', { getAllRouters: 'getAllRouters' }),
refreshAll () {
if (this.allRouters.loading) {
return
}
this.pagination.page = 1
this.onGetAll({
pagination: this.pagination,
filter: this.filter
})
},
onGetAll (props) {
let { page, rowsPerPage, sortBy, descending } = props.pagination
this.getAllRouters({ query: props.filter, status: this.status, page, limit: rowsPerPage, sortBy, descending })
.then(body => {
if (!body) {
this.loading = false
return
}
this.loading = false
console.log('Success -> tcp/routers', body)
// update rowsNumber with appropriate value
this.pagination.rowsNumber = body.total
// update local pagination object
this.pagination.page = page
this.pagination.rowsPerPage = rowsPerPage
this.pagination.sortBy = sortBy
this.pagination.descending = descending
})
.catch(error => {
console.log('Error -> tcp/router', error)
})
}
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
created () {
},
mounted () {
this.refreshAll()
},
beforeDestroy () {
this.$store.commit('tcp/getAllRoutersClear')
}
} }
</script> </script>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template> <template>
<q-page class="flex flex-center"> <page-default>
<h2>TCP Services</h2>
</q-page> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/>
</div>
<div class="row items-center q-col-gutter-lg">
<div class="col-12">
<main-table :data="allServices.items" :request="onGetAll" :loading="loading" :pagination.sync="pagination" :filter="filter" type="tcp-services"/>
</div>
</div>
</div>
</section>
</page-default>
</template> </template>
<style>
</style>
<script> <script>
import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault'
import ToolBarTable from '../../components/_commons/ToolBarTable'
import MainTable from '../../components/_commons/MainTable'
export default { export default {
name: 'PageTCPServices' name: 'PageTCPServices',
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () {
return {
loading: true,
filter: '',
status: '',
pagination: {
sortBy: '',
descending: true,
page: 1,
rowsPerPage: 10,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('tcp', { allServices: 'allServices' })
},
methods: {
...mapActions('tcp', { getAllServices: 'getAllServices' }),
refreshAll () {
if (this.allServices.loading) {
return
}
this.pagination.page = 1
this.onGetAll({
pagination: this.pagination,
filter: this.filter
})
},
onGetAll (props) {
let { page, rowsPerPage, sortBy, descending } = props.pagination
this.getAllServices({ query: props.filter, status: this.status, page, limit: rowsPerPage, sortBy, descending })
.then(body => {
if (!body) {
this.loading = false
return
}
this.loading = false
console.log('Success -> tcp/services', body)
// update rowsNumber with appropriate value
this.pagination.rowsNumber = body.total
// update local pagination object
this.pagination.page = page
this.pagination.rowsPerPage = rowsPerPage
this.pagination.sortBy = sortBy
this.pagination.descending = descending
})
.catch(error => {
console.log('Error -> tcp/services', error)
})
}
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
created () {
},
mounted () {
this.refreshAll()
},
beforeDestroy () {
this.$store.commit('tcp/getAllServicesClear')
}
} }
</script> </script>
<style scoped lang="scss">
</style>

View file

@ -14,6 +14,150 @@ const routes = [
} }
} }
] ]
},
{
path: '/http',
redirect: '/http/routers',
component: LayoutDefault,
children: [
{
path: 'routers',
name: 'httpRouters',
components: {
default: () => import('pages/http/Routers.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'http',
title: 'HTTP Routers'
}
},
{
path: 'routers/:name',
name: 'httpRouterDetail',
components: {
default: () => import('pages/_commons/RouterDetail.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'http',
title: 'HTTP Router Detail'
}
},
{
path: 'services',
name: 'httpServices',
components: {
default: () => import('pages/http/Services.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'http',
title: 'HTTP Services'
}
},
{
path: 'services/:name',
name: 'httpServiceDetail',
components: {
default: () => import('pages/_commons/ServiceDetail.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'http',
title: 'HTTP Service Detail'
}
},
{
path: 'middlewares',
name: 'httpMiddlewares',
components: {
default: () => import('pages/http/Middlewares.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'http',
title: 'HTTP Middlewares'
}
},
{
path: 'middlewares/:name',
name: 'httpMiddlewareDetail',
components: {
default: () => import('pages/_commons/MiddlewareDetail.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'http',
title: 'HTTP Middleware Detail'
}
}
]
},
{
path: '/tcp',
redirect: '/tcp/routers',
component: LayoutDefault,
children: [
{
path: 'routers',
name: 'tcpRouters',
components: {
default: () => import('pages/tcp/Routers.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'tcp',
title: 'TCP Routers'
}
},
{
path: 'routers/:name',
name: 'tcpRouterDetail',
components: {
default: () => import('pages/_commons/RouterDetail.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'tcp',
title: 'TCP Router Detail'
}
},
{
path: 'services',
name: 'tcpServices',
components: {
default: () => import('pages/tcp/Services.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'tcp',
title: 'TCP Services'
}
},
{
path: 'services/:name',
name: 'tcpServiceDetail',
components: {
default: () => import('pages/_commons/ServiceDetail.vue'),
NavBar: () => import('components/_commons/ToolBar.vue')
},
props: { default: true, NavBar: true },
meta: {
protocol: 'tcp',
title: 'TCP Service Detail'
}
}
]
} }
] ]

BIN
webui/src/statics/app-logo-128x128.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
webui/src/statics/icons/apple-icon-120x120.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
webui/src/statics/icons/apple-icon-152x152.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 22 KiB

BIN
webui/src/statics/icons/apple-icon-167x167.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
webui/src/statics/icons/apple-icon-180x180.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
webui/src/statics/icons/favicon-16x16.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 468 B

BIN
webui/src/statics/icons/favicon-32x32.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
webui/src/statics/icons/favicon-96x96.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
webui/src/statics/icons/icon-128x128.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
webui/src/statics/icons/icon-192x192.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 29 KiB

BIN
webui/src/statics/icons/icon-256x256.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 42 KiB

BIN
webui/src/statics/icons/icon-384x384.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 73 KiB

BIN
webui/src/statics/icons/icon-512x512.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 109 KiB

BIN
webui/src/statics/icons/ms-icon-144x144.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 20 KiB

4
webui/src/statics/icons/safari-pinned-tab.svg Normal file → Executable file

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="16" fill="#326CE6"/>
<path fill="#FFF" fill-rule="nonzero" d="M27.35 18.155c-.036 0-.073 0-.073-.037s-.072-.037-.144-.037c-.145-.037-.289-.037-.434-.037-.072 0-.144 0-.216-.037h-.036a7.13 7.13 0 0 1-1.229-.223.442.442 0 0 1-.253-.26c.037 0 0 0 0 0l-.289-.074c.145-1.075.073-2.187-.144-3.263a10.189 10.189 0 0 0-1.265-3.04l.217-.223v-.037c0-.112.036-.26.109-.334.325-.297.65-.52 1.011-.742l.217-.111c.144-.074.253-.148.397-.222.036-.038.073-.038.109-.075.036-.037 0-.037 0-.074.325-.26.397-.704.144-1.038a.684.684 0 0 0-.506-.26.85.85 0 0 0-.505.186l-.036.037c-.037.037-.073.074-.109.074a2.38 2.38 0 0 0-.289.334c-.036.074-.108.11-.144.148a4.791 4.791 0 0 1-.904.816c-.072.037-.144.074-.216.074-.036 0-.109 0-.145-.037h-.036l-.289.185c-.289-.296-.614-.593-.903-.89a9.125 9.125 0 0 0-4.696-1.928l-.037-.297v.037c-.108-.074-.144-.185-.18-.296 0-.408 0-.816.072-1.261v-.037c0-.074.036-.148.036-.223.036-.148.036-.296.072-.445v-.222c.037-.371-.252-.742-.614-.779-.216-.037-.433.074-.614.26a.742.742 0 0 0-.217.519v.185c0 .149.037.297.073.445a.481.481 0 0 1 .036.223v.037c.072.408.072.816.072 1.26-.036.112-.072.223-.18.297v.075l-.037.296c-.397.037-.795.111-1.228.186-1.698.37-3.251 1.298-4.444 2.595l-.216-.148H9.25c-.037 0-.073.037-.145.037s-.144-.037-.217-.074a6.73 6.73 0 0 1-.903-.853c-.036-.074-.108-.111-.144-.148-.109-.111-.181-.223-.29-.334-.035-.037-.072-.037-.108-.074l-.036-.037a.85.85 0 0 0-.506-.186c-.216 0-.397.075-.505.26a.813.813 0 0 0 .144 1.038c.036 0 .036.037.036.037s.073.075.109.075c.108.074.253.148.397.222l.217.111c.361.223.722.445 1.011.742.073.074.145.222.109.334v-.037l.216.222c-.036.074-.072.111-.108.186-1.12 1.817-1.59 3.967-1.264 6.081l-.29.074c0 .037-.035.037-.035.037-.037.112-.145.186-.253.26a6.554 6.554 0 0 1-1.229.223c-.072 0-.144 0-.216.037-.145 0-.29.037-.434.037-.036 0-.072.037-.144.037-.037 0-.037 0-.073.037a.722.722 0 0 0-.578.853.672.672 0 0 0 .723.519c.072 0 .108 0 .18-.037.037 0 .037 0 .037-.037s.108 0 .144 0c.145-.037.29-.111.397-.149.073-.037.145-.074.217-.074h.036a6.31 6.31 0 0 1 1.193-.333h.036c.108 0 .216.037.289.11.036 0 .036.038.036.038l.325-.037c.542 1.706 1.553 3.226 2.962 4.339.325.26.614.482.976.667l-.181.26c0 .037.036.037.036.037.072.11.072.26.036.37-.144.371-.361.742-.578 1.076v.037c-.036.074-.072.111-.144.185-.073.075-.145.223-.253.371-.036.037-.036.074-.072.112 0 0 0 .037-.037.037-.18.37-.036.815.29 1 .072.038.18.075.252.075.29 0 .542-.185.687-.445 0 0 0-.037.036-.037 0-.037.036-.074.072-.111.036-.149.109-.26.145-.408l.072-.223c.108-.408.289-.778.47-1.15a.563.563 0 0 1 .289-.222c.036 0 .036 0 .036-.037l.144-.297a8.31 8.31 0 0 0 3.143.594c.65 0 1.3-.074 1.951-.26.397-.074.795-.222 1.156-.334l.144.26c.037 0 .037 0 .037.037a.563.563 0 0 1 .289.223c.18.37.36.741.47 1.15v.036l.072.223c.036.148.072.296.144.408.036.037.036.074.072.11 0 0 0 .038.036.038.145.26.398.445.687.445.108 0 .18-.037.289-.074a.617.617 0 0 0 .325-.408.978.978 0 0 0-.036-.556c0-.037-.036-.037-.036-.037 0-.037-.036-.075-.073-.112a1.333 1.333 0 0 0-.252-.37.649.649 0 0 0-.145-.186v-.074a4.417 4.417 0 0 1-.578-1.075.449.449 0 0 1 .036-.371c0-.037.036-.037.036-.037l-.108-.297a9.128 9.128 0 0 0 3.902-5.043l.289.037c.036 0 .036-.037.036-.037a.404.404 0 0 1 .289-.112h.036c.397.075.795.186 1.156.334h.036c.072.037.144.074.217.074.144.074.253.149.397.186.036 0 .072.037.145.037.036 0 .036 0 .072.037a.319.319 0 0 0 .18.037.787.787 0 0 0 .723-.52.867.867 0 0 0-.65-.778zm-10.44-1.15l-.976.483-.976-.483-.252-1.075.686-.89h1.084l.686.89-.253 1.075zm5.888-2.41c.18.779.217 1.558.144 2.336L19.51 15.93c-.325-.074-.505-.408-.433-.742a.602.602 0 0 1 .144-.26l2.71-2.52c.397.667.686 1.408.867 2.187zm-1.951-3.56l-2.962 2.15c-.253.15-.614.112-.795-.147a.34.34 0 0 1-.108-.26l-.217-3.745a6.73 6.73 0 0 1 4.082 2.002zm-6.539-1.891l.723-.149-.18 3.709c0 .334-.29.593-.615.593-.108 0-.18-.037-.289-.074l-2.998-2.188c.939-.927 2.095-1.595 3.36-1.891zm-4.407 3.263l2.673 2.448a.607.607 0 0 1 .072.853c-.072.11-.144.148-.289.185L8.853 16.93a7.972 7.972 0 0 1 1.048-4.524zm-.614 6.267l3.576-.63c.29 0 .578.185.614.482a.449.449 0 0 1-.036.37l-1.373 3.412a7.268 7.268 0 0 1-2.781-3.634zm8.2 4.599a7.13 7.13 0 0 1-1.553.185c-.759 0-1.554-.148-2.276-.37l1.77-3.301c.18-.223.47-.297.723-.149.108.075.18.149.289.26l1.734 3.226c-.217.037-.434.074-.687.149zm4.408-3.227a6.992 6.992 0 0 1-2.168 2.225l-1.409-3.486a.608.608 0 0 1 .325-.704c.109-.037.217-.074.325-.074l3.613.63c-.18.52-.397 1.001-.686 1.41z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="16" fill="#326CE6"/>
<path fill="#FFF" fill-rule="nonzero" d="M27.35 18.155c-.036 0-.073 0-.073-.037s-.072-.037-.144-.037c-.145-.037-.289-.037-.434-.037-.072 0-.144 0-.216-.037h-.036a7.13 7.13 0 0 1-1.229-.223.442.442 0 0 1-.253-.26c.037 0 0 0 0 0l-.289-.074c.145-1.075.073-2.187-.144-3.263a10.189 10.189 0 0 0-1.265-3.04l.217-.223v-.037c0-.112.036-.26.109-.334.325-.297.65-.52 1.011-.742l.217-.111c.144-.074.253-.148.397-.222.036-.038.073-.038.109-.075.036-.037 0-.037 0-.074.325-.26.397-.704.144-1.038a.684.684 0 0 0-.506-.26.85.85 0 0 0-.505.186l-.036.037c-.037.037-.073.074-.109.074a2.38 2.38 0 0 0-.289.334c-.036.074-.108.11-.144.148a4.791 4.791 0 0 1-.904.816c-.072.037-.144.074-.216.074-.036 0-.109 0-.145-.037h-.036l-.289.185c-.289-.296-.614-.593-.903-.89a9.125 9.125 0 0 0-4.696-1.928l-.037-.297v.037c-.108-.074-.144-.185-.18-.296 0-.408 0-.816.072-1.261v-.037c0-.074.036-.148.036-.223.036-.148.036-.296.072-.445v-.222c.037-.371-.252-.742-.614-.779-.216-.037-.433.074-.614.26a.742.742 0 0 0-.217.519v.185c0 .149.037.297.073.445a.481.481 0 0 1 .036.223v.037c.072.408.072.816.072 1.26-.036.112-.072.223-.18.297v.075l-.037.296c-.397.037-.795.111-1.228.186-1.698.37-3.251 1.298-4.444 2.595l-.216-.148H9.25c-.037 0-.073.037-.145.037s-.144-.037-.217-.074a6.73 6.73 0 0 1-.903-.853c-.036-.074-.108-.111-.144-.148-.109-.111-.181-.223-.29-.334-.035-.037-.072-.037-.108-.074l-.036-.037a.85.85 0 0 0-.506-.186c-.216 0-.397.075-.505.26a.813.813 0 0 0 .144 1.038c.036 0 .036.037.036.037s.073.075.109.075c.108.074.253.148.397.222l.217.111c.361.223.722.445 1.011.742.073.074.145.222.109.334v-.037l.216.222c-.036.074-.072.111-.108.186-1.12 1.817-1.59 3.967-1.264 6.081l-.29.074c0 .037-.035.037-.035.037-.037.112-.145.186-.253.26a6.554 6.554 0 0 1-1.229.223c-.072 0-.144 0-.216.037-.145 0-.29.037-.434.037-.036 0-.072.037-.144.037-.037 0-.037 0-.073.037a.722.722 0 0 0-.578.853.672.672 0 0 0 .723.519c.072 0 .108 0 .18-.037.037 0 .037 0 .037-.037s.108 0 .144 0c.145-.037.29-.111.397-.149.073-.037.145-.074.217-.074h.036a6.31 6.31 0 0 1 1.193-.333h.036c.108 0 .216.037.289.11.036 0 .036.038.036.038l.325-.037c.542 1.706 1.553 3.226 2.962 4.339.325.26.614.482.976.667l-.181.26c0 .037.036.037.036.037.072.11.072.26.036.37-.144.371-.361.742-.578 1.076v.037c-.036.074-.072.111-.144.185-.073.075-.145.223-.253.371-.036.037-.036.074-.072.112 0 0 0 .037-.037.037-.18.37-.036.815.29 1 .072.038.18.075.252.075.29 0 .542-.185.687-.445 0 0 0-.037.036-.037 0-.037.036-.074.072-.111.036-.149.109-.26.145-.408l.072-.223c.108-.408.289-.778.47-1.15a.563.563 0 0 1 .289-.222c.036 0 .036 0 .036-.037l.144-.297a8.31 8.31 0 0 0 3.143.594c.65 0 1.3-.074 1.951-.26.397-.074.795-.222 1.156-.334l.144.26c.037 0 .037 0 .037.037a.563.563 0 0 1 .289.223c.18.37.36.741.47 1.15v.036l.072.223c.036.148.072.296.144.408.036.037.036.074.072.11 0 0 0 .038.036.038.145.26.398.445.687.445.108 0 .18-.037.289-.074a.617.617 0 0 0 .325-.408.978.978 0 0 0-.036-.556c0-.037-.036-.037-.036-.037 0-.037-.036-.075-.073-.112a1.333 1.333 0 0 0-.252-.37.649.649 0 0 0-.145-.186v-.074a4.417 4.417 0 0 1-.578-1.075.449.449 0 0 1 .036-.371c0-.037.036-.037.036-.037l-.108-.297a9.128 9.128 0 0 0 3.902-5.043l.289.037c.036 0 .036-.037.036-.037a.404.404 0 0 1 .289-.112h.036c.397.075.795.186 1.156.334h.036c.072.037.144.074.217.074.144.074.253.149.397.186.036 0 .072.037.145.037.036 0 .036 0 .072.037a.319.319 0 0 0 .18.037.787.787 0 0 0 .723-.52.867.867 0 0 0-.65-.778zm-10.44-1.15l-.976.483-.976-.483-.252-1.075.686-.89h1.084l.686.89-.253 1.075zm5.888-2.41c.18.779.217 1.558.144 2.336L19.51 15.93c-.325-.074-.505-.408-.433-.742a.602.602 0 0 1 .144-.26l2.71-2.52c.397.667.686 1.408.867 2.187zm-1.951-3.56l-2.962 2.15c-.253.15-.614.112-.795-.147a.34.34 0 0 1-.108-.26l-.217-3.745a6.73 6.73 0 0 1 4.082 2.002zm-6.539-1.891l.723-.149-.18 3.709c0 .334-.29.593-.615.593-.108 0-.18-.037-.289-.074l-2.998-2.188c.939-.927 2.095-1.595 3.36-1.891zm-4.407 3.263l2.673 2.448a.607.607 0 0 1 .072.853c-.072.11-.144.148-.289.185L8.853 16.93a7.972 7.972 0 0 1 1.048-4.524zm-.614 6.267l3.576-.63c.29 0 .578.185.614.482a.449.449 0 0 1-.036.37l-1.373 3.412a7.268 7.268 0 0 1-2.781-3.634zm8.2 4.599a7.13 7.13 0 0 1-1.553.185c-.759 0-1.554-.148-2.276-.37l1.77-3.301c.18-.223.47-.297.723-.149.108.075.18.149.289.26l1.734 3.226c-.217.037-.434.074-.687.149zm4.408-3.227a6.992 6.992 0 0 1-2.168 2.225l-1.409-3.486a.608.608 0 0 1 .325-.704c.109-.037.217-.074.325-.074l3.613.63c-.18.52-.397 1.001-.686 1.41z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -1,8 +1,8 @@
import entrypointsService from '../../_services/EntrypointsService' import EntrypointsService from '../../_services/EntrypointsService'
export function getAll ({ commit }) { export function getAll ({ commit }) {
commit('getAllRequest') commit('getAllRequest')
return entrypointsService.getAll() return EntrypointsService.getAll()
.then(body => { .then(body => {
commit('getAllSuccess', body) commit('getAllSuccess', body)
return body return body
@ -12,3 +12,16 @@ export function getAll ({ commit }) {
return Promise.reject(error) return Promise.reject(error)
}) })
} }
export function getByName ({ commit }, name) {
commit('getByNameRequest')
return EntrypointsService.getByName(name)
.then(body => {
commit('getByNameSuccess', body)
return body
})
.catch(error => {
commit('getByNameFailure', error)
return Promise.reject(error)
})
}

View file

@ -4,3 +4,10 @@
export function all (state) { export function all (state) {
return state.all return state.all
} }
// ----------------------------
// byName
// ----------------------------
export function byName (state) {
return state.byName
}

View file

@ -16,3 +16,22 @@ export function getAllFailure (state, error) {
export function getAllClear (state) { export function getAllClear (state) {
state.all = {} state.all = {}
} }
// ----------------------------
// Get By Name
// ----------------------------
export function getByNameRequest (state) {
state.byName.loading = true
}
export function getByNameSuccess (state, body) {
state.byName = { item: body, loading: false }
}
export function getByNameFailure (state, error) {
state.byName = { error }
}
export function getByNameClear (state) {
state.byName = {}
}

View file

@ -0,0 +1,79 @@
import HttpService from '../../_services/HttpService'
export function getAllRouters ({ commit }, params) {
commit('getAllRoutersRequest')
return HttpService.getAllRouters(params)
.then(body => {
commit('getAllRoutersSuccess', body)
return body
})
.catch(error => {
commit('getAllRoutersFailure', error)
return Promise.reject(error)
})
}
export function getRouterByName ({ commit }, name) {
commit('getRouterByNameRequest')
return HttpService.getRouterByName(name)
.then(body => {
commit('getRouterByNameSuccess', body)
return body
})
.catch(error => {
commit('getRouterByNameFailure', error)
return Promise.reject(error)
})
}
export function getAllServices ({ commit }, params) {
commit('getAllServicesRequest')
return HttpService.getAllServices(params)
.then(body => {
commit('getAllServicesSuccess', body)
return body
})
.catch(error => {
commit('getAllServicesFailure', error)
return Promise.reject(error)
})
}
export function getServiceByName ({ commit }, name) {
commit('getServiceByNameRequest')
return HttpService.getServiceByName(name)
.then(body => {
commit('getServiceByNameSuccess', body)
return body
})
.catch(error => {
commit('getServiceByNameFailure', error)
return Promise.reject(error)
})
}
export function getAllMiddlewares ({ commit }, params) {
commit('getAllMiddlewaresRequest')
return HttpService.getAllMiddlewares(params)
.then(body => {
commit('getAllMiddlewaresSuccess', body)
return body
})
.catch(error => {
commit('getAllMiddlewaresFailure', error)
return Promise.reject(error)
})
}
export function getMiddlewareByName ({ commit }, name) {
commit('getMiddlewareByNameRequest')
return HttpService.getMiddlewareByName(name)
.then(body => {
commit('getMiddlewareByNameSuccess', body)
return body
})
.catch(error => {
commit('getMiddlewareByNameFailure', error)
return Promise.reject(error)
})
}

View file

@ -0,0 +1,41 @@
// ----------------------------
// all Routers
// ----------------------------
export function allRouters (state) {
return state.allRouters
}
// ----------------------------
// Router by Name
// ----------------------------
export function routerByName (state) {
return state.routerByName
}
// ----------------------------
// all Services
// ----------------------------
export function allServices (state) {
return state.allServices
}
// ----------------------------
// Service by Name
// ----------------------------
export function serviceByName (state) {
return state.serviceByName
}
// ----------------------------
// all Middlewares
// ----------------------------
export function allMiddlewares (state) {
return state.allMiddlewares
}
// ----------------------------
// Middleware by Name
// ----------------------------
export function middlewareByName (state) {
return state.middlewareByName
}

View file

@ -0,0 +1,12 @@
import state from './state'
import * as getters from './getters'
import * as mutations from './mutations'
import * as actions from './actions'
export default {
namespaced: true,
getters,
mutations,
actions,
state
}

View file

@ -0,0 +1,113 @@
// ----------------------------
// Get All Routers
// ----------------------------
export function getAllRoutersRequest (state) {
state.allRouters.loading = true
}
export function getAllRoutersSuccess (state, body) {
state.allRouters = { items: body.data, total: body.total, loading: false }
}
export function getAllRoutersFailure (state, error) {
state.allRouters = { error }
}
export function getAllRoutersClear (state) {
state.allRouters = {}
}
// ----------------------------
// Get Router By Name
// ----------------------------
export function getRouterByNameRequest (state) {
state.routerByName.loading = true
}
export function getRouterByNameSuccess (state, body) {
state.routerByName = { item: body, loading: false }
}
export function getRouterByNameFailure (state, error) {
state.routerByName = { error }
}
export function getRouterByNameClear (state) {
state.routerByName = {}
}
// ----------------------------
// Get All Services
// ----------------------------
export function getAllServicesRequest (state) {
state.allServices.loading = true
}
export function getAllServicesSuccess (state, body) {
state.allServices = { items: body.data, total: body.total, loading: false }
}
export function getAllServicesFailure (state, error) {
state.allServices = { error }
}
export function getAllServicesClear (state) {
state.allServices = {}
}
// ----------------------------
// Get Service By Name
// ----------------------------
export function getServiceByNameRequest (state) {
state.serviceByName.loading = true
}
export function getServiceByNameSuccess (state, body) {
state.serviceByName = { item: body, loading: false }
}
export function getServiceByNameFailure (state, error) {
state.serviceByName = { error }
}
export function getServiceByNameClear (state) {
state.serviceByName = {}
}
// ----------------------------
// Get All Middlewares
// ----------------------------
export function getAllMiddlewaresRequest (state) {
state.allMiddlewares.loading = true
}
export function getAllMiddlewaresSuccess (state, body) {
state.allMiddlewares = { items: body.data, total: body.total, loading: false }
}
export function getAllMiddlewaresFailure (state, error) {
state.allMiddlewares = { error }
}
export function getAllMiddlewaresClear (state) {
state.allMiddlewares = {}
}
// ----------------------------
// Get Middleware By Name
// ----------------------------
export function getMiddlewareByNameRequest (state) {
state.middlewareByName.loading = true
}
export function getMiddlewareByNameSuccess (state, body) {
state.middlewareByName = { item: body, loading: false }
}
export function getMiddlewareByNameFailure (state, error) {
state.middlewareByName = { error }
}
export function getMiddlewareByNameClear (state) {
state.middlewareByName = {}
}

View file

@ -0,0 +1,8 @@
export default {
allRouters: {},
routerByName: {},
allServices: {},
serviceByName: {},
allMiddlewares: {},
middlewareByName: {}
}

View file

@ -3,6 +3,8 @@ import Vuex from 'vuex'
import core from './core' import core from './core'
import entrypoints from './entrypoints' import entrypoints from './entrypoints'
import http from './http'
import tcp from './tcp'
Vue.use(Vuex) Vue.use(Vuex)
@ -15,7 +17,9 @@ export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({ const Store = new Vuex.Store({
modules: { modules: {
core, core,
entrypoints entrypoints,
http,
tcp
}, },
// enable strict mode (adds overhead!) // enable strict mode (adds overhead!)

View file

@ -0,0 +1,53 @@
import TcpService from '../../_services/TcpService'
export function getAllRouters ({ commit }, params) {
commit('getAllRoutersRequest')
return TcpService.getAllRouters(params)
.then(body => {
commit('getAllRoutersSuccess', body)
return body
})
.catch(error => {
commit('getAllRoutersFailure', error)
return Promise.reject(error)
})
}
export function getRouterByName ({ commit }, name) {
commit('getRouterByNameRequest')
return TcpService.getRouterByName(name)
.then(body => {
commit('getRouterByNameSuccess', body)
return body
})
.catch(error => {
commit('getRouterByNameFailure', error)
return Promise.reject(error)
})
}
export function getAllServices ({ commit }, params) {
commit('getAllServicesRequest')
return TcpService.getAllServices(params)
.then(body => {
commit('getAllServicesSuccess', body)
return body
})
.catch(error => {
commit('getAllServicesFailure', error)
return Promise.reject(error)
})
}
export function getServiceByName ({ commit }, name) {
commit('getServiceByNameRequest')
return TcpService.getServiceByName(name)
.then(body => {
commit('getServiceByNameSuccess', body)
return body
})
.catch(error => {
commit('getServiceByNameFailure', error)
return Promise.reject(error)
})
}

View file

@ -0,0 +1,27 @@
// ----------------------------
// all Routers
// ----------------------------
export function allRouters (state) {
return state.allRouters
}
// ----------------------------
// Router by Name
// ----------------------------
export function routerByName (state) {
return state.routerByName
}
// ----------------------------
// all Services
// ----------------------------
export function allServices (state) {
return state.allServices
}
// ----------------------------
// Service by Name
// ----------------------------
export function serviceByName (state) {
return state.serviceByName
}

View file

@ -0,0 +1,12 @@
import state from './state'
import * as getters from './getters'
import * as mutations from './mutations'
import * as actions from './actions'
export default {
namespaced: true,
getters,
mutations,
actions,
state
}

View file

@ -0,0 +1,75 @@
// ----------------------------
// Get All Routers
// ----------------------------
export function getAllRoutersRequest (state) {
state.allRouters.loading = true
}
export function getAllRoutersSuccess (state, body) {
state.allRouters = { items: body.data, total: body.total, loading: false }
}
export function getAllRoutersFailure (state, error) {
state.allRouters = { error }
}
export function getAllRoutersClear (state) {
state.allRouters = {}
}
// ----------------------------
// Get Router By Name
// ----------------------------
export function getRouterByNameRequest (state) {
state.routerByName.loading = true
}
export function getRouterByNameSuccess (state, body) {
state.routerByName = { item: body, loading: false }
}
export function getRouterByNameFailure (state, error) {
state.routerByName = { error }
}
export function getRouterByNameClear (state) {
state.routerByName = {}
}
// ----------------------------
// Get All Services
// ----------------------------
export function getAllServicesRequest (state) {
state.allServices.loading = true
}
export function getAllServicesSuccess (state, body) {
state.allServices = { items: body.data, total: body.total, loading: false }
}
export function getAllServicesFailure (state, error) {
state.allServices = { error }
}
export function getAllServicesClear (state) {
state.allServices = {}
}
// ----------------------------
// Get Service By Name
// ----------------------------
export function getServiceByNameRequest (state) {
state.serviceByName.loading = true
}
export function getServiceByNameSuccess (state, body) {
state.serviceByName = { item: body, loading: false }
}
export function getServiceByNameFailure (state, error) {
state.serviceByName = { error }
}
export function getServiceByNameClear (state) {
state.serviceByName = {}
}

View file

@ -0,0 +1,6 @@
export default {
allRouters: {},
routerByName: {},
allServices: {},
serviceByName: {}
}