diff options
Diffstat (limited to 'src/client/pages/instance/index.vue')
| -rw-r--r-- | src/client/pages/instance/index.vue | 674 |
1 files changed, 337 insertions, 337 deletions
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 5a48232417..db88982330 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -1,169 +1,54 @@ <template> -<div v-if="meta" class="mk-instance-page"> +<div v-if="meta" class="xhexznfu"> <portal to="icon"><fa :icon="faServer"/></portal> <portal to="title">{{ $t('instance') }}</portal> - <section class="_card info"> - <div class="_title"><fa :icon="faInfoCircle"/> {{ $t('basicInfo') }}</div> - <div class="_content"> - <mk-input v-model="name">{{ $t('instanceName') }}</mk-input> - <mk-textarea v-model="description">{{ $t('instanceDescription') }}</mk-textarea> - <mk-input v-model="iconUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('iconUrl') }}</mk-input> - <mk-input v-model="bannerUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('bannerUrl') }}</mk-input> - <mk-input v-model="tosUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('tosUrl') }}</mk-input> - <mk-input v-model="maintainerName">{{ $t('maintainerName') }}</mk-input> - <mk-input v-model="maintainerEmail" type="email"><template #icon><fa :icon="faEnvelope"/></template>{{ $t('maintainerEmail') }}</mk-input> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> - </div> - </section> - - <section class="_card info"> - <div class="_content"> - <mk-input v-model="maxNoteTextLength" type="number" :save="() => save()" style="margin:0;"><template #icon><fa :icon="faPencilAlt"/></template>{{ $t('maxNoteTextLength') }}</mk-input> - </div> - <div class="_content"> - <mk-switch v-model="enableLocalTimeline" @change="save()">{{ $t('enableLocalTimeline') }}</mk-switch> - <mk-switch v-model="enableGlobalTimeline" @change="save()">{{ $t('enableGlobalTimeline') }}</mk-switch> - <mk-info>{{ $t('disablingTimelinesInfo') }}</mk-info> - </div> - </section> - - <section class="_card info"> - <div class="_title"><fa :icon="faUser"/> {{ $t('registration') }}</div> - <div class="_content"> - <mk-switch v-model="enableRegistration" @change="save()">{{ $t('enableRegistration') }}</mk-switch> - <mk-button v-if="!enableRegistration" @click="invite">{{ $t('invite') }}</mk-button> - </div> - </section> - - <section class="_card"> - <div class="_title"><fa :icon="faShieldAlt"/> {{ $t('recaptcha') }}</div> - <div class="_content"> - <mk-switch v-model="enableRecaptcha">{{ $t('enableRecaptcha') }}</mk-switch> - <template v-if="enableRecaptcha"> - <mk-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa :icon="faKey"/></template>{{ $t('recaptchaSiteKey') }}</mk-input> - <mk-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa :icon="faKey"/></template>{{ $t('recaptchaSecretKey') }}</mk-input> - </template> - </div> - <div class="_content" v-if="enableRecaptcha && recaptchaSiteKey"> - <header>{{ $t('preview') }}</header> - <div ref="recaptcha" style="margin: 16px 0 0 0;" :key="recaptchaSiteKey"></div> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> - </div> - </section> - - <section class="_card"> - <div class="_title"><fa :icon="faBolt"/> {{ $t('serviceworker') }}</div> - <div class="_content"> - <mk-switch v-model="enableServiceWorker">{{ $t('enableServiceworker') }}<template #desc>{{ $t('serviceworkerInfo') }}</template></mk-switch> - <template v-if="enableServiceWorker"> - <mk-horizon-group inputs class="fit-bottom"> - <mk-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>Public key</mk-input> - <mk-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>Private key</mk-input> - </mk-horizon-group> - </template> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> - </div> - </section> - - <section class="_card"> - <div class="_title"><fa :icon="faThumbtack"/> {{ $t('pinnedUsers') }}</div> - <div class="_content"> - <mk-textarea v-model="pinnedUsers"> - <template #desc>{{ $t('pinnedUsersDescription') }} <button class="_textButton" @click="addPinUser">{{ $t('addUser') }}</button></template> - </mk-textarea> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> - </div> - </section> - - <section class="_card"> - <div class="_title"><fa :icon="faCloud"/> {{ $t('files') }}</div> - <div class="_content"> - <mk-switch v-model="cacheRemoteFiles">{{ $t('cacheRemoteFiles') }}<template #desc>{{ $t('cacheRemoteFilesDescription') }}</template></mk-switch> - <mk-switch v-model="proxyRemoteFiles">{{ $t('proxyRemoteFiles') }}<template #desc>{{ $t('proxyRemoteFilesDescription') }}</template></mk-switch> - <mk-input v-model="localDriveCapacityMb" type="number">{{ $t('driveCapacityPerLocalAccount') }}<template #suffix>MB</template><template #desc>{{ $t('inMb') }}</template></mk-input> - <mk-input v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" style="margin-bottom: 0;">{{ $t('driveCapacityPerRemoteAccount') }}<template #suffix>MB</template><template #desc>{{ $t('inMb') }}</template></mk-input> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> - </div> - </section> + <mk-instance-stats style="margin-bottom: var(--margin);"/> - <section class="_card"> - <div class="_title"><fa :icon="faGhost"/> {{ $t('proxyAccount') }}</div> - <div class="_content"> - <mk-input :value="proxyAccount ? proxyAccount.username : null" style="margin: 0;" disabled><template #prefix>@</template>{{ $t('proxyAccount') }}<template #desc>{{ $t('proxyAccountDescription') }}</template></mk-input> - <mk-button primary @click="chooseProxyAccount">{{ $t('chooseProxyAccount') }}</mk-button> + <section class="_card chart"> + <div class="_title"><fa :icon="faMicrochip"/> {{ $t('cpuAndMemory') }}</div> + <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> + <canvas ref="cpumem"></canvas> </div> - </section> - - <section class="_card"> - <div class="_title"><fa :icon="faBan"/> {{ $t('blockedInstances') }}</div> - <div class="_content"> - <mk-textarea v-model="blockedHosts"> - <template #desc>{{ $t('blockedInstancesDescription') }}</template> - </mk-textarea> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> + <div class="_content" v-if="serverInfo"> + <div class="table"> + <div class="row"> + <div class="cell"><div class="label">CPU</div>{{ serverInfo.cpu.model }}</div> + </div> + <div class="row"> + <div class="cell"><div class="label">MEM total</div>{{ serverInfo.mem.total | bytes }}</div> + <div class="cell"><div class="label">MEM used</div>{{ memUsage | bytes }} ({{ (memUsage / serverInfo.mem.total * 100).toFixed(0) }}%)</div> + <div class="cell"><div class="label">MEM free</div>{{ serverInfo.mem.total - memUsage | bytes }} ({{ ((serverInfo.mem.total - memUsage) / serverInfo.mem.total * 100).toFixed(0) }}%)</div> + </div> + </div> </div> </section> - - <section class="_card"> - <div class="_title"><fa :icon="faShareAlt"/> {{ $t('integration') }}</div> - <div class="_content"> - <header><fa :icon="faTwitter"/> Twitter</header> - <mk-switch v-model="enableTwitterIntegration">{{ $t('enable') }}</mk-switch> - <template v-if="enableTwitterIntegration"> - <mk-info>Callback URL: {{ `${url}/api/tw/cb` }}</mk-info> - <mk-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><fa :icon="faKey"/></template>Consumer Key</mk-input> - <mk-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><fa :icon="faKey"/></template>Consumer Secret</mk-input> - </template> - </div> - <div class="_content"> - <header><fa :icon="faGithub"/> GitHub</header> - <mk-switch v-model="enableGithubIntegration">{{ $t('enable') }}</mk-switch> - <template v-if="enableGithubIntegration"> - <mk-info>Callback URL: {{ `${url}/api/gh/cb` }}</mk-info> - <mk-input v-model="githubClientId" :disabled="!enableGithubIntegration"><template #icon><fa :icon="faKey"/></template>Client ID</mk-input> - <mk-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><fa :icon="faKey"/></template>Client Secret</mk-input> - </template> + <section class="_card chart"> + <div class="_title"><fa :icon="faHdd"/> {{ $t('disk') }}</div> + <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> + <canvas ref="disk"></canvas> </div> - <div class="_content"> - <header><fa :icon="faDiscord"/> Discord</header> - <mk-switch v-model="enableDiscordIntegration">{{ $t('enable') }}</mk-switch> - <template v-if="enableDiscordIntegration"> - <mk-info>Callback URL: {{ `${url}/api/dc/cb` }}</mk-info> - <mk-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><fa :icon="faKey"/></template>Client ID</mk-input> - <mk-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><fa :icon="faKey"/></template>Client Secret</mk-input> - </template> - </div> - <div class="_footer"> - <mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> + <div class="_content" v-if="serverInfo"> + <div class="table"> + <div class="row"> + <div class="cell"><div class="label">Disk total</div>{{ serverInfo.fs.total | bytes }}</div> + <div class="cell"><div class="label">Disk used</div>{{ serverInfo.fs.used | bytes }} ({{ (serverInfo.fs.used / serverInfo.fs.total * 100).toFixed(0) }}%)</div> + <div class="cell"><div class="label">Disk free</div>{{ serverInfo.fs.total - serverInfo.fs.used | bytes }} ({{ ((serverInfo.fs.total - serverInfo.fs.used) / serverInfo.fs.total * 100).toFixed(0) }}%)</div> + </div> + </div> </div> </section> - - <section class="_card info"> - <div class="_title"><fa :icon="faInfoCircle"/> {{ $t('instanceInfo') }}</div> - <div class="_content table" v-if="stats"> - <div><b>{{ $t('users') }}</b><span>{{ stats.originalUsersCount | number }}</span></div> - <div><b>{{ $t('notes') }}</b><span>{{ stats.originalNotesCount | number }}</span></div> + <section class="_card chart"> + <div class="_title"><fa :icon="faExchangeAlt"/> {{ $t('network') }}</div> + <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> + <canvas ref="net"></canvas> </div> - <div class="_content table"> - <div><b>Misskey</b><span>v{{ version }}</span></div> - </div> - <div class="_content table" v-if="serverInfo"> - <div><b>Node.js</b><span>{{ serverInfo.node }}</span></div> - <div><b>PostgreSQL</b><span>v{{ serverInfo.psql }}</span></div> - <div><b>Redis</b><span>v{{ serverInfo.redis }}</span></div> + <div class="_content" v-if="serverInfo"> + <div class="table"> + <div class="row"> + <div class="cell"><div class="label">Interface</div>{{ serverInfo.net.interface }}</div> + </div> + </div> </div> </section> </div> @@ -171,18 +56,19 @@ <script lang="ts"> import Vue from 'vue'; -import { faPencilAlt, faShareAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faThumbtack, faUser, faShieldAlt, faKey, faBolt } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt, faEnvelope } from '@fortawesome/free-regular-svg-icons'; -import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'; -import MkButton from '../../components/ui/button.vue'; -import MkInput from '../../components/ui/input.vue'; -import MkTextarea from '../../components/ui/textarea.vue'; -import MkSwitch from '../../components/ui/switch.vue'; -import MkInfo from '../../components/ui/info.vue'; -import MkUserSelect from '../../components/user-select.vue'; +import { faServer, faExchangeAlt, faMicrochip, faHdd } from '@fortawesome/free-solid-svg-icons'; +import Chart from 'chart.js'; +import MkInstanceStats from '../../components/instance-stats.vue'; import { version, url } from '../../config'; import i18n from '../../i18n'; -import getAcct from '../../../misc/acct/render'; + +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); + const g = parseInt(result[2], 16); + const b = parseInt(result[3], 16); + return `rgba(${r}, ${g}, ${b}, ${a})`; +}; export default Vue.extend({ i18n, @@ -194,11 +80,7 @@ export default Vue.extend({ }, components: { - MkButton, - MkInput, - MkTextarea, - MkSwitch, - MkInfo, + MkInstanceStats, }, data() { @@ -207,41 +89,11 @@ export default Vue.extend({ url, stats: null, serverInfo: null, - proxyAccount: null, - proxyAccountId: null, - cacheRemoteFiles: false, - proxyRemoteFiles: false, - localDriveCapacityMb: 0, - remoteDriveCapacityMb: 0, - blockedHosts: '', - pinnedUsers: '', - maintainerName: null, - maintainerEmail: null, - name: null, - description: null, - tosUrl: null, - bannerUrl: null, - iconUrl: null, - maxNoteTextLength: 0, - enableRegistration: false, - enableLocalTimeline: false, - enableGlobalTimeline: false, - enableRecaptcha: false, - recaptchaSiteKey: null, - recaptchaSecretKey: null, - enableServiceWorker: false, - swPublicKey: null, - swPrivateKey: null, - enableTwitterIntegration: false, - twitterConsumerKey: null, - twitterConsumerSecret: null, - enableGithubIntegration: false, - githubClientId: null, - githubClientSecret: null, - enableDiscordIntegration: false, - discordClientId: null, - discordClientSecret: null, - faPencilAlt, faTwitter, faDiscord, faGithub, faShareAlt, faTrashAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faEnvelope, faThumbtack, faUser, faShieldAlt, faKey, faBolt + connection: null, + memUsage: 0, + chartCpuMem: null, + chartNet: null, + faServer, faExchangeAlt, faMicrochip, faHdd } }, @@ -251,160 +103,308 @@ export default Vue.extend({ }, }, - created() { - this.name = this.meta.name; - this.description = this.meta.description; - this.tosUrl = this.meta.tosUrl; - this.bannerUrl = this.meta.bannerUrl; - this.iconUrl = this.meta.iconUrl; - this.maintainerName = this.meta.maintainerName; - this.maintainerEmail = this.meta.maintainerEmail; - this.maxNoteTextLength = this.meta.maxNoteTextLength; - this.enableRegistration = !this.meta.disableRegistration; - this.enableLocalTimeline = !this.meta.disableLocalTimeline; - this.enableGlobalTimeline = !this.meta.disableGlobalTimeline; - this.enableRecaptcha = this.meta.enableRecaptcha; - this.recaptchaSiteKey = this.meta.recaptchaSiteKey; - this.recaptchaSecretKey = this.meta.recaptchaSecretKey; - this.proxyAccountId = this.meta.proxyAccountId; - this.cacheRemoteFiles = this.meta.cacheRemoteFiles; - this.proxyRemoteFiles = this.meta.proxyRemoteFiles; - this.localDriveCapacityMb = this.meta.driveCapacityPerLocalUserMb; - this.remoteDriveCapacityMb = this.meta.driveCapacityPerRemoteUserMb; - this.blockedHosts = this.meta.blockedHosts.join('\n'); - this.pinnedUsers = this.meta.pinnedUsers.join('\n'); - this.enableServiceWorker = this.meta.enableServiceWorker; - this.swPublicKey = this.meta.swPublickey; - this.swPrivateKey = this.meta.swPrivateKey; - this.enableTwitterIntegration = this.meta.enableTwitterIntegration; - this.twitterConsumerKey = this.meta.twitterConsumerKey; - this.twitterConsumerSecret = this.meta.twitterConsumerSecret; - this.enableGithubIntegration = this.meta.enableGithubIntegration; - this.githubClientId = this.meta.githubClientId; - this.githubClientSecret = this.meta.githubClientSecret; - this.enableDiscordIntegration = this.meta.enableDiscordIntegration; - this.discordClientId = this.meta.discordClientId; - this.discordClientSecret = this.meta.discordClientSecret; + mounted() { + Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); - if (this.proxyAccountId) { - this.$root.api('users/show', { userId: this.proxyAccountId }).then(proxyAccount => { - this.proxyAccount = proxyAccount; - }); - } + this.chartCpuMem = new Chart(this.$refs.cpumem, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'CPU', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#86b300', + backgroundColor: alpha('#86b300', 0.1), + data: [] + }, { + label: 'MEM (active)', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#935dbf', + backgroundColor: alpha('#935dbf', 0.02), + data: [] + }, { + label: 'MEM (used)', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#935dbf', + borderDash: [5, 5], + fill: false, + data: [] + }] + }, + options: { + aspectRatio: 3, + layout: { + padding: { + left: 0, + right: 0, + top: 8, + bottom: 0 + } + }, + legend: { + position: 'bottom', + labels: { + boxWidth: 16, + } + }, + scales: { + xAxes: [{ + gridLines: { + display: false + }, + ticks: { + display: false + } + }], + yAxes: [{ + position: 'right', + ticks: { + display: false, + max: 100 + } + }] + }, + tooltips: { + intersect: false, + mode: 'index', + } + } + }); - this.$root.api('admin/server-info').then(res => { - this.serverInfo = res; + this.chartNet = new Chart(this.$refs.net, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'In', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#94a029', + backgroundColor: alpha('#94a029', 0.1), + data: [] + }, { + label: 'Out', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#ff9156', + backgroundColor: alpha('#ff9156', 0.1), + data: [] + }] + }, + options: { + aspectRatio: 3, + layout: { + padding: { + left: 0, + right: 0, + top: 8, + 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', + } + } }); - this.$root.api('stats').then(res => { - this.stats = res; + this.chartDisk = new Chart(this.$refs.disk, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Read', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#94a029', + backgroundColor: alpha('#94a029', 0.1), + data: [] + }, { + label: 'Write', + pointRadius: 0, + lineTension: 0, + borderWidth: 2, + borderColor: '#ff9156', + backgroundColor: alpha('#ff9156', 0.1), + data: [] + }] + }, + options: { + aspectRatio: 3, + layout: { + padding: { + left: 0, + right: 0, + top: 8, + 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', + } + } }); - }, - mounted() { - const renderRecaptchaPreview = () => { - if (!(window as any).grecaptcha) return; - if (!this.$refs.recaptcha) return; - if (!this.recaptchaSiteKey) return; - (window as any).grecaptcha.render(this.$refs.recaptcha, { - sitekey: this.recaptchaSiteKey + this.$root.api('admin/server-info', {}).then(res => { + this.serverInfo = res; + + this.connection = this.$root.stream.useSharedConnection('serverStats'); + this.connection.on('stats', this.onStats); + this.connection.on('statsLog', this.onStatsLog); + this.connection.send('requestLog', { + id: Math.random().toString().substr(2, 8), + length: 150 }); - }; - window.onRecaotchaLoad = () => { - renderRecaptchaPreview(); - }; - const head = document.getElementsByTagName('head')[0]; - const script = document.createElement('script'); - script.setAttribute('src', 'https://www.google.com/recaptcha/api.js?onload=onRecaotchaLoad'); - head.appendChild(script); - this.$watch('enableRecaptcha', () => { - renderRecaptchaPreview(); - }); - this.$watch('recaptchaSiteKey', () => { - renderRecaptchaPreview(); }); }, + beforeDestroy() { + this.connection.off('stats', this.onStats); + this.connection.off('statsLog', this.onStatsLog); + this.connection.dispose(); + }, + methods: { - addPinUser() { - this.$root.new(MkUserSelect, {}).$once('selected', user => { - this.pinnedUsers = this.pinnedUsers.trim(); - this.pinnedUsers += '\n@' + getAcct(user); - this.pinnedUsers = this.pinnedUsers.trim(); - }); - }, + onStats(stats) { + 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); + this.memUsage = stats.mem.active; - chooseProxyAccount() { - this.$root.new(MkUserSelect, {}).$once('selected', user => { - this.proxyAccount = user; - this.proxyAccountId = user.id; - this.save(true); - }); + this.chartCpuMem.data.labels.push(''); + this.chartCpuMem.data.datasets[0].data.push(cpu); + this.chartCpuMem.data.datasets[1].data.push(memActive); + this.chartCpuMem.data.datasets[2].data.push(memUsed); + this.chartNet.data.labels.push(''); + this.chartNet.data.datasets[0].data.push(stats.net.rx); + this.chartNet.data.datasets[1].data.push(stats.net.tx); + this.chartDisk.data.labels.push(''); + this.chartDisk.data.datasets[0].data.push(stats.fs.r); + this.chartDisk.data.datasets[1].data.push(stats.fs.w); + if (this.chartCpuMem.data.datasets[0].data.length > 150) { + this.chartCpuMem.data.labels.shift(); + this.chartCpuMem.data.datasets[0].data.shift(); + this.chartCpuMem.data.datasets[1].data.shift(); + this.chartCpuMem.data.datasets[2].data.shift(); + this.chartNet.data.labels.shift(); + this.chartNet.data.datasets[0].data.shift(); + this.chartNet.data.datasets[1].data.shift(); + this.chartDisk.data.labels.shift(); + this.chartDisk.data.datasets[0].data.shift(); + this.chartDisk.data.datasets[1].data.shift(); + } + this.chartCpuMem.update(); + this.chartNet.update(); + this.chartDisk.update(); }, - save(withDialog = false) { - this.$root.api('admin/update-meta', { - name: this.name, - description: this.description, - tosUrl: this.tosUrl, - bannerUrl: this.bannerUrl, - iconUrl: this.iconUrl, - maintainerName: this.maintainerName, - maintainerEmail: this.maintainerEmail, - maxNoteTextLength: this.maxNoteTextLength, - disableRegistration: !this.enableRegistration, - disableLocalTimeline: !this.enableLocalTimeline, - disableGlobalTimeline: !this.enableGlobalTimeline, - enableRecaptcha: this.enableRecaptcha, - recaptchaSiteKey: this.recaptchaSiteKey, - recaptchaSecretKey: this.recaptchaSecretKey, - proxyAccountId: this.proxyAccountId, - cacheRemoteFiles: this.cacheRemoteFiles, - proxyRemoteFiles: this.proxyRemoteFiles, - localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), - remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10), - blockedHosts: this.blockedHosts.split('\n') || [], - pinnedUsers: this.pinnedUsers ? this.pinnedUsers.split('\n') : [], - enableServiceWorker: this.enableServiceWorker, - swPublicKey: this.swPublicKey, - swPrivateKey: this.swPrivateKey, - enableTwitterIntegration: this.enableTwitterIntegration, - twitterConsumerKey: this.twitterConsumerKey, - twitterConsumerSecret: this.twitterConsumerSecret, - enableGithubIntegration: this.enableGithubIntegration, - githubClientId: this.githubClientId, - githubClientSecret: this.githubClientSecret, - enableDiscordIntegration: this.enableDiscordIntegration, - discordClientId: this.discordClientId, - discordClientSecret: this.discordClientSecret, - }).then(() => { - this.$store.dispatch('instance/fetch'); - if (withDialog) { - this.$root.dialog({ - type: 'success', - iconOnly: true, autoClose: true - }); - } - }).catch(e => { - this.$root.dialog({ - type: 'error', - text: e - }); - }); + onStatsLog(statsLog) { + for (const stats of statsLog.reverse()) { + this.onStats(stats); + } } } }); </script> <style lang="scss" scoped> -.mk-instance-page { - > .info { - > .table { - > div { - display: flex; +.xhexznfu { + > .stats { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + margin: calc(0px - var(--margin) / 2); + margin-bottom: calc(var(--margin) / 2); + + > div { + flex: 1 0 213px; + margin: calc(var(--margin) / 2); + box-sizing: border-box; + padding: 16px; + } + } + + > .chart { + > ._content { + > .table { + > .row { + display: flex; + + &:not(:last-child) { + margin-bottom: 16px; + + @media (max-width: 500px) { + margin-bottom: 8px; + } + } + + > .cell { + flex: 1; + + > .label { + font-size: 80%; + opacity: 0.7; - > * { - flex: 1; + > .icon { + margin-right: 4px; + display: none; + } + } + } } } } |