diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-10-22 05:36:48 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-22 05:36:48 +0900 |
| commit | 4e4c559db6964cbf17fcadf38d55fc79c995ca42 (patch) | |
| tree | 0e136ef1bf75c201b5805e667129082db3abcb61 /src/client/pages/instance | |
| parent | リモートノートで意図せずローカルカスタム絵文字が使... (diff) | |
| download | sharkey-4e4c559db6964cbf17fcadf38d55fc79c995ca42.tar.gz sharkey-4e4c559db6964cbf17fcadf38d55fc79c995ca42.tar.bz2 sharkey-4e4c559db6964cbf17fcadf38d55fc79c995ca42.zip | |
Migrate to Chart.js v3 (#7896)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* 定期的にresync
* Update overview.vue
* wip
* wip
Diffstat (limited to 'src/client/pages/instance')
| -rw-r--r-- | src/client/pages/instance/instance.vue | 266 | ||||
| -rw-r--r-- | src/client/pages/instance/metrics.vue | 83 | ||||
| -rw-r--r-- | src/client/pages/instance/overview.vue | 174 | ||||
| -rw-r--r-- | src/client/pages/instance/queue.chart.vue | 2 |
4 files changed, 181 insertions, 344 deletions
diff --git a/src/client/pages/instance/instance.vue b/src/client/pages/instance/instance.vue index 6117f090de..5572fbbf75 100644 --- a/src/client/pages/instance/instance.vue +++ b/src/client/pages/instance/instance.vue @@ -78,17 +78,17 @@ <span class="label">{{ $ts.charts }}</span> <div class="selects"> <MkSelect v-model="chartSrc" style="margin: 0; flex: 1;"> - <option value="requests">{{ $ts._instanceCharts.requests }}</option> - <option value="users">{{ $ts._instanceCharts.users }}</option> - <option value="users-total">{{ $ts._instanceCharts.usersTotal }}</option> - <option value="notes">{{ $ts._instanceCharts.notes }}</option> - <option value="notes-total">{{ $ts._instanceCharts.notesTotal }}</option> - <option value="ff">{{ $ts._instanceCharts.ff }}</option> - <option value="ff-total">{{ $ts._instanceCharts.ffTotal }}</option> - <option value="drive-usage">{{ $ts._instanceCharts.cacheSize }}</option> - <option value="drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option> - <option value="drive-files">{{ $ts._instanceCharts.files }}</option> - <option value="drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option> + <option value="instance-requests">{{ $ts._instanceCharts.requests }}</option> + <option value="instance-users">{{ $ts._instanceCharts.users }}</option> + <option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option> + <option value="instance-notes">{{ $ts._instanceCharts.notes }}</option> + <option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option> + <option value="instance-ff">{{ $ts._instanceCharts.ff }}</option> + <option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option> + <option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option> + <option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option> + <option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option> + <option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option> </MkSelect> <MkSelect v-model="chartSpan" style="margin: 0;"> <option value="hour">{{ $ts.perHour }}</option> @@ -97,7 +97,7 @@ </div> </div> <div class="chart"> - <canvas :ref="setChart"></canvas> + <MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart> </div> </div> <div class="operations section"> @@ -124,28 +124,17 @@ <script lang="ts"> import { defineComponent, markRaw } from 'vue'; -import Chart from 'chart.js'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkUsersDialog from '@client/components/users-dialog.vue'; import MkSelect from '@client/components/form/select.vue'; import MkButton from '@client/components/ui/button.vue'; import MkSwitch from '@client/components/form/switch.vue'; import MkInfo from '@client/components/ui/info.vue'; +import MkChart from '@client/components/chart.vue'; import bytes from '@client/filters/bytes'; import number from '@client/filters/number'; import * as os from '@client/os'; -const chartLimit = 90; -const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b)); -const negate = arr => arr.map(x => -x); -const alpha = hex => { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!; - const r = parseInt(result[1], 16); - const g = parseInt(result[2], 16); - const b = parseInt(result[3], 16); - return `rgba(${r}, ${g}, ${b}, 0.1)`; -}; - export default defineComponent({ components: { XModalWindow, @@ -153,6 +142,7 @@ export default defineComponent({ MkButton, MkSwitch, MkInfo, + MkChart, }, props: { @@ -167,42 +157,12 @@ export default defineComponent({ data() { return { isSuspended: this.instance.isSuspended, - now: null, - canvas: null, - chart: null, - chartInstance: null, chartSrc: 'requests', chartSpan: 'hour', }; }, computed: { - data(): any { - if (this.chart == null) return null; - switch (this.chartSrc) { - case 'requests': return this.requestsChart(); - case 'users': return this.usersChart(false); - case 'users-total': return this.usersChart(true); - case 'notes': return this.notesChart(false); - case 'notes-total': return this.notesChart(true); - case 'ff': return this.ffChart(false); - case 'ff-total': return this.ffChart(true); - case 'drive-usage': return this.driveUsageChart(false); - case 'drive-usage-total': return this.driveUsageChart(true); - case 'drive-files': return this.driveFilesChart(false); - case 'drive-files-total': return this.driveFilesChart(true); - } - }, - - stats(): any[] { - const stats = - this.chartSpan == 'day' ? this.chart.perDay : - this.chartSpan == 'hour' ? this.chart.perHour : - null; - - return stats; - }, - meta() { return this.$instance; }, @@ -219,49 +179,15 @@ export default defineComponent({ isSuspended: this.isSuspended }); }, - - chartSrc() { - this.renderChart(); - }, - - chartSpan() { - this.renderChart(); - } - }, - - async created() { - this.now = new Date(); - - const [perHour, perDay] = await Promise.all([ - os.api('charts/instance', { host: this.instance.host, limit: chartLimit, span: 'hour' }), - os.api('charts/instance', { host: this.instance.host, limit: chartLimit, span: 'day' }), - ]); - - const chart = { - perHour: perHour, - perDay: perDay - }; - - this.chart = chart; - - this.renderChart(); }, methods: { - setChart(el) { - this.canvas = el; - }, - changeBlock(e) { os.api('admin/update-meta', { blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host) }); }, - setSrc(src) { - this.chartSrc = src; - }, - removeAllFollowing() { os.apiWithDialog('admin/federation/remove-all-following', { host: this.instance.host @@ -274,170 +200,6 @@ export default defineComponent({ }); }, - renderChart() { - if (this.chartInstance) { - this.chartInstance.destroy(); - } - - Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); - this.chartInstance = markRaw(new Chart(this.canvas, { - type: 'line', - data: { - labels: new Array(chartLimit).fill(0).map((_, i) => this.getDate(i).toLocaleString()).slice().reverse(), - datasets: this.data.series.map(x => ({ - label: x.name, - data: x.data.slice().reverse(), - pointRadius: 0, - lineTension: 0, - borderWidth: 2, - borderColor: x.color, - backgroundColor: alpha(x.color), - })) - }, - options: { - aspectRatio: 2.5, - layout: { - padding: { - left: 16, - right: 16, - top: 16, - bottom: 0 - } - }, - legend: { - position: 'bottom', - labels: { - boxWidth: 16, - } - }, - scales: { - xAxes: [{ - gridLines: { - display: false - }, - ticks: { - display: false - } - }], - yAxes: [{ - position: 'right', - ticks: { - display: false - } - }] - }, - tooltips: { - intersect: false, - mode: 'index', - } - } - })); - }, - - getDate(ago: number) { - const y = this.now.getFullYear(); - const m = this.now.getMonth(); - const d = this.now.getDate(); - const h = this.now.getHours(); - - return this.chartSpan == 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago); - }, - - format(arr) { - return arr; - }, - - requestsChart(): any { - return { - series: [{ - name: 'In', - color: '#008FFB', - data: this.format(this.stats.requests.received) - }, { - name: 'Out (succ)', - color: '#00E396', - data: this.format(this.stats.requests.succeeded) - }, { - name: 'Out (fail)', - color: '#FEB019', - data: this.format(this.stats.requests.failed) - }] - }; - }, - - usersChart(total: boolean): any { - return { - series: [{ - name: 'Users', - color: '#008FFB', - data: this.format(total - ? this.stats.users.total - : sum(this.stats.users.inc, negate(this.stats.users.dec)) - ) - }] - }; - }, - - notesChart(total: boolean): any { - return { - series: [{ - name: 'Notes', - color: '#008FFB', - data: this.format(total - ? this.stats.notes.total - : sum(this.stats.notes.inc, negate(this.stats.notes.dec)) - ) - }] - }; - }, - - ffChart(total: boolean): any { - return { - series: [{ - name: 'Following', - color: '#008FFB', - data: this.format(total - ? this.stats.following.total - : sum(this.stats.following.inc, negate(this.stats.following.dec)) - ) - }, { - name: 'Followers', - color: '#00E396', - data: this.format(total - ? this.stats.followers.total - : sum(this.stats.followers.inc, negate(this.stats.followers.dec)) - ) - }] - }; - }, - - driveUsageChart(total: boolean): any { - return { - bytes: true, - series: [{ - name: 'Drive usage', - color: '#008FFB', - data: this.format(total - ? this.stats.drive.totalUsage - : sum(this.stats.drive.incUsage, negate(this.stats.drive.decUsage)) - ) - }] - }; - }, - - driveFilesChart(total: boolean): any { - return { - series: [{ - name: 'Drive files', - color: '#008FFB', - data: this.format(total - ? this.stats.drive.totalFiles - : sum(this.stats.drive.incFiles, negate(this.stats.drive.decFiles)) - ) - }] - }; - }, - showFollowing() { os.modal(MkUsersDialog, { title: this.$ts.instanceFollowing, diff --git a/src/client/pages/instance/metrics.vue b/src/client/pages/instance/metrics.vue index 1606063aee..da36f6c688 100644 --- a/src/client/pages/instance/metrics.vue +++ b/src/client/pages/instance/metrics.vue @@ -52,7 +52,21 @@ <script lang="ts"> import { defineComponent, markRaw } from 'vue'; -import Chart from 'chart.js'; +import { + Chart, + ArcElement, + LineElement, + BarElement, + PointElement, + BarController, + LineController, + CategoryScale, + LinearScale, + Legend, + Title, + Tooltip, + SubTitle +} from 'chart.js'; import MkButton from '@client/components/ui/button.vue'; import MkSelect from '@client/components/form/select.vue'; import MkInput from '@client/components/form/input.vue'; @@ -64,6 +78,21 @@ import bytes from '@client/filters/bytes'; import number from '@client/filters/number'; import MkInstanceInfo from './instance.vue'; +Chart.register( + ArcElement, + LineElement, + BarElement, + PointElement, + BarController, + LineController, + CategoryScale, + LinearScale, + Legend, + Title, + Tooltip, + SubTitle +); + const alpha = (hex, a) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!; const r = parseInt(result[1], 16); @@ -116,7 +145,7 @@ export default defineComponent({ mounted() { this.fetchJobs(); - Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); + Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); os.api('admin/server-info', {}).then(res => { this.serverInfo = res; @@ -157,7 +186,7 @@ export default defineComponent({ datasets: [{ label: 'CPU', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#86b300', backgroundColor: alpha('#86b300', 0.1), @@ -165,7 +194,7 @@ export default defineComponent({ }, { label: 'MEM (active)', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#935dbf', backgroundColor: alpha('#935dbf', 0.02), @@ -173,7 +202,7 @@ export default defineComponent({ }, { label: 'MEM (used)', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#935dbf', borderDash: [5, 5], @@ -198,7 +227,7 @@ export default defineComponent({ } }, scales: { - xAxes: [{ + x: { gridLines: { display: false, color: this.gridColor, @@ -207,8 +236,8 @@ export default defineComponent({ ticks: { display: false, } - }], - yAxes: [{ + }, + y: { position: 'right', gridLines: { display: true, @@ -219,7 +248,7 @@ export default defineComponent({ display: false, max: 100 } - }] + } }, tooltips: { intersect: false, @@ -238,7 +267,7 @@ export default defineComponent({ datasets: [{ label: 'In', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#94a029', backgroundColor: alpha('#94a029', 0.1), @@ -246,7 +275,7 @@ export default defineComponent({ }, { label: 'Out', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#ff9156', backgroundColor: alpha('#ff9156', 0.1), @@ -270,7 +299,7 @@ export default defineComponent({ } }, scales: { - xAxes: [{ + x: { gridLines: { display: false, color: this.gridColor, @@ -279,8 +308,8 @@ export default defineComponent({ ticks: { display: false } - }], - yAxes: [{ + }, + y: { position: 'right', gridLines: { display: true, @@ -290,7 +319,7 @@ export default defineComponent({ ticks: { display: false, } - }] + } }, tooltips: { intersect: false, @@ -309,7 +338,7 @@ export default defineComponent({ datasets: [{ label: 'Read', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#94a029', backgroundColor: alpha('#94a029', 0.1), @@ -317,7 +346,7 @@ export default defineComponent({ }, { label: 'Write', pointRadius: 0, - lineTension: 0, + tension: 0, borderWidth: 2, borderColor: '#ff9156', backgroundColor: alpha('#ff9156', 0.1), @@ -341,7 +370,7 @@ export default defineComponent({ } }, scales: { - xAxes: [{ + x: { gridLines: { display: false, color: this.gridColor, @@ -350,8 +379,8 @@ export default defineComponent({ ticks: { display: false } - }], - yAxes: [{ + }, + y: { position: 'right', gridLines: { display: true, @@ -361,7 +390,7 @@ export default defineComponent({ ticks: { display: false, } - }] + } }, tooltips: { intersect: false, @@ -371,18 +400,6 @@ export default defineComponent({ })); }, - async showInstanceInfo(q) { - let instance = q; - if (typeof q === 'string') { - instance = await os.api('federation/show-instance', { - host: q - }); - } - os.popup(MkInstanceInfo, { - instance: instance - }, {}, 'closed'); - }, - fetchJobs() { os.api('admin/queue/deliver-delayed', {}).then(jobs => { this.jobs = jobs; diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue index c6db9d0c04..4a01eeb751 100644 --- a/src/client/pages/instance/overview.vue +++ b/src/client/pages/instance/overview.vue @@ -1,61 +1,67 @@ <template> -<FormBase> - <FormSuspense :p="init"> - <FormSuspense :p="fetchStats" v-slot="{ result: stats }"> - <FormGroup> - <FormKeyValueView> - <template #key>Users</template> - <template #value>{{ number(stats.originalUsersCount) }}</template> - </FormKeyValueView> - <FormKeyValueView> - <template #key>Notes</template> - <template #value>{{ number(stats.originalNotesCount) }}</template> - </FormKeyValueView> - </FormGroup> - </FormSuspense> - - <div class="_debobigegoItem"> - <div class="_debobigegoPanel"> - <MkInstanceStats :chart-limit="300" :detailed="true"/> +<div> + <MkHeader :info="header"/> + + <div class="edbbcaef"> + <div class="numbers" v-if="stats"> + <div class="number _panel"> + <div class="label">Users</div> + <div class="value _monospace"> + {{ number(stats.originalUsersCount) }} + <MkNumberDiff v-if="usersComparedToThePrevDay" class="diff" :value="usersComparedToThePrevDay" v-tooltip="$ts.dayOverDayChanges"><template #before>(</template><template #after>)</template></MkNumberDiff> + </div> + </div> + <div class="number _panel"> + <div class="label">Notes</div> + <div class="value _monospace"> + {{ number(stats.originalNotesCount) }} + <MkNumberDiff v-if="notesComparedToThePrevDay" class="diff" :value="notesComparedToThePrevDay" v-tooltip="$ts.dayOverDayChanges"><template #before>(</template><template #after>)</template></MkNumberDiff> + </div> </div> </div> - <XMetrics/> + <MkContainer :foldable="true" class="charts"> + <template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template> + <div style="padding-top: 12px;"> + <MkInstanceStats :chart-limit="500" :detailed="true"/> + </div> + </MkContainer> + + <!--<XMetrics/>--> - <FormSuspense :p="fetchServerInfo" v-slot="{ result: serverInfo }"> - <FormGroup> - <FormKeyValueView> - <template #key>Node.js</template> - <template #value>{{ serverInfo.node }}</template> - </FormKeyValueView> - <FormKeyValueView> - <template #key>PostgreSQL</template> - <template #value>{{ serverInfo.psql }}</template> - </FormKeyValueView> - <FormKeyValueView> - <template #key>Redis</template> - <template #value>{{ serverInfo.redis }}</template> - </FormKeyValueView> - </FormGroup> - </FormSuspense> - </FormSuspense> -</FormBase> + <div class="numbers"> + <div class="number _panel"> + <div class="label">Misskey</div> + <div class="value _monospace">{{ version }}</div> + </div> + <div class="number _panel" v-if="serverInfo"> + <div class="label">Node.js</div> + <div class="value _monospace">{{ serverInfo.node }}</div> + </div> + <div class="number _panel" v-if="serverInfo"> + <div class="label">PostgreSQL</div> + <div class="value _monospace">{{ serverInfo.psql }}</div> + </div> + <div class="number _panel" v-if="serverInfo"> + <div class="label">Redis</div> + <div class="value _monospace">{{ serverInfo.redis }}</div> + </div> + <div class="number _panel"> + <div class="label">Vue</div> + <div class="value _monospace">{{ vueVersion }}</div> + </div> + </div> + </div> +</div> </template> <script lang="ts"> -import { computed, defineComponent, markRaw } from 'vue'; +import { computed, defineComponent, version as vueVersion } from 'vue'; import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; -import FormInput from '@client/components/debobigego/input.vue'; -import FormButton from '@client/components/debobigego/button.vue'; -import FormBase from '@client/components/debobigego/base.vue'; -import FormGroup from '@client/components/debobigego/group.vue'; -import FormTextarea from '@client/components/debobigego/textarea.vue'; -import FormInfo from '@client/components/debobigego/info.vue'; -import FormSuspense from '@client/components/debobigego/suspense.vue'; import MkInstanceStats from '@client/components/instance-stats.vue'; import MkButton from '@client/components/ui/button.vue'; import MkSelect from '@client/components/form/select.vue'; -import MkInput from '@client/components/form/input.vue'; +import MkNumberDiff from '@client/components/number-diff.vue'; import MkContainer from '@client/components/ui/container.vue'; import MkFolder from '@client/components/ui/folder.vue'; import { version, url } from '@client/config'; @@ -68,12 +74,10 @@ import * as symbols from '@client/symbols'; export default defineComponent({ components: { - FormBase, - FormSuspense, - FormGroup, - FormInfo, + MkNumberDiff, FormKeyValueView, MkInstanceStats, + MkContainer, XMetrics, }, @@ -82,17 +86,22 @@ export default defineComponent({ data() { return { [symbols.PAGE_INFO]: { - title: this.$ts.overview, + title: this.$ts.dashboard, icon: 'fas fa-tachometer-alt', bg: 'var(--bg)', }, - page: 'index', + header: { + title: this.$ts.dashboard, + icon: 'fas fa-tachometer-alt', + }, version, + vueVersion, url, stats: null, meta: null, - fetchStats: () => os.api('stats', {}), - fetchServerInfo: () => os.api('admin/server-info', {}), + serverInfo: null, + usersComparedToThePrevDay: null, + notesComparedToThePrevDay: null, fetchJobs: () => os.api('admin/queue/deliver-delayed', {}), fetchModLogs: () => os.api('admin/show-moderation-logs', {}), } @@ -100,13 +109,29 @@ export default defineComponent({ async mounted() { this.$emit('info', this[symbols.PAGE_INFO]); + + os.api('meta', { detail: true }).then(meta => { + this.meta = meta; + }); + + os.api('stats', {}).then(stats => { + this.stats = stats; + + os.api('charts/users', { limit: 2, span: 'day' }).then(chart => { + this.usersComparedToThePrevDay = this.stats.originalUsersCount - chart.local.total[1]; + }); + + os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => { + this.notesComparedToThePrevDay = this.stats.originalNotesCount - chart.local.total[1]; + }); + }); + + os.api('admin/server-info', {}).then(serverInfo => { + this.serverInfo = serverInfo; + }); }, methods: { - async init() { - this.meta = await os.api('meta', { detail: true }); - }, - async showInstanceInfo(q) { let instance = q; if (typeof q === 'string') { @@ -125,3 +150,36 @@ export default defineComponent({ } }); </script> + +<style lang="scss" scoped> +.edbbcaef { + > .numbers { + display: grid; + grid-gap: 8px; + grid-template-columns: repeat(auto-fill,minmax(130px,1fr)); + margin: 16px; + + > .number { + padding: 12px 16px; + + > .label { + opacity: 0.7; + font-size: 0.8em; + } + + > .value { + font-weight: bold; + font-size: 1.2em; + + > .diff { + font-size: 0.8em; + } + } + } + } + + > .charts { + margin: var(--margin); + } +} +</style> diff --git a/src/client/pages/instance/queue.chart.vue b/src/client/pages/instance/queue.chart.vue index 887fe9a574..4f8fd762bb 100644 --- a/src/client/pages/instance/queue.chart.vue +++ b/src/client/pages/instance/queue.chart.vue @@ -67,7 +67,7 @@ export default defineComponent({ // TODO: var(--panel)の色が暗いか明るいかで判定する const gridColor = this.$store.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; - Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); + Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); this.chart = markRaw(new Chart(this.$refs.chart, { type: 'line', |