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.
![Web UI Providers](docs/content/assets/img/dashboard-main.png)
![Web UI Health](docs/content/assets/img/dashboard-health.png)
![Web UI Providers](docs/content/assets/img/webui-dashboard.png)
## 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
{: .subtitle }
`TODO add schema`
<!--
TODO: add schema
-->
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
{: .subtitle }
`TODO: add schema`
<!--
TODO: add schema
-->
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
{: .subtitle }
`TODO: add schema`
<!--
TODO: add schema
-->
RegexRedirect redirect request from a scheme to another.

View file

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

View file

@ -3,7 +3,9 @@
Updating the Path Before Forwarding the Request (Using a Regex)
{: .subtitle }
`TODO: add schema`
<!--
TODO: add schema
-->
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
{: .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.
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
{: .subtitle }
`TODO: add schema`
<!--
TODO: add schema
-->
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.
!!! 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>
<img src="../../assets/img/dashboard-main.png" alt="Dashboard - Providers" />
<figcaption>The dashboard in action with Traefik listening to 3 different providers</figcaption>
</figure>
<figure>
<img src="../../assets/img/dashboard-health.png" alt="Dashboard - Health" />
<figcaption>The dashboard shows the health of the system.</figcaption>
<img src="../../assets/img/webui-dashboard.png" alt="Dashboard - Providers" />
<figcaption>The dashboard in action</figcaption>
</figure>
By default, the dashboard is available on `/` on port `:8080`.
!!! tip "Did You Know?"
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
@ -64,4 +55,4 @@ api:
!!! tip "Did You Know?"
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",
"version": "0.1.0",
"version": "2.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1465,6 +1465,7 @@
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dev": true,
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
@ -1619,7 +1620,8 @@
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
"array-includes": {
"version": "3.0.3",
@ -2091,6 +2093,7 @@
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dev": true,
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
@ -2108,6 +2111,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@ -2115,7 +2119,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
@ -2297,7 +2302,8 @@
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"dev": true
},
"cacache": {
"version": "11.3.3",
@ -2929,7 +2935,8 @@
"connect-history-api-fallback": {
"version": "1.6.0",
"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": {
"version": "1.1.0",
@ -2971,6 +2978,7 @@
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dev": true,
"requires": {
"safe-buffer": "5.1.2"
}
@ -2978,7 +2986,8 @@
"content-type": {
"version": "1.0.4",
"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": {
"version": "1.6.0",
@ -2992,12 +3001,14 @@
"cookie": {
"version": "0.4.0",
"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": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
"dev": true
},
"copy-concurrently": {
"version": "1.0.5",
@ -3575,7 +3586,8 @@
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"dev": true
},
"des.js": {
"version": "1.0.0",
@ -3590,7 +3602,8 @@
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"dev": true
},
"detect-node": {
"version": "2.0.4",
@ -3751,7 +3764,8 @@
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"dev": true
},
"ejs": {
"version": "2.6.2",
@ -3812,7 +3826,8 @@
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
"end-of-stream": {
"version": "1.4.1",
@ -3895,7 +3910,8 @@
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
@ -4296,7 +4312,8 @@
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
},
"eventemitter3": {
"version": "3.1.2",
@ -4398,6 +4415,7 @@
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dev": true,
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
@ -4435,6 +4453,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@ -4442,7 +4461,8 @@
"ms": {
"version": "2.0.0",
"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",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dev": true,
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
@ -4695,6 +4716,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@ -4702,7 +4724,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
@ -4844,7 +4867,8 @@
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"dev": true
},
"fragment-cache": {
"version": "0.2.1",
@ -4858,7 +4882,8 @@
"fresh": {
"version": "0.5.2",
"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": {
"version": "1.7.0",
@ -5515,6 +5540,7 @@
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dev": true,
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
@ -5526,7 +5552,8 @@
"inherits": {
"version": "2.0.3",
"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",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
@ -5873,7 +5901,8 @@
"ipaddr.js": {
"version": "1.9.0",
"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": {
"version": "2.1.0",
@ -6704,7 +6733,8 @@
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
},
"mem": {
"version": "4.3.0",
@ -6846,7 +6876,8 @@
"merge-descriptors": {
"version": "1.0.1",
"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": {
"version": "1.1.0",
@ -6874,7 +6905,8 @@
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"dev": true
},
"micromatch": {
"version": "4.0.2",
@ -6899,17 +6931,20 @@
"mime": {
"version": "1.6.0",
"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": {
"version": "1.40.0",
"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": {
"version": "2.1.24",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
"dev": true,
"requires": {
"mime-db": "1.40.0"
}
@ -7128,7 +7163,8 @@
"negotiator": {
"version": "0.6.2",
"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": {
"version": "2.6.1",
@ -7540,6 +7576,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dev": true,
"requires": {
"ee-first": "1.1.1"
}
@ -7803,7 +7840,8 @@
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true
},
"pascalcase": {
"version": "0.1.1",
@ -7856,7 +7894,8 @@
"path-to-regexp": {
"version": "0.1.7",
"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": {
"version": "3.0.0",
@ -8719,6 +8758,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
"dev": true,
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.0"
@ -8804,7 +8844,8 @@
"qs": {
"version": "6.7.0",
"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": {
"version": "1.0.5",
@ -8861,12 +8902,14 @@
"range-parser": {
"version": "1.2.1",
"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": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dev": true,
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
@ -9382,7 +9425,8 @@
"safe-buffer": {
"version": "5.1.2",
"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": {
"version": "1.1.0",
@ -9396,7 +9440,8 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sass-graph": {
"version": "2.2.4",
@ -9713,6 +9758,7 @@
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dev": true,
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
@ -9733,6 +9779,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
},
@ -9740,14 +9787,16 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"ms": {
"version": "2.1.1",
"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",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dev": true,
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
@ -9862,7 +9912,8 @@
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"dev": true
},
"sha.js": {
"version": "2.4.11",
@ -10321,7 +10372,8 @@
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"dev": true
},
"stdout-stream": {
"version": "1.4.1",
@ -10792,7 +10844,8 @@
"toidentifier": {
"version": "1.0.0",
"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": {
"version": "1.0.7",
@ -10885,6 +10938,7 @@
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
@ -10993,7 +11047,8 @@
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
"unquote": {
"version": "1.1.1",
@ -11163,7 +11218,8 @@
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true
},
"uuid": {
"version": "3.3.2",
@ -11184,7 +11240,8 @@
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"dev": true
},
"vendors": {
"version": "1.0.3",

View file

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

View file

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

View file

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

View file

@ -10,6 +10,15 @@ function getAll () {
})
}
export default {
getAll
function getByName (name) {
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>
<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="/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-space />
<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="text-h6 text-weight-bold">{{getName}}</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>
</q-card-section>
<q-card-section>
@ -104,7 +107,7 @@ export default {
} else {
result = num
}
return isNaN(result) ? 0 : result
return isNaN(result) || result < 0 ? 0 : result
},
getWarnings (inPercent = false) {
const num = this.data.warnings
@ -114,7 +117,7 @@ export default {
} else {
result = num
}
return isNaN(result) ? 0 : result
return isNaN(result) || result < 0 ? 0 : result
},
getErrors (inPercent = false) {
const num = this.data.errors
@ -124,7 +127,7 @@ export default {
} else {
result = num
}
return isNaN(result) ? 0 : result
return isNaN(result) || result < 0 ? 0 : result
},
getData () {
return [this.getSuccess(), this.getWarnings(), this.getErrors()]

View file

@ -1,9 +1,9 @@
<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>
<div class="row items-center no-wrap">
<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>
</q-card-section>
@ -16,10 +16,41 @@
<script>
export default {
name: 'PanelEntry',
props: ['address', 'name']
props: ['address', 'name', 'type', 'focus', 'exSize']
}
</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>

View file

@ -1,18 +1,15 @@
<template>
<q-card flat bordered>
<q-card flat bordered v-bind:class="['panel-feature']">
<q-card-section>
<div class="row items-center no-wrap">
<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>
</q-card-section>
<q-card-section>
<div class="text-h3 text-center text-weight-bold">
<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}]">
{{getVal}}
</q-chip>
@ -51,26 +48,37 @@ export default {
<style scoped lang="scss">
@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 {
border-radius: 12px;
border-width: 2px;
height: 56px;
padding: 12px 24px;
color: $primary;
&-string{
border-color: $app-text-grey;
font-size: 24px;
color: $app-text-grey !important;
font-size: 20px;
color: $app-text-grey;
background-color: rgba( $app-text-grey, .1 );
}
&-boolean{
font-size: 40px;
font-weight: 700;
border-color: $negative;
color: $negative !important;
color: $negative;
background-color: rgba( $negative, .1 );
&-true{
border-color: $positive;
color: $positive !important;
color: $positive;
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
$app-text-white: #ffffff;
$app-text-grey: #737373;
$app-text-purple: #9d0fb0;
$app-text-green: #24a1c1;
$app-text-green-2: #06a21d;
$app-text-marine: #06102a;
// Quasar
$primary: #06102a;

View file

@ -39,6 +39,10 @@ body {
color: $app-text-grey;
}
.bg-app-toggle {
background-color: rgba( $accent, .1 );
}
// Helps
.xs-text-center {
@include respond-to(sm, max) {
@ -60,6 +64,10 @@ body {
&-label {
font-size: inherit;
font-weight: inherit;
&-sub {
font-size: 16px;
font-weight: 600;
}
}
.q-icon + &-label {
margin-left: 8px;
@ -107,3 +115,45 @@ body {
.q-card {
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>
<q-page class="flex flex-center">
<h2>HTTP Middlewares</h2>
</q-page>
<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 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>
<style>
</style>
<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 {
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>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template>
<q-page class="flex flex-center">
<h2>HTTP Routers</h2>
</q-page>
<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 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>
<style>
</style>
<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 {
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>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template>
<q-page class="flex flex-center">
<h2>HTTP Services</h2>
</q-page>
<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 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>
<style>
</style>
<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 {
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>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template>
<q-page class="flex flex-center">
<h2>TCP Routers</h2>
</q-page>
<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 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>
<style>
</style>
<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 {
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>
<style scoped lang="scss">
</style>

View file

@ -1,14 +1,108 @@
<template>
<q-page class="flex flex-center">
<h2>TCP Services</h2>
</q-page>
<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 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>
<style>
</style>
<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 {
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>
<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 }) {
commit('getAllRequest')
return entrypointsService.getAll()
return EntrypointsService.getAll()
.then(body => {
commit('getAllSuccess', body)
return body
@ -12,3 +12,16 @@ export function getAll ({ commit }) {
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) {
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) {
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 entrypoints from './entrypoints'
import http from './http'
import tcp from './tcp'
Vue.use(Vuex)
@ -15,7 +17,9 @@ export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
core,
entrypoints
entrypoints,
http,
tcp
},
// 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: {}
}