From 7b030d8e5b9c7d597905c1a31382c5d27c213d81 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sun, 2 Jan 2022 23:09:31 +0000 Subject: [PATCH 01/40] :sparkles: Adds current Eth gas price widget --- docs/widgets.md | 28 ++- src/components/Widgets/EthGasPrices.vue | 228 ++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 + src/utils/defaults.js | 2 + 4 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/components/Widgets/EthGasPrices.vue diff --git a/docs/widgets.md b/docs/widgets.md index 852d516a..7829ce88 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -21,6 +21,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Public Holidays](#public-holidays) - [TFL Status](#tfl-status) - [Stock Price History](#stock-price-history) + - [ETH Gas Prices](#eth-gas-prices) - [Joke of the Day](#joke) - [XKCD Comics](#xkcd-comics) - [News Headlines](#news-headlines) @@ -129,7 +130,7 @@ Displays the weather (temperature and conditions) for the next few days for a gi **Field** | **Type** | **Required** | **Description** --- | --- | --- | --- -**`apiKey`** | `string` | Required | Your OpenWeatherMap API key. You can get one for free at [openweathermap.org](https://openweathermap.org/) +**`apiKey`** | `string` | Required | Your OpenWeatherMap API key. You can get one at [openweathermap.org](https://openweathermap.org/) or for free via the [OWM Student Plan](https://home.openweathermap.org/students) **`city`** | `string` | Required | A city name to use for fetching weather. This can also be a state code or country code, following the ISO-3166 format **`numDays`** | `number` | _Optional_ | The number of days to display of forecast info to display. Defaults to `4`, max `16` days **`units`** | `string` | _Optional_ | The units to use for displaying data, can be either `metric` or `imperial`. Defaults to `metric` @@ -526,6 +527,31 @@ Shows recent price history for a given publicly-traded stock or share --- +### ETH Gas Prices + +Renders the current Gas cost of transactions on the Ethereum network (in both GWEI and USD), along with recent historical prices. Useful for spotting a good time to transact. Uses data from [ethgas.watch](https://ethgas.watch/) + +

+ +##### Options + +_No config options._ + +##### Example + +```yaml +- type: eth-gas-prices +``` + +##### Info +- **CORS**: 🟒 Enabled +- **Auth**: 🟒 Not Required +- **Price**: 🟒 Free +- **Host**: Managed Instance or Self-Hosted (see [wslyvh/ethgaswatch](https://github.com/wslyvh/ethgaswatch)) +- **Privacy**: ⚫ No Policy Available + +--- + ### Joke Renders a programming or generic joke. Data is fetched from the [JokesAPI](https://github.com/Sv443/JokeAPI) by @Sv443. All fields are optional. diff --git a/src/components/Widgets/EthGasPrices.vue b/src/components/Widgets/EthGasPrices.vue new file mode 100644 index 00000000..3ce6d455 --- /dev/null +++ b/src/components/Widgets/EthGasPrices.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 3b810030..65610814 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -67,6 +67,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/CryptoWatchList.vue'), CveVulnerabilities: () => import('@/components/Widgets/CveVulnerabilities.vue'), EmbedWidget: () => import('@/components/Widgets/EmbedWidget.vue'), + EthGasPrices: () => import('@/components/Widgets/EthGasPrices.vue'), ExchangeRates: () => import('@/components/Widgets/ExchangeRates.vue'), Flights: () => import('@/components/Widgets/Flights.vue'), GitHubTrending: () => import('@/components/Widgets/GitHubTrending.vue'), diff --git a/src/utils/defaults.js b/src/utils/defaults.js index d952f23c..c353a5bb 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -213,6 +213,8 @@ module.exports = { cryptoPrices: 'https://api.coingecko.com/api/v3/coins/', cryptoWatchList: 'https://api.coingecko.com/api/v3/coins/markets/', cveVulnerabilities: 'https://www.cvedetails.com/json-feed.php', + ethGasPrices: 'https://ethgas.watch/api/gas', + ethGasHistory: 'https://ethgas.watch/api/gas/trend', exchangeRates: 'https://v6.exchangerate-api.com/v6/', flights: 'https://aerodatabox.p.rapidapi.com/flights/airports/icao/', githubTrending: 'https://gh-trending-repos.herokuapp.com/', From 1c8021964bf62aca233f55b8f4ef0a8516494c6a Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Mon, 3 Jan 2022 09:30:51 +0000 Subject: [PATCH 02/40] :adhesive_bandage: Fix capitalization in weather widget (#402) --- src/components/Widgets/Weather.vue | 64 +++++++--------------- src/components/Widgets/WeatherForecast.vue | 62 +++++++-------------- 2 files changed, 38 insertions(+), 88 deletions(-) diff --git a/src/components/Widgets/Weather.vue b/src/components/Widgets/Weather.vue index a2928d4b..364e1bb4 100644 --- a/src/components/Widgets/Weather.vue +++ b/src/components/Widgets/Weather.vue @@ -23,7 +23,6 @@ @@ -218,6 +189,9 @@ export default { &:not(:last-child) { border-bottom: 1px dashed var(--widget-text-color); } + span.lbl { + text-transform: capitalize; + } } } } diff --git a/src/components/Widgets/WeatherForecast.vue b/src/components/Widgets/WeatherForecast.vue index 83f883f8..3f77e4ea 100644 --- a/src/components/Widgets/WeatherForecast.vue +++ b/src/components/Widgets/WeatherForecast.vue @@ -31,8 +31,8 @@ @@ -266,6 +239,9 @@ export default { margin: 0.1rem 0.5rem; padding: 0.1rem 0; color: var(--widget-text-color); + span.lbl { + text-transform: capitalize; + } &:not(:last-child) { border-bottom: 1px dashed var(--widget-text-color); } From d516bb02fa2ae3540e3f20e7e63eaf9df56eb58e Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Mon, 3 Jan 2022 09:39:51 +0000 Subject: [PATCH 03/40] :speech_balloon: Adds option for clock to have `customCityName` (#402) --- docs/widgets.md | 1 + src/components/Widgets/Clock.vue | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/widgets.md b/docs/widgets.md index 7829ce88..b35c23c6 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -69,6 +69,7 @@ A simple, live-updating time and date widget with time-zone support. All fields --- | --- | --- | --- **`timeZone`** | `string` | _Optional_ | The time zone to display date and time in.
Specified as Region/City, for example: `Australia/Melbourne`. See the [Time Zone DB](https://timezonedb.com/time-zones) for a full list of supported TZs. Defaults to the browser / device's local time **`format`** | `string` | _Optional_ | A country code for displaying the date and time in local format.
Specified as `[ISO-3166]-[ISO-639]`, for example: `en-AU`. See [here](https://www.fincher.org/Utilities/CountryLanguageList.shtml) for a full list of locales. Defaults to the browser / device's region +**`customCityName`** | `string` | _Optional_ | By default the city from the time-zone is shown, but setting this value will override that text **`hideDate`** | `boolean` | _Optional_ | If set to `true`, the date and city will not be shown. Defaults to `false` ##### Example diff --git a/src/components/Widgets/Clock.vue b/src/components/Widgets/Clock.vue index 04bae1cc..d4aa0aea 100644 --- a/src/components/Widgets/Clock.vue +++ b/src/components/Widgets/Clock.vue @@ -1,7 +1,7 @@ + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 65610814..707602e6 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -228,8 +228,8 @@ @error="handleError" :ref="widgetRef" /> - +
{{ handleError('Widget type was not found') }}
@@ -302,6 +309,7 @@ export default { StockPriceChart: () => import('@/components/Widgets/StockPriceChart.vue'), SystemInfo: () => import('@/components/Widgets/SystemInfo.vue'), TflStatus: () => import('@/components/Widgets/TflStatus.vue'), + WalletBalance: () => import('@/components/Widgets/WalletBalance.vue'), Weather: () => import('@/components/Widgets/Weather.vue'), WeatherForecast: () => import('@/components/Widgets/WeatherForecast.vue'), XkcdComic: () => import('@/components/Widgets/XkcdComic.vue'), diff --git a/src/mixins/WidgetMixin.js b/src/mixins/WidgetMixin.js index 8ae161a8..4636b93c 100644 --- a/src/mixins/WidgetMixin.js +++ b/src/mixins/WidgetMixin.js @@ -57,8 +57,10 @@ const WidgetMixin = { this.finishLoading(); }, /* Used as v-tooltip, pass text content in, and will show on hover */ - tooltip(content) { - return { content, trigger: 'hover focus', delay: 250 }; + tooltip(content, html = false) { + return { + content, html, trigger: 'hover focus', delay: 250, + }; }, /* Makes data request, returns promise */ makeRequest(endpoint, options) { diff --git a/src/utils/defaults.js b/src/utils/defaults.js index c353a5bb..30b3e752 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -228,6 +228,8 @@ module.exports = { sportsScores: 'https://www.thesportsdb.com/api/v1/json', stockPriceChart: 'https://www.alphavantage.co/query', tflStatus: 'https://api.tfl.gov.uk/line/mode/tube/status', + walletBalance: 'https://api.blockcypher.com/v1', + walletQrCode: 'https://www.bitcoinqrcodemaker.com/api', weather: 'https://api.openweathermap.org/data/2.5/weather', weatherForecast: 'https://api.openweathermap.org/data/2.5/forecast/daily', xkcdComic: 'https://xkcd.vercel.app/', From f5c11b3dc64ea99fe87644a80001a7f2224de836 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Mon, 3 Jan 2022 18:31:49 +0000 Subject: [PATCH 06/40] :sparkles: Adds Covid status widget --- docs/widgets.md | 48 ++++++ src/components/Widgets/CovidStats.vue | 229 ++++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 + src/utils/defaults.js | 1 + 4 files changed, 286 insertions(+) create mode 100644 src/components/Widgets/CovidStats.vue diff --git a/docs/widgets.md b/docs/widgets.md index 4dbddaa3..022cc63e 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -23,6 +23,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [TFL Status](#tfl-status) - [Stock Price History](#stock-price-history) - [ETH Gas Prices](#eth-gas-prices) + - [Covid-19 Status](#covid-19-status) - [Joke of the Day](#joke) - [XKCD Comics](#xkcd-comics) - [News Headlines](#news-headlines) @@ -586,6 +587,53 @@ _No config options._ --- +### Covid-19 Status + +Keep track of the current COVID-19 status. Optionally also show cases by country, and a time-series chart. Uses live data from various sources, computed by [disease.sh](https://disease.sh/) + +

+ +##### Options + +**Field** | **Type** | **Required** | **Description** +--- | --- | --- | --- +**`showChart`** | `boolean` | _Optional_ | Also display a time-series chart showing number of recent cases +**`showCountries`** | `boolean` | _Optional_ | +**`numDays`** | `number` | _Optional_ | Specify number of days worth of history to render on the chart +**`countries`** | `string[]` | _Optional_ | An array of countries to display, specified by their [ISO-3 codes](https://www.iso.org/obp/ui). Leave blank to show all, sorted by most cases +**`limit`** | `number` | _Optional_ | If showing all countries, set a limit for number of results to return. Defaults to `10`, no maximum + + +##### Example + +```yaml +- type: covid-stats +``` + +Or + +```yaml +- type: covid-stats + options: + showChart: true + showCountries: true + countries: + - GBR + - USA + - IND + - RUS +``` + +##### Info +- **CORS**: 🟒 Enabled +- **Auth**: 🟒 Not Required +- **Price**: 🟒 Free +- **Host**: Managed Instance or Self-Hosted (see [disease-sh/api](https://github.com/disease-sh/api)) +- **Privacy**: ⚫ No Policy Available +- **Conditions**: [Terms of Use](https://github.com/disease-sh/api/blob/master/TERMS.md) + +--- + ### Joke Renders a programming or generic joke. Data is fetched from the [JokesAPI](https://github.com/Sv443/JokeAPI) by @Sv443. All fields are optional. diff --git a/src/components/Widgets/CovidStats.vue b/src/components/Widgets/CovidStats.vue new file mode 100644 index 00000000..a7348631 --- /dev/null +++ b/src/components/Widgets/CovidStats.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 707602e6..058a6ec9 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -60,6 +60,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/Apod.vue'), Clock: () => import('@/components/Widgets/Clock.vue'), CodeStats: () => import('@/components/Widgets/CodeStats.vue'), + CovidStats: () => import('@/components/Widgets/CovidStats.vue'), CryptoPriceChart: () => import('@/components/Widgets/CryptoPriceChart.vue'), CryptoWatchList: () => import('@/components/Widgets/CryptoWatchList.vue'), CveVulnerabilities: () => import('@/components/Widgets/CveVulnerabilities.vue'), diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 30b3e752..59db5ce8 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -210,6 +210,7 @@ module.exports = { widgetApiEndpoints: { astronomyPictureOfTheDay: 'https://apodapi.herokuapp.com/api', codeStats: 'https://codestats.net/', + covidStats: 'https://disease.sh/v3/covid-19', cryptoPrices: 'https://api.coingecko.com/api/v3/coins/', cryptoWatchList: 'https://api.coingecko.com/api/v3/coins/markets/', cveVulnerabilities: 'https://www.cvedetails.com/json-feed.php', From a889c0e78a78e127f398e11821a2033978d3f8b9 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 4 Jan 2022 22:45:36 +0000 Subject: [PATCH 07/40] :package: Built an SVG gauge chart component --- src/components/Charts/Gauge.vue | 361 ++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 src/components/Charts/Gauge.vue diff --git a/src/components/Charts/Gauge.vue b/src/components/Charts/Gauge.vue new file mode 100644 index 00000000..63fcf684 --- /dev/null +++ b/src/components/Charts/Gauge.vue @@ -0,0 +1,361 @@ + + + + + From 37e8a003f947ebb5838f84f0e2f5712125b9cd4f Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Wed, 5 Jan 2022 21:22:34 +0000 Subject: [PATCH 08/40] :sparkles: Builds CPU Gauge Chart --- src/components/Widgets/GlCpuGauge.vue | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/components/Widgets/GlCpuGauge.vue diff --git a/src/components/Widgets/GlCpuGauge.vue b/src/components/Widgets/GlCpuGauge.vue new file mode 100644 index 00000000..beda0af7 --- /dev/null +++ b/src/components/Widgets/GlCpuGauge.vue @@ -0,0 +1,64 @@ + + + + + From 9ebdf67a442985b7c45bc5b1027d093f03ed7347 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 7 Jan 2022 21:32:58 +0000 Subject: [PATCH 09/40] :package: Build a percentage chart component --- src/components/Charts/PercentageChart.vue | 111 ++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/components/Charts/PercentageChart.vue diff --git a/src/components/Charts/PercentageChart.vue b/src/components/Charts/PercentageChart.vue new file mode 100644 index 00000000..08fc2a9d --- /dev/null +++ b/src/components/Charts/PercentageChart.vue @@ -0,0 +1,111 @@ + + + + + From 4a14b27cf3564500c5252a59375af14ebad8f6b3 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 7 Jan 2022 23:25:32 +0000 Subject: [PATCH 10/40] :construction: Working on CPU Usage Widgets --- src/components/Charts/Gauge.vue | 9 ++- src/components/Charts/PercentageChart.vue | 1 + src/components/Widgets/GlCpuCores.vue | 93 +++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 20 ++++- src/mixins/WidgetMixin.js | 7 +- src/utils/MiscHelpers.js | 9 ++- 6 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 src/components/Widgets/GlCpuCores.vue diff --git a/src/components/Charts/Gauge.vue b/src/components/Charts/Gauge.vue index 63fcf684..d4edc5cb 100644 --- a/src/components/Charts/Gauge.vue +++ b/src/components/Charts/Gauge.vue @@ -8,7 +8,7 @@ - + @@ -192,7 +192,7 @@ export default { { offset: 0, color: '#20e253' }, { offset: 30, color: '#f6f000' }, { offset: 60, color: '#fca016' }, - { offset: 90, color: '#f80363' }, + { offset: 80, color: '#f80363' }, ]), }, /* Color of the base of the gauge */ @@ -200,6 +200,11 @@ export default { type: String, default: '#DDDDDD', }, + /* The inset shadow color */ + shadowColor: { + type: String, + default: '#8787871a', + }, /* Scale interval, won't display any scall if 0 or `null` */ scaleInterval: { type: Number, diff --git a/src/components/Charts/PercentageChart.vue b/src/components/Charts/PercentageChart.vue index 08fc2a9d..339fb8b8 100644 --- a/src/components/Charts/PercentageChart.vue +++ b/src/components/Charts/PercentageChart.vue @@ -40,6 +40,7 @@ export default { blocks() { let startPositionSum = 0; const results = []; + console.log(this.values); const total = this.values.reduce((prev, cur) => (prev.size || prev) + cur.size); const multiplier = this.showAsPercent ? 100 / total : 1; this.values.forEach((value, index) => { diff --git a/src/components/Widgets/GlCpuCores.vue b/src/components/Widgets/GlCpuCores.vue new file mode 100644 index 00000000..6dde6591 --- /dev/null +++ b/src/components/Widgets/GlCpuCores.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 058a6ec9..cd73b403 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -95,6 +95,13 @@ @error="handleError" :ref="widgetRef" /> + - + import('@/components/Widgets/ExchangeRates.vue'), Flights: () => import('@/components/Widgets/Flights.vue'), GitHubTrending: () => import('@/components/Widgets/GitHubTrending.vue'), + GlCpuCores: () => import('@/components/Widgets/GlCpuCores.vue'), + GlCpuGauge: () => import('@/components/Widgets/GlCpuGauge.vue'), GitHubProfile: () => import('@/components/Widgets/GitHubProfile.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), diff --git a/src/mixins/WidgetMixin.js b/src/mixins/WidgetMixin.js index 4636b93c..d8aa7310 100644 --- a/src/mixins/WidgetMixin.js +++ b/src/mixins/WidgetMixin.js @@ -17,6 +17,7 @@ const WidgetMixin = { data: () => ({ progress: new ProgressBar({ color: 'var(--progress-bar)' }), overrideProxyChoice: false, + disableLoader: false, // Prevent ever showing the loader }), /* When component mounted, fetch initial data */ mounted() { @@ -44,8 +45,10 @@ const WidgetMixin = { }, /* When a data request update starts, show loader */ startLoading() { - this.$emit('loading', true); - this.progress.start(); + if (!this.disableLoader) { + this.$emit('loading', true); + this.progress.start(); + } }, /* When a data request finishes, hide loader */ finishLoading() { diff --git a/src/utils/MiscHelpers.js b/src/utils/MiscHelpers.js index 4edda362..bee89e6f 100644 --- a/src/utils/MiscHelpers.js +++ b/src/utils/MiscHelpers.js @@ -86,7 +86,8 @@ export const showNumAsThousand = (bigNum) => { /* Capitalizes the first letter of each word within a string */ export const capitalize = (str) => { - return str.replace(/\w\S*/g, (w) => (w.replace(/^\w/, (c) => c.toUpperCase()))); + const words = str.replaceAll('_', ' ').replaceAll('-', ' '); + return words.replace(/\w\S*/g, (w) => (w.replace(/^\w/, (c) => c.toUpperCase()))); }; /* Round price to appropriate number of decimals */ @@ -122,6 +123,12 @@ export const getTimeAgo = (dateTime) => { return 'unknown'; }; +/* Given the name of a CSS variable, returns it's value */ +export const getValueFromCss = (colorVar) => { + const cssProps = getComputedStyle(document.documentElement); + return cssProps.getPropertyValue(`--${colorVar}`).trim(); +}; + /* Given a currency code, return the corresponding unicode symbol */ export const findCurrencySymbol = (currencyCode) => { const code = currencyCode.toUpperCase().trim(); From e253a35b05ca38f3959251754de6e04e03b9c3fa Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 8 Jan 2022 11:58:38 +0000 Subject: [PATCH 11/40] :art: Moves widget update logic into mixin --- src/components/Widgets/WidgetBase.vue | 28 +---------------------- src/mixins/WidgetMixin.js | 32 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index cd73b403..458f2f17 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -346,7 +346,6 @@ export default { loading: false, error: false, errorMsg: null, - updater: null, // Stores interval }), computed: { /* Returns the widget type, shows error if not specified */ @@ -361,27 +360,13 @@ export default { widgetOptions() { const options = this.widget.options || {}; const useProxy = !!this.widget.useProxy; - const updateInterval = this.widget.updateInterval || 0; + const updateInterval = this.widget.updateInterval || null; return { useProxy, updateInterval, ...options }; }, /* A unique string to reference the widget by */ widgetRef() { return `widget-${this.widgetType}-${this.index}`; }, - /* Returns either `false` or a number in ms to continuously update widget data */ - updateInterval() { - const usersInterval = this.widget.updateInterval; - if (!usersInterval) return 0; - // If set to `true`, then default to 30 seconds - if (typeof usersInterval === 'boolean') return 30 * 1000; - // If set to a number, and within valid range, return user choice - if (typeof usersInterval === 'number' - && usersInterval >= 10 - && usersInterval < 7200) { - return usersInterval * 1000; - } - return 0; - }, }, methods: { /* Calls update data method on widget */ @@ -402,17 +387,6 @@ export default { this.loading = loading; }, }, - mounted() { - // If continuous updates enabled, create interval - if (this.updateInterval) { - this.updater = setInterval(() => { - this.update(); - }, this.updateInterval); - } - }, - beforeDestroy() { - clearInterval(this.updater); - }, }; diff --git a/src/mixins/WidgetMixin.js b/src/mixins/WidgetMixin.js index d8aa7310..e5ebeded 100644 --- a/src/mixins/WidgetMixin.js +++ b/src/mixins/WidgetMixin.js @@ -17,11 +17,22 @@ const WidgetMixin = { data: () => ({ progress: new ProgressBar({ color: 'var(--progress-bar)' }), overrideProxyChoice: false, + overrideUpdateInterval: null, disableLoader: false, // Prevent ever showing the loader + updater: null, // Stores interval }), /* When component mounted, fetch initial data */ mounted() { this.fetchData(); + if (this.updateInterval) { + this.continuousUpdates(); + this.disableLoader = true; + } + }, + beforeDestroy() { + if (this.updater) { + clearInterval(this.updater); + } }, computed: { proxyReqEndpoint() { @@ -31,6 +42,23 @@ const WidgetMixin = { useProxy() { return this.options.useProxy || this.overrideProxyChoice; }, + /* Returns either a number in ms to continuously update widget data. Or 0 for no updates */ + updateInterval() { + const usersInterval = this.options.updateInterval; + if (usersInterval === null && this.overrideUpdateInterval) { + return this.overrideUpdateInterval * 1000; + } + if (!usersInterval) return 0; + // If set to `true`, then default to 30 seconds + if (typeof usersInterval === 'boolean') return 30 * 1000; + // If set to a number, and within valid range, return user choice + if (typeof usersInterval === 'number' + && usersInterval >= 2 + && usersInterval <= 7200) { + return usersInterval * 1000; + } + return 0; + }, }, methods: { /* Re-fetches external data, called by parent. Usually overridden by widget */ @@ -38,6 +66,10 @@ const WidgetMixin = { this.startLoading(); this.fetchData(); }, + /* If continuous updates enabled, create interval */ + continuousUpdates() { + this.updater = setInterval(() => { this.update(); }, this.updateInterval); + }, /* Called when an error occurs. Logs to handler, and passes to parent component */ error(msg, stackTrace) { ErrorHandler(msg, stackTrace); From 98840879759d49920e8e45e2baeecd9abe5fc895 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 8 Jan 2022 11:59:40 +0000 Subject: [PATCH 12/40] :package: Adds legend, and customizable height and title to percentage chart component --- src/components/Charts/PercentageChart.vue | 122 +++++++++++++--------- 1 file changed, 73 insertions(+), 49 deletions(-) diff --git a/src/components/Charts/PercentageChart.vue b/src/components/Charts/PercentageChart.vue index 339fb8b8..05c6aaba 100644 --- a/src/components/Charts/PercentageChart.vue +++ b/src/components/Charts/PercentageChart.vue @@ -1,20 +1,26 @@ diff --git a/src/components/Widgets/GlCpuGauge.vue b/src/components/Widgets/GlCpuGauge.vue index beda0af7..2b466686 100644 --- a/src/components/Widgets/GlCpuGauge.vue +++ b/src/components/Widgets/GlCpuGauge.vue @@ -1,15 +1,24 @@ + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 458f2f17..55d555c0 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -123,6 +123,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/GitHubTrending.vue'), GlCpuCores: () => import('@/components/Widgets/GlCpuCores.vue'), GlCpuGauge: () => import('@/components/Widgets/GlCpuGauge.vue'), + GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), GitHubProfile: () => import('@/components/Widgets/GitHubProfile.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), From 4e64ccff3b9afa44c7fc68318d5fa5e303260f2f Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 8 Jan 2022 13:55:26 +0000 Subject: [PATCH 15/40] :sparkles: Adds current memory usage widget --- docs/widgets.md | 17 ++++ src/components/Widgets/GlMemGauge.vue | 136 ++++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 10 +- src/utils/MiscHelpers.js | 9 ++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 src/components/Widgets/GlMemGauge.vue diff --git a/docs/widgets.md b/docs/widgets.md index b21f63a4..3cfda213 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -46,6 +46,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [CPU Usage Current](#current-cpu-usage) - [CPU Usage Per Core](#cpu-usage-per-core) - [CPU Usage History](#cpu-usage-history) + - [Memory Usage Current](#current-memory-usage) - [Dynamic Widgets](#dynamic-widgets) - [Iframe Widget](#iframe-widget) - [HTML Embed Widget](#html-embedded-widget) @@ -1240,6 +1241,22 @@ Recent CPU usage history, across all cores, and displayed by user and system --- +### Current Memory Usage + +Real-time memory usage gauge, with more info visible on click + +

+ +##### Example + +```yaml +- type: gl-current-mem + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ## Dynamic Widgets ### Iframe Widget diff --git a/src/components/Widgets/GlMemGauge.vue b/src/components/Widgets/GlMemGauge.vue new file mode 100644 index 00000000..c43e400d --- /dev/null +++ b/src/components/Widgets/GlMemGauge.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 55d555c0..0e1df96f 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -130,6 +130,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/ExchangeRates.vue'), Flights: () => import('@/components/Widgets/Flights.vue'), GitHubTrending: () => import('@/components/Widgets/GitHubTrending.vue'), + GitHubProfile: () => import('@/components/Widgets/GitHubProfile.vue'), GlCpuCores: () => import('@/components/Widgets/GlCpuCores.vue'), GlCpuGauge: () => import('@/components/Widgets/GlCpuGauge.vue'), GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), - GitHubProfile: () => import('@/components/Widgets/GitHubProfile.vue'), + GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), Jokes: () => import('@/components/Widgets/Jokes.vue'), diff --git a/src/utils/MiscHelpers.js b/src/utils/MiscHelpers.js index bee89e6f..5e998048 100644 --- a/src/utils/MiscHelpers.js +++ b/src/utils/MiscHelpers.js @@ -90,6 +90,15 @@ export const capitalize = (str) => { return words.replace(/\w\S*/g, (w) => (w.replace(/^\w/, (c) => c.toUpperCase()))); }; +/* Given a mem size in bytes, will return it in appropriate unit */ +export const convertBytes = (bytes, decimals = 2) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${parseFloat((bytes / (k ** i)).toFixed(decimals))} ${sizes[i]}`; +}; + /* Round price to appropriate number of decimals */ export const roundPrice = (price) => { if (Number.isNaN(price)) return price; From e3b3f3c5a850374269342a1622031f2e6fc2ab10 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 8 Jan 2022 14:10:04 +0000 Subject: [PATCH 16/40] :sparkles: Adds current memory history widget --- docs/widgets.md | 25 +++++++ src/components/Widgets/GlMemHistory.vue | 86 +++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 +++ 3 files changed, 119 insertions(+) create mode 100644 src/components/Widgets/GlMemHistory.vue diff --git a/docs/widgets.md b/docs/widgets.md index 3cfda213..856d5efe 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -47,6 +47,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [CPU Usage Per Core](#cpu-usage-per-core) - [CPU Usage History](#cpu-usage-history) - [Memory Usage Current](#current-memory-usage) + - [Memory Usage History](#memory-usage-history) - [Dynamic Widgets](#dynamic-widgets) - [Iframe Widget](#iframe-widget) - [HTML Embed Widget](#html-embedded-widget) @@ -1257,6 +1258,30 @@ Real-time memory usage gauge, with more info visible on click --- +### Memory Usage History + +Recent memory usage chart + +

+ +##### Options + +**Field** | **Type** | **Required** | **Description** +--- | --- | --- | --- +**`limit`** | `number` | _Optional_ | Limit the number of results returned, rendering more data points will take longer to load. Defaults to `100` + + +##### Example + +```yaml +- type: gl-mem-history + options: + hostname: http://localhost:61208 + limit: 80 +``` + +--- + ## Dynamic Widgets ### Iframe Widget diff --git a/src/components/Widgets/GlMemHistory.vue b/src/components/Widgets/GlMemHistory.vue new file mode 100644 index 00000000..a874b22f --- /dev/null +++ b/src/components/Widgets/GlMemHistory.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 0e1df96f..597cb98d 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -137,6 +137,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/GlCpuGauge.vue'), GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), + GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), Jokes: () => import('@/components/Widgets/Jokes.vue'), From 3a3364f15649a8deab83e598051c51246ebaf050 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 8 Jan 2022 17:06:36 +0000 Subject: [PATCH 17/40] :sparkles: Adds file system widget --- docs/widgets.md | 18 +++++- src/assets/locales/en.json | 8 ++- src/components/Widgets/GlDiskSpace.vue | 82 ++++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 +++ 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 src/components/Widgets/GlDiskSpace.vue diff --git a/docs/widgets.md b/docs/widgets.md index 856d5efe..357b104e 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -1246,7 +1246,7 @@ Recent CPU usage history, across all cores, and displayed by user and system Real-time memory usage gauge, with more info visible on click -

+

##### Example @@ -1282,6 +1282,22 @@ Recent memory usage chart --- +### Disk Space + +List connected disks, showing free / used space and other info (file system, mount point and space available) + +

+ +##### Example + +```yaml +- type: gl-disk-space + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ## Dynamic Widgets ### Iframe Widget diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 70713065..dc5c5e99 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -269,6 +269,12 @@ "mem-breakdown-title": "Memory Breakdown", "load-chart-title": "System Load" }, + "glances": { + "disk-space-free": "Free", + "disk-space-used": "Used", + "disk-mount-point": "Mount Point", + "disk-file-system": "File System" + }, "system-info": { "uptime": "Uptime" }, @@ -281,4 +287,4 @@ "good-service-rest": "Good Service on all other Lines" } } -} +} \ No newline at end of file diff --git a/src/components/Widgets/GlDiskSpace.vue b/src/components/Widgets/GlDiskSpace.vue new file mode 100644 index 00000000..93943318 --- /dev/null +++ b/src/components/Widgets/GlDiskSpace.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 597cb98d..7ee6b551 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -130,6 +130,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/GlCpuCores.vue'), GlCpuGauge: () => import('@/components/Widgets/GlCpuGauge.vue'), GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), + GlDiskSpace: () => import('@/components/Widgets/GlDiskSpace.vue'), GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), From 8b1282c5e102bb2b74c23e6e2610b36948345f0d Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 15 Jan 2022 05:12:24 +0000 Subject: [PATCH 18/40] :adhesive_bandage: Fixes clock date update delay at midnight (#402) --- src/components/Widgets/Clock.vue | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/components/Widgets/Clock.vue b/src/components/Widgets/Clock.vue index d4aa0aea..aa5ccc51 100644 --- a/src/components/Widgets/Clock.vue +++ b/src/components/Widgets/Clock.vue @@ -15,9 +15,9 @@ export default { mixins: [WidgetMixin], data() { return { - timeUpdateInterval: null, // Stores setInterval function time: null, // Current time string date: null, // Current date string + timeUpdateInterval: null, // Stores setInterval function }; }, computed: { @@ -61,16 +61,11 @@ export default { created() { // Set initial date and time this.update(); - // Update the date every hour, and the time each second - this.timeUpdateInterval = setInterval(() => { - this.setTime(); - const now = new Date(); - if (now.getMinutes() === 0 && now.getSeconds() === 0) { - this.setDate(); - } - }, 1000); + // Update the time and date every second (1000 ms) + this.timeUpdateInterval = setInterval(this.update, 1000); }, beforeDestroy() { + // Remove the clock interval listener clearInterval(this.timeUpdateInterval); }, }; @@ -102,6 +97,7 @@ export default { font-size: 4rem; padding: 0.5rem; text-align: center; + font-variant-numeric: tabular-nums; font-family: Digital, var(--font-monospace); } } From 323123e6c0ba1b984d73bd554cd435838f44c6fa Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 15 Jan 2022 21:28:25 +0000 Subject: [PATCH 19/40] :adhesive_bandage: Remove continious update from disk space widget --- src/components/Widgets/GlDiskSpace.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/Widgets/GlDiskSpace.vue b/src/components/Widgets/GlDiskSpace.vue index 93943318..3400e770 100644 --- a/src/components/Widgets/GlDiskSpace.vue +++ b/src/components/Widgets/GlDiskSpace.vue @@ -53,9 +53,6 @@ export default { this.disks = diskData; }, }, - created() { - this.overrideUpdateInterval = 2; - }, mounted() { this.background = getValueFromCss('widget-accent-color'); }, From 63a0d188130f3be97bd78aeeb7321de63412ca59 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 15 Jan 2022 21:30:43 +0000 Subject: [PATCH 20/40] :sparkles: Adds a disk IO widget --- docs/widgets.md | 18 ++++ src/components/Widgets/GlDiskIo.vue | 127 ++++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 ++ 3 files changed, 153 insertions(+) create mode 100644 src/components/Widgets/GlDiskIo.vue diff --git a/docs/widgets.md b/docs/widgets.md index 357b104e..74f3b349 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -48,6 +48,8 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [CPU Usage History](#cpu-usage-history) - [Memory Usage Current](#current-memory-usage) - [Memory Usage History](#memory-usage-history) + - [Disk Space](#disk-space) + - [Disk IO](#disk-io) - [Dynamic Widgets](#dynamic-widgets) - [Iframe Widget](#iframe-widget) - [HTML Embed Widget](#html-embedded-widget) @@ -1298,6 +1300,22 @@ List connected disks, showing free / used space and other info (file system, mou --- +### Disk IO + +Shows real-time read and write speeds and operations per sec for each disk + +

+ +##### Example + +```yaml +- type: gl-disk-io + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ## Dynamic Widgets ### Iframe Widget diff --git a/src/components/Widgets/GlDiskIo.vue b/src/components/Widgets/GlDiskIo.vue new file mode 100644 index 00000000..75c1ab79 --- /dev/null +++ b/src/components/Widgets/GlDiskIo.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 7ee6b551..25611792 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -130,6 +130,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/GlCpuCores.vue'), GlCpuGauge: () => import('@/components/Widgets/GlCpuGauge.vue'), GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), + GlDiskIo: () => import('@/components/Widgets/GlDiskIo.vue'), GlDiskSpace: () => import('@/components/Widgets/GlDiskSpace.vue'), GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), From 9148195b848aaec4c5bbcc4b350b4b47b427af2e Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 18 Jan 2022 16:03:44 +0000 Subject: [PATCH 21/40] :sparkles: Built system load and load history widgets --- docs/widgets.md | 92 ++++++++++++++++++----- src/assets/locales/en.json | 5 +- src/components/Widgets/GlDiskIo.vue | 4 +- src/components/Widgets/GlLoadHistory.vue | 95 ++++++++++++++++++++++++ src/components/Widgets/GlSystemLoad.vue | 68 +++++++++++++++++ src/components/Widgets/WidgetBase.vue | 16 ++++ 6 files changed, 257 insertions(+), 23 deletions(-) create mode 100644 src/components/Widgets/GlLoadHistory.vue create mode 100644 src/components/Widgets/GlSystemLoad.vue diff --git a/docs/widgets.md b/docs/widgets.md index 74f3b349..99f2af6f 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -7,7 +7,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a > Adding / editing widgets through the UI isn't yet supported, you will need to do this in the YAML config file. ##### Contents -- [General Widgets](#general-widgets) +- **[General Widgets](#general-widgets)** - [Clock](#clock) - [Weather](#weather) - [Weather Forecast](#weather-forecast) @@ -32,7 +32,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [GitHub Trending](#github-trending) - [GitHub Profile Stats](#github-profile-stats) - [Public IP Address](#public-ip) -- [Self-Hosted Services Widgets](#self-hosted-services-widgets) +- **[Self-Hosted Services Widgets](#self-hosted-services-widgets)** - [System Info](#system-info) - [Cron Monitoring](#cron-monitoring-health-checks) - [CPU History](#cpu-history-netdata) @@ -42,7 +42,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Pi Hole Queries](#pi-hole-queries) - [Recent Traffic](#recent-traffic) - [Stat Ping Statuses](#stat-ping-statuses) -- [System Resource Monitoring](#system-resource-monitoring) +- **[System Resource Monitoring](#system-resource-monitoring)** - [CPU Usage Current](#current-cpu-usage) - [CPU Usage Per Core](#cpu-usage-per-core) - [CPU Usage History](#cpu-usage-history) @@ -50,16 +50,20 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Memory Usage History](#memory-usage-history) - [Disk Space](#disk-space) - [Disk IO](#disk-io) -- [Dynamic Widgets](#dynamic-widgets) + - [System Load](#system-load) + - [System Load History](#system-load-history) +- **[Dynamic Widgets](#dynamic-widgets)** - [Iframe Widget](#iframe-widget) - [HTML Embed Widget](#html-embedded-widget) - [API Response](#api-response) - [Prometheus Data](#prometheus-data) - [Data Feed](#data-feed) -- [Usage & Customizations](#usage--customizations) +- **[Usage & Customizations](#usage--customizations)** - [Widget Usage Guide](#widget-usage-guide) - [Continuous Updates](#continuous-updates) + - [Proxying Requests](#proxying-requests) - [Custom CSS Styling](#widget-styling) + - [Customizing Charts](#customizing-charts) - [Language Translations](#language-translations) - [Widget UI Options](#widget-ui-options) - [Building a Widget](#build-your-own-widget) @@ -1168,7 +1172,9 @@ The easiest method for displaying system info and resource usage in Dashy is wit Glances is a cross-platform monitoring tool developed by [@nicolargo](https://github.com/nicolargo). It's similar to top/htop but with a [Rest API](https://glances.readthedocs.io/en/latest/api.html) and many [data exporters](https://glances.readthedocs.io/en/latest/gw/index.html) available. Under the hood, it uses [psutil](https://github.com/giampaolo/psutil) for retrieving system info. -If you don't already have it installed, either follow the [Installation Guide](https://github.com/nicolargo/glances/blob/master/README.rst) for your system, or setup [with Docker](https://glances.readthedocs.io/en/latest/docker.html), or use the one-line install script: `curl -L https://bit.ly/glances | /bin/bash`. You'll need to run Glances as a web server, using the `-w` option, see the [command reference docs](https://glances.readthedocs.io/en/latest/cmds.html) for more info. +If you don't already have it installed, either follow the [Installation Guide](https://github.com/nicolargo/glances/blob/master/README.rst) for your system, or setup [with Docker](https://glances.readthedocs.io/en/latest/docker.html), or use the one-line install script: `curl -L https://bit.ly/glances | /bin/bash`. + +Glances can be launched with the `glances` command. You'll need to run it in web server mode, using the `-w` option for the API to be reachable. If you don't plan on using the Web UI, then you can disable it using `--disable-webui`. See the [command reference docs](https://glances.readthedocs.io/en/latest/cmds.html) for more info. ##### Options @@ -1316,6 +1322,38 @@ Shows real-time read and write speeds and operations per sec for each disk --- +### System Load + +Shows the number of processes waiting in the run-queue, averaged across all cores. Displays for past 5, 10 and 15 minutes + +

+ +##### Example + +```yaml +- type: gl-system-load + options: + hostname: http://192.168.130.2:61208 +``` + +--- + +### System Load History + +Shows recent historical system load, calculated from the number of processes waiting in the run-queue, in 1, 5 and 15 minute intervals, and averaged across all cores. Optionally specify `limit` to set number of results returned, defaults to `500`, max `100000`, but the higher the number the longer the load and render times will be. + +

+ +##### Example + +```yaml +- type: gl-load-history + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ## Dynamic Widgets ### Iframe Widget @@ -1465,19 +1503,6 @@ Note that if you have many widgets, and set them to continuously update frequent --- -### Widget Styling - -Like elsewhere in Dashy, all colours can be easily modified with CSS variables. - -Widgets use the following color variables, which can be overridden if desired: -- `--widget-text-color` - Text color, defaults to `--primary` -- `--widget-background-color` - Background color, defaults to `--background-darker` -- `--widget-accent-color` - Accent color, defaults to `--background` - -For more info on how to apply custom variables, see the [Theming Docs](/docs/theming.md#setting-custom-css-in-the-ui) - ---- - ### Proxying Requests If a widget fails to make a data request, and the console shows a CORS error, this means the server is blocking client-side requests. @@ -1503,6 +1528,33 @@ Vary: Origin --- +### Widget Styling + +Like elsewhere in Dashy, all colours can be easily modified with CSS variables. + +Widgets use the following color variables, which can be overridden if desired: +- `--widget-text-color` - Text color, defaults to `--primary` +- `--widget-background-color` - Background color, defaults to `--background-darker` +- `--widget-accent-color` - Accent color, defaults to `--background` + +For more info on how to apply custom variables, see the [Theming Docs](/docs/theming.md#setting-custom-css-in-the-ui) + +--- + +### Customizing Charts + +For widgets that contain charts, you can set an array of colors under `chartColors`. To specify the chart height, set `chartHeight` to an integer (in `px`), defaults to `300`. For example: + +```yaml +- type: gl-load-history + options: + hostname: http://192.168.130.2:61208 + chartColors: ['#9b5de5', '#f15bb5', '#00bbf9', '#00f5d4'] + chartHeight: 450 +``` + +--- + ### Language Translations Since most of the content displayed within widgets is fetched from an external API, unless that API supports multiple languages, translating dynamic content is not possible. @@ -1531,7 +1583,7 @@ Widgets are built in a modular fashion, making it easy for anyone to create thei For a full tutorial on creating your own widget, you can follow [this guide](/docs/development-guides.md#building-a-widget), or take a look at [here](https://github.com/Lissy93/dashy/commit/3da76ce2999f57f76a97454c0276301e39957b8e) for a code example. -Alternatively, for displaying simple data, you could also just use the either the [iframe](#iframe-widget), [embed](#html-embedded-widget), [Data Feed](#data-feed) or [API response](#api-response) widgets. +Alternatively, for displaying simple data, you could also just use the either the [iframe](#iframe-widget), [embed](#html-embedded-widget), [data feed](#data-feed) or [API response](#api-response) widgets. --- diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index dc5c5e99..58eeaa3b 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -273,7 +273,10 @@ "disk-space-free": "Free", "disk-space-used": "Used", "disk-mount-point": "Mount Point", - "disk-file-system": "File System" + "disk-file-system": "File System", + "disk-io-read": "Read", + "disk-io-write": "Write", + "system-load-desc": "Number of processes waiting in the run-queue, averaged across all cores" }, "system-info": { "uptime": "Uptime" diff --git a/src/components/Widgets/GlDiskIo.vue b/src/components/Widgets/GlDiskIo.vue index 75c1ab79..b7417241 100644 --- a/src/components/Widgets/GlDiskIo.vue +++ b/src/components/Widgets/GlDiskIo.vue @@ -4,13 +4,13 @@

{{ disk.name }}

- Read: + {{ $t('widgets.glances.disk-io-read') }}: {{ disk.readB | formatSize }} {{ disk.readD | getArrow }}
- Write: + {{ $t('widgets.glances.disk-io-write') }}: {{ disk.writeB | formatSize }} {{ disk.writeD | getArrow }}
diff --git a/src/components/Widgets/GlLoadHistory.vue b/src/components/Widgets/GlLoadHistory.vue new file mode 100644 index 00000000..4584bc98 --- /dev/null +++ b/src/components/Widgets/GlLoadHistory.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/components/Widgets/GlSystemLoad.vue b/src/components/Widgets/GlSystemLoad.vue new file mode 100644 index 00000000..1b937bc9 --- /dev/null +++ b/src/components/Widgets/GlSystemLoad.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 25611792..b500b16d 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -144,6 +144,13 @@ @error="handleError" :ref="widgetRef" /> + + import('@/components/Widgets/GlCpuHistory.vue'), GlDiskIo: () => import('@/components/Widgets/GlDiskIo.vue'), GlDiskSpace: () => import('@/components/Widgets/GlDiskSpace.vue'), + GlLoadHistory: () => import('@/components/Widgets/GlLoadHistory.vue'), GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), + GlSystemLoad: () => import('@/components/Widgets/GlSystemLoad.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), Jokes: () => import('@/components/Widgets/Jokes.vue'), From 6b8a5ee08678e1297ec357b96b84d8edffa13c9c Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 18 Jan 2022 17:16:00 +0000 Subject: [PATCH 22/40] :sparkles: Adds a system alert widget --- docs/widgets.md | 17 ++++ src/components/Widgets/GlAlerts.vue | 129 ++++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 ++ src/utils/MiscHelpers.js | 26 ++++-- 4 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 src/components/Widgets/GlAlerts.vue diff --git a/docs/widgets.md b/docs/widgets.md index 99f2af6f..dbb85e76 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -52,6 +52,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Disk IO](#disk-io) - [System Load](#system-load) - [System Load History](#system-load-history) + - [Resource Usage Alerts](#resource-usage-alerts) - **[Dynamic Widgets](#dynamic-widgets)** - [Iframe Widget](#iframe-widget) - [HTML Embed Widget](#html-embedded-widget) @@ -1354,6 +1355,22 @@ Shows recent historical system load, calculated from the number of processes wai --- +### Resource Usage Alerts + +Lists recent high resource usage alerts (e.g. CPU, mem, IO, load, temp) + +

+ +##### Example + +```yaml +- type: gl-alerts + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ## Dynamic Widgets ### Iframe Widget diff --git a/src/components/Widgets/GlAlerts.vue b/src/components/Widgets/GlAlerts.vue new file mode 100644 index 00000000..358814ed --- /dev/null +++ b/src/components/Widgets/GlAlerts.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index b500b16d..3623ea1b 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -109,6 +109,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/Flights.vue'), GitHubTrending: () => import('@/components/Widgets/GitHubTrending.vue'), GitHubProfile: () => import('@/components/Widgets/GitHubProfile.vue'), + GlAlerts: () => import('@/components/Widgets/GlAlerts.vue'), GlCpuCores: () => import('@/components/Widgets/GlCpuCores.vue'), GlCpuGauge: () => import('@/components/Widgets/GlCpuGauge.vue'), GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), diff --git a/src/utils/MiscHelpers.js b/src/utils/MiscHelpers.js index 5e998048..e84b132f 100644 --- a/src/utils/MiscHelpers.js +++ b/src/utils/MiscHelpers.js @@ -117,19 +117,25 @@ export const truncateStr = (str, len = 60, ellipse = '...') => { return str.length > len + ellipse.length ? `${str.slice(0, len)}${ellipse}` : str; }; +/* Given two timestamp, return the difference in text format, e.g. '10 minutes' */ +export const getTimeDifference = (startTime, endTime) => { + const msDifference = new Date(endTime).getTime() - new Date(startTime).getTime(); + const diff = Math.abs(Math.round(msDifference / 1000)); + const divide = (time, round) => Math.round(time / round); + if (diff < 60) return `${divide(diff, 1)} seconds`; + if (diff < 3600) return `${divide(diff, 60)} minutes`; + if (diff < 86400) return `${divide(diff, 3600)} hours`; + if (diff < 604800) return `${divide(diff, 86400)} days`; + if (diff >= 604800) return `${divide(diff, 604800)} weeks`; + return 'unknown'; +}; + /* Given a timestamp, return how long ago it was, e.g. '10 minutes' */ export const getTimeAgo = (dateTime) => { const now = new Date().getTime(); - const then = new Date(dateTime).getTime(); - if (then < 0) return 'Never'; - const diff = (now - then) / 1000; - const divide = (time, round) => Math.round(time / round); - if (diff < 60) return `${divide(diff, 1)} seconds ago`; - if (diff < 3600) return `${divide(diff, 60)} minutes ago`; - if (diff < 86400) return `${divide(diff, 3600)} hours ago`; - if (diff < 604800) return `${divide(diff, 86400)} days ago`; - if (diff >= 604800) return `${divide(diff, 604800)} weeks ago`; - return 'unknown'; + const diffStr = getTimeDifference(dateTime, now); + if (diffStr === 'unknown') return diffStr; + return `${diffStr} ago`; }; /* Given the name of a CSS variable, returns it's value */ From 6d9e34c90ff0112d50a9b121555eecaf99ccb4d6 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 18 Jan 2022 19:11:03 +0000 Subject: [PATCH 23/40] :sparkles: Adds a network interface widget --- docs/widgets.md | 19 ++- .../Widgets/GlNetworkInterfaces.vue | 157 ++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 + 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/components/Widgets/GlNetworkInterfaces.vue diff --git a/docs/widgets.md b/docs/widgets.md index dbb85e76..f6e6c597 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -52,6 +52,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Disk IO](#disk-io) - [System Load](#system-load) - [System Load History](#system-load-history) + - [Network Interfaces](#network-interfaces) - [Resource Usage Alerts](#resource-usage-alerts) - **[Dynamic Widgets](#dynamic-widgets)** - [Iframe Widget](#iframe-widget) @@ -1355,11 +1356,27 @@ Shows recent historical system load, calculated from the number of processes wai --- +### Network Interfaces + +Lists visible network interfaces, including real-time upload/ download stats + +

+ +##### Example + +```yaml +- type: gl-network-interfaces + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ### Resource Usage Alerts Lists recent high resource usage alerts (e.g. CPU, mem, IO, load, temp) -

+

##### Example diff --git a/src/components/Widgets/GlNetworkInterfaces.vue b/src/components/Widgets/GlNetworkInterfaces.vue new file mode 100644 index 00000000..f19884e4 --- /dev/null +++ b/src/components/Widgets/GlNetworkInterfaces.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 3623ea1b..6e09bfdb 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -172,6 +172,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/GlLoadHistory.vue'), GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), + GlNetworkInterfaces: () => import('@/components/Widgets/GlNetworkInterfaces.vue'), GlSystemLoad: () => import('@/components/Widgets/GlSystemLoad.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), From 88727cf2e200e1a9636360d1578dbc734b501322 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 18 Jan 2022 23:07:24 +0000 Subject: [PATCH 24/40] :sparkles: Adds a network traffic widget --- docs/widgets.md | 30 +++++- src/components/Widgets/GlNetworkTraffic.vue | 113 ++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 8 ++ 3 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 src/components/Widgets/GlNetworkTraffic.vue diff --git a/docs/widgets.md b/docs/widgets.md index f6e6c597..75dc44f7 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -53,6 +53,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [System Load](#system-load) - [System Load History](#system-load-history) - [Network Interfaces](#network-interfaces) + - [Network Traffic](#network-traffic) - [Resource Usage Alerts](#resource-usage-alerts) - **[Dynamic Widgets](#dynamic-widgets)** - [Iframe Widget](#iframe-widget) @@ -1185,7 +1186,7 @@ All Glance's based widgets require a `hostname` **Field** | **Type** | **Required** | **Description** --- | --- | --- | --- -**`hostname`** | `string` | Required | The URL to your Glances instance, without a trailing slash +**`hostname`** | `string` | Required | The URL to your Glances instance (without a trailing slash) ##### Info @@ -1372,6 +1373,23 @@ Lists visible network interfaces, including real-time upload/ download stats --- +### Network Traffic + +Shows amount of data recently uploaded/ downloaded across all network interfaces. Optionally set the `limit` option to specify number historical of data points to return + +

+ +##### Example + +```yaml +- type: gl-network-traffic + options: + hostname: http://192.168.130.2:61208 + limit: 500 +``` + +--- + ### Resource Usage Alerts Lists recent high resource usage alerts (e.g. CPU, mem, IO, load, temp) @@ -1577,7 +1595,9 @@ For more info on how to apply custom variables, see the [Theming Docs](/docs/the ### Customizing Charts -For widgets that contain charts, you can set an array of colors under `chartColors`. To specify the chart height, set `chartHeight` to an integer (in `px`), defaults to `300`. For example: +For widgets that contain charts, you can set an array of colors under `chartColors`. +To specify the chart height, set `chartHeight` to an integer (in `px`), defaults to `300`. +For example: ```yaml - type: gl-load-history @@ -1601,13 +1621,13 @@ For more info about multi-language support, see the [Internationalization Docs]( ### Widget UI Options -Widgets can be opened in full-page view, by clicking the Arrow icon (top-right). The URL in your address bar will also update, and visiting that web address will take you straight to the selected widget. +Widgets can be opened in full-page view, by clicking the Arrow icon (top-right). The URL in your address bar will also update, and visiting that web address directly will take you straight to that widget. You can reload the data of any widget, by clicking the Refresh Data icon (also in top-right). This will only affect the widget where the action was triggered from. -All [config options](/docs/configuring.md#section) that can be applied to sections, can also be applied to widget sections. For example, to make a widget span multiple columns, set `displayData.cols: 2` within the parent section. You can collapse a widget (by clicking the section title), and collapse state will be saved locally. +All [config options](/docs/configuring.md#section) that can be applied to sections, can also be applied to widget sections. For example, to make a widget section double the width, set `displayData.cols: 2` within the parent section. You can collapse a widget (by clicking the section title), and collapse state will be saved locally. -Widgets cannot currently be edited through the UI. This feature is in development, and will be released soon. In the meantime, you can either use the JSON config editor, or use VS Code or SSH into your box to edit the conf.yml file directly. +Widgets cannot currently be edited through the UI. This feature is in development, and will be released soon. In the meantime, you can either use the JSON config editor, or use [VS Code Server](https://github.com/coder/code-server), or just SSH into your box and edit the conf.yml file directly. --- diff --git a/src/components/Widgets/GlNetworkTraffic.vue b/src/components/Widgets/GlNetworkTraffic.vue new file mode 100644 index 00000000..b70340be --- /dev/null +++ b/src/components/Widgets/GlNetworkTraffic.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 6e09bfdb..40856f4e 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -179,6 +179,13 @@ @error="handleError" :ref="widgetRef" /> + import('@/components/Widgets/GlMemGauge.vue'), GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), GlNetworkInterfaces: () => import('@/components/Widgets/GlNetworkInterfaces.vue'), + GlNetworkTraffic: () => import('@/components/Widgets/GlNetworkTraffic.vue'), GlSystemLoad: () => import('@/components/Widgets/GlSystemLoad.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), From 2e593fcf922e1ddbd4a4bc9156118c9d34a3979e Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 18 Jan 2022 23:56:29 +0000 Subject: [PATCH 25/40] :zap: Respect user updateInterval when set to zero --- src/components/Widgets/WidgetBase.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 40856f4e..d768e831 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -440,7 +440,8 @@ export default { widgetOptions() { const options = this.widget.options || {}; const useProxy = !!this.widget.useProxy; - const updateInterval = this.widget.updateInterval || null; + const updateInterval = this.widget.updateInterval !== undefined + ? this.widget.updateInterval : null; return { useProxy, updateInterval, ...options }; }, /* A unique string to reference the widget by */ From b3dcc5e43f7f3077390ad23e3d6b44af0a910cdf Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 18 Jan 2022 23:59:41 +0000 Subject: [PATCH 26/40] :bento: Adds screenshot to widget docs --- docs/widgets.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/widgets.md b/docs/widgets.md index 75dc44f7..eb2f4364 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -1196,6 +1196,9 @@ All Glance's based widgets require a `hostname` - **Host**: Self-Hosted (see [GitHub - Nicolargo/Glances](https://github.com/nicolargo/glances)) - **Privacy**: ⚫ No Policy Available +##### Screenshot +[![example-screenshot](https://i.ibb.co/xfK6BGb/system-monitor-board.png)](https://ibb.co/pR6dMZT) + --- ### Current CPU Usage From ea3ffa5d368809ef741b2bb722f5bd391f951e68 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Wed, 19 Jan 2022 00:06:09 +0000 Subject: [PATCH 27/40] :rotating_light: Fixes warnings from DeepScan --- src/components/Charts/PercentageChart.vue | 2 +- src/components/Widgets/WalletBalance.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Charts/PercentageChart.vue b/src/components/Charts/PercentageChart.vue index 05c6aaba..be5c0324 100644 --- a/src/components/Charts/PercentageChart.vue +++ b/src/components/Charts/PercentageChart.vue @@ -8,7 +8,7 @@
diff --git a/src/components/Widgets/WalletBalance.vue b/src/components/Widgets/WalletBalance.vue index 09ba466c..923ae8f9 100644 --- a/src/components/Widgets/WalletBalance.vue +++ b/src/components/Widgets/WalletBalance.vue @@ -96,7 +96,7 @@ export default { final: formatAmount(data.final_balance), totalSent: formatAmount(data.total_sent), totalReceived: formatAmount(data.total_received), - lastTransaction: data.txrefs ? getTimeAgo(data.txrefs[0].confirmed) : 'Never', + lastTransaction: data.txrefs[0] ? getTimeAgo(data.txrefs[0].confirmed) : 'Never', }; const transactions = []; data.txrefs.forEach((transaction) => { From dfea4e317c72fde8c1978f6552326b2d177666fb Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Wed, 19 Jan 2022 11:43:04 +0000 Subject: [PATCH 28/40] :zap: Refactors all Glances widgets to inherit from parent mixin --- docs/widgets.md | 4 ++- src/components/Widgets/GlAlerts.vue | 12 ++----- src/components/Widgets/GlCpuCores.vue | 12 ++----- src/components/Widgets/GlCpuGauge.vue | 12 ++----- src/components/Widgets/GlCpuHistory.vue | 12 ++----- src/components/Widgets/GlDiskIo.vue | 12 ++----- src/components/Widgets/GlDiskSpace.vue | 9 ++--- src/components/Widgets/GlLoadHistory.vue | 12 ++----- src/components/Widgets/GlMemGauge.vue | 12 ++----- src/components/Widgets/GlMemHistory.vue | 13 ++----- .../Widgets/GlNetworkInterfaces.vue | 12 ++----- src/components/Widgets/GlNetworkTraffic.vue | 12 ++----- src/components/Widgets/GlSystemLoad.vue | 12 ++----- src/mixins/GlancesMixin.js | 36 +++++++++++++++++++ 14 files changed, 75 insertions(+), 107 deletions(-) create mode 100644 src/mixins/GlancesMixin.js diff --git a/docs/widgets.md b/docs/widgets.md index eb2f4364..0e74552a 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -1187,11 +1187,13 @@ All Glance's based widgets require a `hostname` **Field** | **Type** | **Required** | **Description** --- | --- | --- | --- **`hostname`** | `string` | Required | The URL to your Glances instance (without a trailing slash) +**`username`** | `string` | _Optional_ | If you have setup basic auth on Glances, specify username here (defaults to `glances`) +**`password`** | `string` | _Optional_ | If you have setup basic auth on Glances, specify password here. **Note**: since this password is in plaintext, it is important not to reuse it anywhere else ##### Info - **CORS**: 🟒 Enabled -- **Auth**: 🟒 Not Required +- **Auth**: 🟠 Optional - **Price**: 🟒 Free - **Host**: Self-Hosted (see [GitHub - Nicolargo/Glances](https://github.com/nicolargo/glances)) - **Privacy**: ⚫ No Policy Available diff --git a/src/components/Widgets/GlAlerts.vue b/src/components/Widgets/GlAlerts.vue index 358814ed..ab16b14a 100644 --- a/src/components/Widgets/GlAlerts.vue +++ b/src/components/Widgets/GlAlerts.vue @@ -20,10 +20,11 @@ + + diff --git a/src/views/Workspace.vue b/src/views/Workspace.vue index 80d22f33..452b3e2d 100644 --- a/src/views/Workspace.vue +++ b/src/views/Workspace.vue @@ -1,8 +1,14 @@ @@ -10,6 +16,7 @@ import HomeMixin from '@/mixins/HomeMixin'; import SideBar from '@/components/Workspace/SideBar'; import WebContent from '@/components/Workspace/WebContent'; +import WidgetView from '@/components/Workspace/WidgetView'; import MultiTaskingWebComtent from '@/components/Workspace/MultiTaskingWebComtent'; import Defaults from '@/utils/defaults'; import { GetTheme, ApplyLocalTheme, ApplyCustomVariables } from '@/utils/ThemeHelper'; @@ -19,6 +26,7 @@ export default { mixins: [HomeMixin], data: () => ({ url: '', + widgets: null, GetTheme, ApplyLocalTheme, ApplyCustomVariables, @@ -37,6 +45,7 @@ export default { components: { SideBar, WebContent, + WidgetView, MultiTaskingWebComtent, }, methods: { @@ -46,6 +55,11 @@ export default { } else { this.url = options.url; } + this.widgets = null; + }, + launchWidget(widgets) { + this.url = ''; + this.widgets = widgets; }, setTheme() { const theme = this.GetTheme(); From c8f33d85bf24ca03de33860a26d4a2c93ad75368 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Thu, 20 Jan 2022 16:36:54 +0000 Subject: [PATCH 32/40] :construction: Adds AnonAddy widget endpoint to defaults --- src/utils/defaults.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 59db5ce8..23a97ee6 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -208,6 +208,7 @@ module.exports = { }, /* API endpoints for widgets that need to fetch external data */ widgetApiEndpoints: { + anonAddy: 'https://app.anonaddy.com', astronomyPictureOfTheDay: 'https://apodapi.herokuapp.com/api', codeStats: 'https://codestats.net/', covidStats: 'https://disease.sh/v3/covid-19', From b96af21bc996137bbb887fcec4f91658828bec61 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 12:56:18 +0000 Subject: [PATCH 33/40] :package: Builds a toggle switch form element --- src/components/FormElements/Toggle.vue | 142 +++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/components/FormElements/Toggle.vue diff --git a/src/components/FormElements/Toggle.vue b/src/components/FormElements/Toggle.vue new file mode 100644 index 00000000..5471956c --- /dev/null +++ b/src/components/FormElements/Toggle.vue @@ -0,0 +1,142 @@ + + + + + From 58a085a550a4175769b6906c0b854564aba9a00a Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 12:58:15 +0000 Subject: [PATCH 34/40] :sparkles: Adds an email widget for AnonAddy --- docs/widgets.md | 50 +++- src/components/Widgets/AnonAddy.vue | 400 ++++++++++++++++++++++++++ src/components/Widgets/WidgetBase.vue | 10 +- 3 files changed, 457 insertions(+), 3 deletions(-) create mode 100644 src/components/Widgets/AnonAddy.vue diff --git a/docs/widgets.md b/docs/widgets.md index 151eab64..1861c6d1 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -17,6 +17,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Crypto Price History](#crypto-token-price-history) - [Crypto Wallet Balance](#wallet-balance) - [Code Stats](#code-stats) + - [Email Aliases (AnonAddy)](#anonaddy) - [Vulnerability Feed](#vulnerability-feed) - [Exchange Rates](#exchange-rates) - [Public Holidays](#public-holidays) @@ -379,6 +380,50 @@ Display your coding summary. [Code::Stats](https://codestats.net/) is a free and --- +### AnonAddy + +[AnonAddy](https://anonaddy.com/) is a free and open source mail forwarding service. Use it to protect your real email address, by using a different alias for each of your online accounts, and have all emails land in your normal inbox(es). Supports custom domains, email replies, PGP-encryption, multiple recipients and more + +This widget display email addresses / aliases from AnonAddy. Click an email address to copy to clipboard, or use the toggle switch to enable/ disable it. Shows usage stats (bandwidth, used aliases etc), as well as total messages recieved, blocked and sent. Works with both self-hosted and managed instances of AnonAddy. + +

+ +##### Options + +**Field** | **Type** | **Required** | **Description** +--- | --- | --- | --- +**`apiKey`** | `string` | Required | Your AnonAddy API Key / Personal Access Token. You can generate this under [Account Settings](https://app.anonaddy.com/settings) +**`hostname`** | `string` | _Optional_ | If your self-hosting AnonAddy, then supply the host name. By default it will use the public hosted instance +**`apiVersion`** | `string` | _Optional_ | If you're using an API version that is not version `v1`, then specify it here +**`limit`** | `number` | _Optional_ | Limit the number of emails shown per page. Defaults to `10` +**`sortBy`** | `string` | _Optional_ | Specify the sort order for email addresses. Defaults to `updated_at`. Can be either: `local_part`, `domain`, `email`, `emails_forwarded`, `emails_blocked`, `emails_replied`, `emails_sent`, `created_at`, `updated_at` or `deleted_at`. Precede with a `-` character to reverse order. +**`searchTerm`** | `string` | _Optional_ | A search term to filter results by, will search the email, description and domain +**`disableControls`** | `boolean` | _Optional_ | Prevent any changes being made to account through the widget. User will not be able to enable or disable aliases through UI when this option is set +**`hideMeta`** | `boolean` | _Optional_ | Don't show account meta info (forward/ block count, quota usage etc) +**`hideAliases`** | `boolean` | _Optional_ | Don't show email address / alias list. Will only show account meta info + +##### Example + +```yaml + - type: anonaddy + options: + apiKey: "xxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + limit: 5 + sortBy: created_at + disableControls: true +``` + +##### Info +- **CORS**: 🟒 Enabled +- **Auth**: πŸ”΄ Required +- **Price**: 🟠 Free for Self-Hosted / Free Plan available on managed instance or $1/month for premium +- **Host**: Self-Hosted or Managed +- **Privacy**: _See [AnonAddy Privacy Policy](https://anonaddy.com/privacy/)_ + +--- + ### Vulnerability Feed Keep track of recent security advisories and vulnerabilities, with optional filtering by score, exploits, vendor and product. All fields are optional. @@ -1181,14 +1226,15 @@ Glances can be launched with the `glances` command. You'll need to run it in web ##### Options -All Glance's based widgets require a `hostname` +All Glance's based widgets require a `hostname`. All other parameters are optional. **Field** | **Type** | **Required** | **Description** --- | --- | --- | --- -**`hostname`** | `string` | Required | The URL to your Glances instance (without a trailing slash) +**`hostname`** | `string` | Required | The URL or IP + port to your Glances instance (without a trailing slash) **`username`** | `string` | _Optional_ | If you have setup basic auth on Glances, specify username here (defaults to `glances`) **`password`** | `string` | _Optional_ | If you have setup basic auth on Glances, specify password here. **Note**: since this password is in plaintext, it is important not to reuse it anywhere else **`apiVersion`** | `string` | _Optional_ | Specify an API version, defaults to V `3`. Note that support for older versions is limited +**`limit`** | `number` | _Optional_ | For widgets that show a time-series chart, optionally limit the number of data points returned. A higher number will show more historical results, but will take longer to load. A value between 300 - 800 is usually optimal ##### Info - **CORS**: 🟒 Enabled diff --git a/src/components/Widgets/AnonAddy.vue b/src/components/Widgets/AnonAddy.vue new file mode 100644 index 00000000..0e466c05 --- /dev/null +++ b/src/components/Widgets/AnonAddy.vue @@ -0,0 +1,400 @@ + + + + + diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index d768e831..ff145fdc 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -18,8 +18,15 @@
+ import('@/components/Widgets/AnonAddy.vue'), Apod: () => import('@/components/Widgets/Apod.vue'), Clock: () => import('@/components/Widgets/Clock.vue'), CodeStats: () => import('@/components/Widgets/CodeStats.vue'), From 6cd9eac49b951212b437f2323616b79314ed52a2 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 13:00:26 +0000 Subject: [PATCH 35/40] :lock: Updates CORS proxy to use res.status --- services/cors-proxy.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/cors-proxy.js b/services/cors-proxy.js index 97ac63b3..9c51214c 100644 --- a/services/cors-proxy.js +++ b/services/cors-proxy.js @@ -23,7 +23,7 @@ module.exports = (req, res) => { // Get desired URL, from Target-URL header const targetURL = req.header('Target-URL'); if (!targetURL) { - res.send(500, { error: 'There is no Target-Endpoint header in the request' }); + res.status(500).send({ error: 'There is no Target-Endpoint header in the request' }); return; } // Apply any custom headers, if needed @@ -40,8 +40,8 @@ module.exports = (req, res) => { // Make the request, and respond with result axios.request(requestConfig) .then((response) => { - res.send(200, response.data); + res.status(200).send(response.data); }).catch((error) => { - res.send(500, { error }); + res.status(500).send({ error }); }); }; From 9cd8c21d8ed8356ed0488e67099aa596579919e3 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 13:00:58 +0000 Subject: [PATCH 36/40] :zap: Show real value on hover, percentage chart --- src/components/Charts/PercentageChart.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Charts/PercentageChart.vue b/src/components/Charts/PercentageChart.vue index be5c0324..d4b4b539 100644 --- a/src/components/Charts/PercentageChart.vue +++ b/src/components/Charts/PercentageChart.vue @@ -15,7 +15,7 @@
+ class="legend-item" v-tooltip="`${Math.round(block.width)}% (${block.value})`">
{{ block.label }}
@@ -59,6 +59,7 @@ export default { width: Math.round(value.size * multiplier), color: value.color || defaultColor, label: value.label, + value: value.size, }); startPositionSum += (value.size * multiplier); }); From 2c9ae46207e6f38b2a69e18ddf7943cb9f974fed Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 13:01:33 +0000 Subject: [PATCH 37/40] :zap: Adds support for custom headers, body and method in widgt request object --- src/mixins/WidgetMixin.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/mixins/WidgetMixin.js b/src/mixins/WidgetMixin.js index e5ebeded..53dfc5ef 100644 --- a/src/mixins/WidgetMixin.js +++ b/src/mixins/WidgetMixin.js @@ -98,16 +98,20 @@ const WidgetMixin = { }; }, /* Makes data request, returns promise */ - makeRequest(endpoint, options) { + makeRequest(endpoint, options, protocol, body) { // Request Options - const method = 'GET'; + const method = protocol || 'GET'; const url = this.useProxy ? this.proxyReqEndpoint : endpoint; - const CustomHeaders = options ? JSON.stringify(options) : null; + const data = JSON.stringify(body || {}); + const CustomHeaders = options || null; const headers = this.useProxy - ? { 'Target-URL': endpoint, CustomHeaders } : CustomHeaders; + ? { 'Target-URL': endpoint, CustomHeaders: JSON.stringify(CustomHeaders) } : CustomHeaders; + const requestConfig = { + method, url, headers, data, + }; // Make request return new Promise((resolve, reject) => { - axios.request({ method, url, headers }) + axios.request(requestConfig) .then((response) => { if (response.data.success === false) { this.error('Proxy returned error from target server', response.data.message); From cf98a96a15bb543f12d627faed797ebe58cef81e Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 13:02:02 +0000 Subject: [PATCH 38/40] :zap: Small refactor to glances mixin --- src/mixins/GlancesMixin.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mixins/GlancesMixin.js b/src/mixins/GlancesMixin.js index 40e7cc1c..413845e5 100644 --- a/src/mixins/GlancesMixin.js +++ b/src/mixins/GlancesMixin.js @@ -1,5 +1,5 @@ /** Reusable mixin for all Glances widgets */ -const WidgetMixin = { +export default { computed: { /* Required, hostname (e.g. IP + port) for Glances instance */ hostname() { @@ -32,5 +32,3 @@ const WidgetMixin = { }, }, }; - -export default WidgetMixin; From bf39553e1de62fa1df57210c0a6654e546fe685e Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Fri, 21 Jan 2022 13:14:30 +0000 Subject: [PATCH 39/40] :zap: Improved widget error handling, don't fail on error --- src/components/Widgets/WidgetBase.vue | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index ff145fdc..4691dd74 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -1,7 +1,7 @@