diff --git a/src/client/components/instance-stats.vue b/src/client/components/instance-stats.vue index 4fdacb94ca..1f3d45d4d9 100644 --- a/src/client/components/instance-stats.vue +++ b/src/client/components/instance-stats.vue @@ -84,7 +84,7 @@ </div> <section class="_card"> - <div class="_title"><fa :icon="faChartBar"/> {{ $t('statistics') }}</div> + <div class="_title" style="position: relative;"><fa :icon="faChartBar"/> {{ $t('statistics') }}<button @click="fetchChart" class="_button" style="position: absolute; right: 0; bottom: 0; top: 0; padding: inherit;"><fa :icon="faSync"/></button></div> <div class="_content" style="margin-top: -8px;"> <div class="selects" style="display: flex;"> <mk-select v-model="chartSrc" style="margin: 0; flex: 1;"> @@ -123,7 +123,7 @@ <script lang="ts"> import Vue from 'vue'; -import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-icons'; +import { faChartBar, faUser, faPencilAlt, faSync } from '@fortawesome/free-solid-svg-icons'; import Chart from 'chart.js'; import MkSelect from './ui/select.vue'; @@ -171,7 +171,7 @@ export default Vue.extend({ chartInstance: null, chartSrc: 'notes', chartSpan: 'hour', - faChartBar, faUser, faPencilAlt + faChartBar, faUser, faPencilAlt, faSync } }, @@ -220,52 +220,56 @@ export default Vue.extend({ this.now = new Date(); - const [perHour, perDay] = await Promise.all([Promise.all([ - this.$root.api('charts/federation', { limit: this.chartLimit, span: 'hour' }), - this.$root.api('charts/users', { limit: this.chartLimit, span: 'hour' }), - this.$root.api('charts/active-users', { limit: this.chartLimit, span: 'hour' }), - this.$root.api('charts/notes', { limit: this.chartLimit, span: 'hour' }), - this.$root.api('charts/drive', { limit: this.chartLimit, span: 'hour' }), - ]), Promise.all([ - this.$root.api('charts/federation', { limit: this.chartLimit, span: 'day' }), - this.$root.api('charts/users', { limit: this.chartLimit, span: 'day' }), - this.$root.api('charts/active-users', { limit: this.chartLimit, span: 'day' }), - this.$root.api('charts/notes', { limit: this.chartLimit, span: 'day' }), - this.$root.api('charts/drive', { limit: this.chartLimit, span: 'day' }), - ])]); - - const chart = { - perHour: { - federation: perHour[0], - users: perHour[1], - activeUsers: perHour[2], - notes: perHour[3], - drive: perHour[4], - }, - perDay: { - federation: perDay[0], - users: perDay[1], - activeUsers: perDay[2], - notes: perDay[3], - drive: perDay[4], - } - }; - - this.notesLocalWoW = this.info.originalNotesCount - chart.perDay.notes.local.total[7]; - this.notesLocalDoD = this.info.originalNotesCount - chart.perDay.notes.local.total[1]; - this.notesRemoteWoW = (this.info.notesCount - this.info.originalNotesCount) - chart.perDay.notes.remote.total[7]; - this.notesRemoteDoD = (this.info.notesCount - this.info.originalNotesCount) - chart.perDay.notes.remote.total[1]; - this.usersLocalWoW = this.info.originalUsersCount - chart.perDay.users.local.total[7]; - this.usersLocalDoD = this.info.originalUsersCount - chart.perDay.users.local.total[1]; - this.usersRemoteWoW = (this.info.usersCount - this.info.originalUsersCount) - chart.perDay.users.remote.total[7]; - this.usersRemoteDoD = (this.info.usersCount - this.info.originalUsersCount) - chart.perDay.users.remote.total[1]; - - this.chart = chart; - - this.renderChart(); + this.fetchChart(); }, methods: { + async fetchChart() { + const [perHour, perDay] = await Promise.all([Promise.all([ + this.$root.api('charts/federation', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/users', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/active-users', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/notes', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/drive', { limit: this.chartLimit, span: 'hour' }), + ]), Promise.all([ + this.$root.api('charts/federation', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/users', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/active-users', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/notes', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/drive', { limit: this.chartLimit, span: 'day' }), + ])]); + + const chart = { + perHour: { + federation: perHour[0], + users: perHour[1], + activeUsers: perHour[2], + notes: perHour[3], + drive: perHour[4], + }, + perDay: { + federation: perDay[0], + users: perDay[1], + activeUsers: perDay[2], + notes: perDay[3], + drive: perDay[4], + } + }; + + this.notesLocalWoW = this.info.originalNotesCount - chart.perDay.notes.local.total[7]; + this.notesLocalDoD = this.info.originalNotesCount - chart.perDay.notes.local.total[1]; + this.notesRemoteWoW = (this.info.notesCount - this.info.originalNotesCount) - chart.perDay.notes.remote.total[7]; + this.notesRemoteDoD = (this.info.notesCount - this.info.originalNotesCount) - chart.perDay.notes.remote.total[1]; + this.usersLocalWoW = this.info.originalUsersCount - chart.perDay.users.local.total[7]; + this.usersLocalDoD = this.info.originalUsersCount - chart.perDay.users.local.total[1]; + this.usersRemoteWoW = (this.info.usersCount - this.info.originalUsersCount) - chart.perDay.users.remote.total[7]; + this.usersRemoteDoD = (this.info.usersCount - this.info.originalUsersCount) - chart.perDay.users.remote.total[1]; + + this.chart = chart; + + this.renderChart(); + }, + renderChart() { if (this.chartInstance) { this.chartInstance.destroy(); diff --git a/src/client/components/ui/container.vue b/src/client/components/ui/container.vue index 247bfdbb9e..382dd76eff 100644 --- a/src/client/components/ui/container.vue +++ b/src/client/components/ui/container.vue @@ -2,11 +2,13 @@ <div class="ukygtjoj _panel" :class="{ naked, hideHeader: !showHeader, scrollable, closed: !showBody }" v-size="{ max: [380], el: resizeBaseEl }"> <header v-if="showHeader" ref="header"> <div class="title"><slot name="header"></slot></div> - <slot name="func"></slot> - <button class="_button" v-if="bodyTogglable" @click="() => showBody = !showBody"> - <template v-if="showBody"><fa :icon="faAngleUp"/></template> - <template v-else><fa :icon="faAngleDown"/></template> - </button> + <div class="sub"> + <slot name="func"></slot> + <button class="_button" v-if="bodyTogglable" @click="() => showBody = !showBody"> + <template v-if="showBody"><fa :icon="faAngleUp"/></template> + <template v-else><fa :icon="faAngleDown"/></template> + </button> + </div> </header> <transition name="container-toggle" @enter="enter" @@ -153,14 +155,17 @@ export default Vue.extend({ } } - > button { + > .sub { position: absolute; z-index: 2; top: 0; right: 0; - padding: 0; - width: 42px; height: 100%; + + > button { + width: 42px; + height: 100%; + } } } diff --git a/src/client/pages/instance/index.queue-chart.vue b/src/client/pages/instance/index.queue-chart.vue index 78dfc9a667..3b7823d924 100644 --- a/src/client/pages/instance/index.queue-chart.vue +++ b/src/client/pages/instance/index.queue-chart.vue @@ -1,6 +1,8 @@ <template> <mk-container :body-togglable="false"> <template #header><slot name="title"></slot></template> + <template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> + <div class="_content _table"> <div class="_row"> <div class="_cell"><div class="_label">Process</div>{{ activeSincePrevTick | number }}</div> @@ -18,6 +20,7 @@ <script lang="ts"> import Vue from 'vue'; import Chart from 'chart.js'; +import { faPlay, faPause } from '@fortawesome/free-solid-svg-icons'; import MkContainer from '../../components/ui/container.vue'; const alpha = (hex, a) => { @@ -49,6 +52,8 @@ export default Vue.extend({ active: 0, waiting: 0, delayed: 0, + paused: false, + faPlay, faPause } }, @@ -155,6 +160,7 @@ export default Vue.extend({ methods: { onStats(stats) { + if (this.paused) return; this.activeSincePrevTick = stats[this.domain].activeSincePrevTick; this.active = stats[this.domain].active; this.waiting = stats[this.domain].waiting; @@ -179,6 +185,14 @@ export default Vue.extend({ this.onStats(stats); } }, + + pause() { + this.paused = true; + }, + + resume() { + this.paused = false; + }, } }); </script> diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index c32fe81df0..f55a53b5f3 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -53,6 +53,7 @@ <div class="segusily"> <mk-container :body-togglable="false" :resize-base-el="() => $el"> <template #header><fa :icon="faMicrochip"/>{{ $t('cpuAndMemory') }}</template> + <template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> <canvas ref="cpumem"></canvas> @@ -72,8 +73,10 @@ </div> </div> </mk-container> + <mk-container :body-togglable="false" :resize-base-el="() => $el"> <template #header><fa :icon="faHdd"/> {{ $t('disk') }}</template> + <template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> <canvas ref="disk"></canvas> @@ -88,8 +91,10 @@ </div> </div> </mk-container> + <mk-container :body-togglable="false" :resize-base-el="() => $el"> <template #header><fa :icon="faExchangeAlt"/> {{ $t('network') }}</template> + <template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> <canvas ref="net"></canvas> @@ -180,7 +185,7 @@ <script lang="ts"> import Vue from 'vue'; -import { faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList } from '@fortawesome/free-solid-svg-icons'; +import { faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList } from '@fortawesome/free-solid-svg-icons'; import Chart from 'chart.js'; import VueJsonPretty from 'vue-json-pretty'; import MkInstanceStats from '../../components/instance-stats.vue'; @@ -240,7 +245,8 @@ export default Vue.extend({ dbInfo: null, overviewHeight: '1fr', queueHeight: '1fr', - faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList, + paused: false, + faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList, } }, @@ -580,6 +586,8 @@ export default Vue.extend({ }, onStats(stats) { + if (this.paused) return; + const cpu = (stats.cpu * 100).toFixed(0); const memActive = (stats.mem.active / this.serverInfo.mem.total * 100).toFixed(0); const memUsed = (stats.mem.used / this.serverInfo.mem.total * 100).toFixed(0); @@ -616,7 +624,15 @@ export default Vue.extend({ for (const stats of [...statsLog].reverse()) { this.onStats(stats); } - } + }, + + pause() { + this.paused = true; + }, + + resume() { + this.paused = false; + }, } }); </script>