Improve instance info page
This commit is contained in:
parent
8fe153c7c1
commit
f45fb56e15
|
@ -403,6 +403,12 @@ regenerate: "再生成"
|
||||||
fontSize: "フォントサイズ"
|
fontSize: "フォントサイズ"
|
||||||
noFollowRequests: "フォロー申請はありません"
|
noFollowRequests: "フォロー申請はありません"
|
||||||
openImageInNewTab: "画像を新しいタブで開く"
|
openImageInNewTab: "画像を新しいタブで開く"
|
||||||
|
dashboard: "ダッシュボード"
|
||||||
|
local: "ローカル"
|
||||||
|
remote: "リモート"
|
||||||
|
total: "合計"
|
||||||
|
weekOverWeekChanges: "前週比"
|
||||||
|
dayOverDayChanges: "前日比"
|
||||||
|
|
||||||
_ago:
|
_ago:
|
||||||
unknown: "謎"
|
unknown: "謎"
|
||||||
|
|
|
@ -413,9 +413,14 @@ export default Vue.extend({
|
||||||
this.$root.menu({
|
this.$root.menu({
|
||||||
items: [{
|
items: [{
|
||||||
type: 'link',
|
type: 'link',
|
||||||
text: this.$t('statistics'),
|
text: this.$t('dashboard'),
|
||||||
to: '/instance/stats',
|
to: '/instance',
|
||||||
icon: faChartBar,
|
icon: faTachometerAlt,
|
||||||
|
}, null, {
|
||||||
|
type: 'link',
|
||||||
|
text: this.$t('settings'),
|
||||||
|
to: '/instance/settings',
|
||||||
|
icon: faCog,
|
||||||
}, {
|
}, {
|
||||||
type: 'link',
|
type: 'link',
|
||||||
text: this.$t('customEmojis'),
|
text: this.$t('customEmojis'),
|
||||||
|
@ -431,11 +436,6 @@ export default Vue.extend({
|
||||||
text: this.$t('files'),
|
text: this.$t('files'),
|
||||||
to: '/instance/files',
|
to: '/instance/files',
|
||||||
icon: faCloud,
|
icon: faCloud,
|
||||||
}, {
|
|
||||||
type: 'link',
|
|
||||||
text: this.$t('monitor'),
|
|
||||||
to: '/instance/monitor',
|
|
||||||
icon: faTachometerAlt,
|
|
||||||
}, {
|
}, {
|
||||||
type: 'link',
|
type: 'link',
|
||||||
text: this.$t('jobQueue'),
|
text: this.$t('jobQueue'),
|
||||||
|
@ -451,11 +451,6 @@ export default Vue.extend({
|
||||||
text: this.$t('announcements'),
|
text: this.$t('announcements'),
|
||||||
to: '/instance/announcements',
|
to: '/instance/announcements',
|
||||||
icon: faBroadcastTower,
|
icon: faBroadcastTower,
|
||||||
}, null, {
|
|
||||||
type: 'link',
|
|
||||||
text: this.$t('general'),
|
|
||||||
to: '/instance',
|
|
||||||
icon: faCog,
|
|
||||||
}],
|
}],
|
||||||
align: 'left',
|
align: 'left',
|
||||||
fixed: true,
|
fixed: true,
|
||||||
|
|
|
@ -1,8 +1,91 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-instance-stats">
|
<div class="zbcjwnqg">
|
||||||
|
<div class="stats" v-if="info">
|
||||||
|
<div class="_panel">
|
||||||
|
<div>
|
||||||
|
<b><fa :icon="faUser"/>{{ $t('users') }}</b>
|
||||||
|
<small>{{ $t('local') }}</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dl class="total">
|
||||||
|
<dt>{{ $t('total') }}</dt>
|
||||||
|
<dd>{{ info.originalUsersCount | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: usersLocalDoD > 0 }">
|
||||||
|
<dt>{{ $t('dayOverDayChanges') }}</dt>
|
||||||
|
<dd>{{ usersLocalDoD | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: usersLocalWoW > 0 }">
|
||||||
|
<dt>{{ $t('weekOverWeekChanges') }}</dt>
|
||||||
|
<dd>{{ usersLocalWoW | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<div>
|
||||||
|
<b><fa :icon="faUser"/>{{ $t('users') }}</b>
|
||||||
|
<small>{{ $t('remote') }}</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dl class="total">
|
||||||
|
<dt>{{ $t('total') }}</dt>
|
||||||
|
<dd>{{ (info.usersCount - info.originalUsersCount) | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: usersRemoteDoD > 0 }">
|
||||||
|
<dt>{{ $t('dayOverDayChanges') }}</dt>
|
||||||
|
<dd>{{ usersRemoteDoD | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: usersRemoteWoW > 0 }">
|
||||||
|
<dt>{{ $t('weekOverWeekChanges') }}</dt>
|
||||||
|
<dd>{{ usersRemoteWoW | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<div>
|
||||||
|
<b><fa :icon="faPencilAlt"/>{{ $t('notes') }}</b>
|
||||||
|
<small>{{ $t('local') }}</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dl class="total">
|
||||||
|
<dt>{{ $t('total') }}</dt>
|
||||||
|
<dd>{{ info.originalNotesCount | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: notesLocalDoD > 0 }">
|
||||||
|
<dt>{{ $t('dayOverDayChanges') }}</dt>
|
||||||
|
<dd>{{ notesLocalDoD | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: notesLocalWoW > 0 }">
|
||||||
|
<dt>{{ $t('weekOverWeekChanges') }}</dt>
|
||||||
|
<dd>{{ notesLocalWoW | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<div>
|
||||||
|
<b><fa :icon="faPencilAlt"/>{{ $t('notes') }}</b>
|
||||||
|
<small>{{ $t('remote') }}</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dl class="total">
|
||||||
|
<dt>{{ $t('total') }}</dt>
|
||||||
|
<dd>{{ (info.notesCount - info.originalNotesCount) | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: notesRemoteDoD > 0 }">
|
||||||
|
<dt>{{ $t('dayOverDayChanges') }}</dt>
|
||||||
|
<dd>{{ notesRemoteDoD | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="diff" :class="{ inc: notesRemoteWoW > 0 }">
|
||||||
|
<dt>{{ $t('weekOverWeekChanges') }}</dt>
|
||||||
|
<dd>{{ notesRemoteWoW | number }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section class="_card">
|
<section class="_card">
|
||||||
<div class="_title"><fa :icon="faChartBar"/> {{ $t('statistics') }}</div>
|
<div class="_title"><fa :icon="faChartBar"/> {{ $t('statistics') }}</div>
|
||||||
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
<div class="_content" style="margin-top: -8px;">
|
||||||
<div class="selects" style="display: flex;">
|
<div class="selects" style="display: flex;">
|
||||||
<mk-select v-model="chartSrc" style="margin: 0; flex: 1;">
|
<mk-select v-model="chartSrc" style="margin: 0; flex: 1;">
|
||||||
<optgroup :label="$t('federation')">
|
<optgroup :label="$t('federation')">
|
||||||
|
@ -40,10 +123,10 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { faChartBar } from '@fortawesome/free-solid-svg-icons';
|
import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Chart from 'chart.js';
|
import Chart from 'chart.js';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../i18n';
|
||||||
import MkSelect from '../../components/ui/select.vue';
|
import MkSelect from './ui/select.vue';
|
||||||
|
|
||||||
const chartLimit = 90;
|
const chartLimit = 90;
|
||||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||||
|
@ -59,24 +142,27 @@ const alpha = (hex, a) => {
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n,
|
i18n,
|
||||||
|
|
||||||
metaInfo() {
|
|
||||||
return {
|
|
||||||
title: `${this.$t('statistics')} | ${this.$t('instance')}`
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
MkSelect
|
MkSelect
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
info: null,
|
||||||
|
notesLocalWoW: 0,
|
||||||
|
notesLocalDoD: 0,
|
||||||
|
notesRemoteWoW: 0,
|
||||||
|
notesRemoteDoD: 0,
|
||||||
|
usersLocalWoW: 0,
|
||||||
|
usersLocalDoD: 0,
|
||||||
|
usersRemoteWoW: 0,
|
||||||
|
usersRemoteDoD: 0,
|
||||||
now: null,
|
now: null,
|
||||||
chart: null,
|
chart: null,
|
||||||
chartInstance: null,
|
chartInstance: null,
|
||||||
chartSrc: 'notes',
|
chartSrc: 'notes',
|
||||||
chartSpan: 'hour',
|
chartSpan: 'hour',
|
||||||
faChartBar
|
faChartBar, faUser, faPencilAlt
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -121,6 +207,8 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
|
this.info = await this.$root.api('stats');
|
||||||
|
|
||||||
this.now = new Date();
|
this.now = new Date();
|
||||||
|
|
||||||
const [perHour, perDay] = await Promise.all([Promise.all([
|
const [perHour, perDay] = await Promise.all([Promise.all([
|
||||||
|
@ -154,6 +242,15 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.usersCount - chart.perDay.users.local.total[7];
|
||||||
|
this.usersLocalDoD = this.info.usersCount - 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.chart = chart;
|
||||||
|
|
||||||
this.renderChart();
|
this.renderChart();
|
||||||
|
@ -489,3 +586,80 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.zbcjwnqg {
|
||||||
|
> .stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: calc(0px - var(--margin) / 2);
|
||||||
|
margin-bottom: calc(var(--margin) / 2);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 213px;
|
||||||
|
margin: calc(var(--margin) / 2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 16px 20px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
width: 50%;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
> b {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
> [data-icon] {
|
||||||
|
width: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> small {
|
||||||
|
margin-left: 16px + 8px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
> dl {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5em;
|
||||||
|
|
||||||
|
> dt,
|
||||||
|
> dd {
|
||||||
|
width: 50%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> dt {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.total {
|
||||||
|
> dt,
|
||||||
|
> dd {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.diff.inc {
|
||||||
|
> dd {
|
||||||
|
color: #82c11c;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "+";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -12,14 +12,12 @@
|
||||||
<div><b>{{ $t('administrator') }}</b><span>{{ meta.maintainerName }}</span></div>
|
<div><b>{{ $t('administrator') }}</b><span>{{ meta.maintainerName }}</span></div>
|
||||||
<div><b></b><span>{{ meta.maintainerEmail }}</span></div>
|
<div><b></b><span>{{ meta.maintainerEmail }}</span></div>
|
||||||
</div>
|
</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>
|
|
||||||
</div>
|
|
||||||
<div class="_content table">
|
<div class="_content table">
|
||||||
<div><b>Misskey</b><span>v{{ version }}</span></div>
|
<div><b>Misskey</b><span>v{{ version }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<mk-instance-stats style="margin-top: var(--margin);"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -28,6 +26,7 @@ import Vue from 'vue';
|
||||||
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { version } from '../config';
|
import { version } from '../config';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
|
import MkInstanceStats from '../components/instance-stats.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -38,10 +37,13 @@ export default Vue.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
MkInstanceStats
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
stats: null,
|
|
||||||
serverInfo: null,
|
serverInfo: null,
|
||||||
faInfoCircle
|
faInfoCircle
|
||||||
}
|
}
|
||||||
|
@ -52,12 +54,6 @@ export default Vue.extend({
|
||||||
return this.$store.state.instance.meta;
|
return this.$store.state.instance.meta;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
|
||||||
this.$root.api('stats').then(res => {
|
|
||||||
this.stats = res;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,169 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="meta" class="mk-instance-page">
|
<div v-if="meta" class="xhexznfu">
|
||||||
<portal to="icon"><fa :icon="faServer"/></portal>
|
<portal to="icon"><fa :icon="faServer"/></portal>
|
||||||
<portal to="title">{{ $t('instance') }}</portal>
|
<portal to="title">{{ $t('instance') }}</portal>
|
||||||
|
|
||||||
<section class="_card info">
|
<mk-instance-stats style="margin-bottom: var(--margin);"/>
|
||||||
<div class="_title"><fa :icon="faInfoCircle"/> {{ $t('basicInfo') }}</div>
|
|
||||||
<div class="_content">
|
<section class="_card chart">
|
||||||
<mk-input v-model="name">{{ $t('instanceName') }}</mk-input>
|
<div class="_title"><fa :icon="faMicrochip"/> {{ $t('cpuAndMemory') }}</div>
|
||||||
<mk-textarea v-model="description">{{ $t('instanceDescription') }}</mk-textarea>
|
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
||||||
<mk-input v-model="iconUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('iconUrl') }}</mk-input>
|
<canvas ref="cpumem"></canvas>
|
||||||
<mk-input v-model="bannerUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('bannerUrl') }}</mk-input>
|
</div>
|
||||||
<mk-input v-model="tosUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('tosUrl') }}</mk-input>
|
<div class="_content" v-if="serverInfo">
|
||||||
<mk-input v-model="maintainerName">{{ $t('maintainerName') }}</mk-input>
|
<div class="table">
|
||||||
<mk-input v-model="maintainerEmail" type="email"><template #icon><fa :icon="faEnvelope"/></template>{{ $t('maintainerEmail') }}</mk-input>
|
<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>
|
||||||
<div class="_footer">
|
|
||||||
<mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<section class="_card chart">
|
||||||
<section class="_card info">
|
<div class="_title"><fa :icon="faHdd"/> {{ $t('disk') }}</div>
|
||||||
<div class="_content">
|
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
||||||
<mk-input v-model="maxNoteTextLength" type="number" :save="() => save()" style="margin:0;"><template #icon><fa :icon="faPencilAlt"/></template>{{ $t('maxNoteTextLength') }}</mk-input>
|
<canvas ref="disk"></canvas>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<section class="_card chart">
|
||||||
<section class="_card info">
|
<div class="_title"><fa :icon="faExchangeAlt"/> {{ $t('network') }}</div>
|
||||||
<div class="_title"><fa :icon="faUser"/> {{ $t('registration') }}</div>
|
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
||||||
<div class="_content">
|
<canvas ref="net"></canvas>
|
||||||
<mk-switch v-model="enableRegistration" @change="save()">{{ $t('enableRegistration') }}</mk-switch>
|
|
||||||
<mk-button v-if="!enableRegistration" @click="invite">{{ $t('invite') }}</mk-button>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<div class="_content" v-if="serverInfo">
|
||||||
|
<div class="table">
|
||||||
<section class="_card">
|
<div class="row">
|
||||||
<div class="_title"><fa :icon="faShieldAlt"/> {{ $t('recaptcha') }}</div>
|
<div class="cell"><div class="label">Interface</div>{{ serverInfo.net.interface }}</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>
|
||||||
<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>
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
</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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -171,18 +56,19 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
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 { faServer, faExchangeAlt, faMicrochip, faHdd } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faTrashAlt, faEnvelope } from '@fortawesome/free-regular-svg-icons';
|
import Chart from 'chart.js';
|
||||||
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
|
import MkInstanceStats from '../../components/instance-stats.vue';
|
||||||
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 { version, url } from '../../config';
|
import { version, url } from '../../config';
|
||||||
import i18n from '../../i18n';
|
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({
|
export default Vue.extend({
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -194,11 +80,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
MkButton,
|
MkInstanceStats,
|
||||||
MkInput,
|
|
||||||
MkTextarea,
|
|
||||||
MkSwitch,
|
|
||||||
MkInfo,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -207,41 +89,11 @@ export default Vue.extend({
|
||||||
url,
|
url,
|
||||||
stats: null,
|
stats: null,
|
||||||
serverInfo: null,
|
serverInfo: null,
|
||||||
proxyAccount: null,
|
connection: null,
|
||||||
proxyAccountId: null,
|
memUsage: 0,
|
||||||
cacheRemoteFiles: false,
|
chartCpuMem: null,
|
||||||
proxyRemoteFiles: false,
|
chartNet: null,
|
||||||
localDriveCapacityMb: 0,
|
faServer, faExchangeAlt, faMicrochip, faHdd
|
||||||
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
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -251,160 +103,308 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
mounted() {
|
||||||
this.name = this.meta.name;
|
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
||||||
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;
|
|
||||||
|
|
||||||
if (this.proxyAccountId) {
|
this.chartCpuMem = new Chart(this.$refs.cpumem, {
|
||||||
this.$root.api('users/show', { userId: this.proxyAccountId }).then(proxyAccount => {
|
type: 'line',
|
||||||
this.proxyAccount = proxyAccount;
|
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.$root.api('stats').then(res => {
|
this.chartNet = new Chart(this.$refs.net, {
|
||||||
this.stats = res;
|
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.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',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
beforeDestroy() {
|
||||||
const renderRecaptchaPreview = () => {
|
this.connection.off('stats', this.onStats);
|
||||||
if (!(window as any).grecaptcha) return;
|
this.connection.off('statsLog', this.onStatsLog);
|
||||||
if (!this.$refs.recaptcha) return;
|
this.connection.dispose();
|
||||||
if (!this.recaptchaSiteKey) return;
|
|
||||||
(window as any).grecaptcha.render(this.$refs.recaptcha, {
|
|
||||||
sitekey: this.recaptchaSiteKey
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
addPinUser() {
|
onStats(stats) {
|
||||||
this.$root.new(MkUserSelect, {}).$once('selected', user => {
|
const cpu = (stats.cpu * 100).toFixed(0);
|
||||||
this.pinnedUsers = this.pinnedUsers.trim();
|
const memActive = (stats.mem.active / this.serverInfo.mem.total * 100).toFixed(0);
|
||||||
this.pinnedUsers += '\n@' + getAcct(user);
|
const memUsed = (stats.mem.used / this.serverInfo.mem.total * 100).toFixed(0);
|
||||||
this.pinnedUsers = this.pinnedUsers.trim();
|
this.memUsage = stats.mem.active;
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
chooseProxyAccount() {
|
this.chartCpuMem.data.labels.push('');
|
||||||
this.$root.new(MkUserSelect, {}).$once('selected', user => {
|
this.chartCpuMem.data.datasets[0].data.push(cpu);
|
||||||
this.proxyAccount = user;
|
this.chartCpuMem.data.datasets[1].data.push(memActive);
|
||||||
this.proxyAccountId = user.id;
|
this.chartCpuMem.data.datasets[2].data.push(memUsed);
|
||||||
this.save(true);
|
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('');
|
||||||
save(withDialog = false) {
|
this.chartDisk.data.datasets[0].data.push(stats.fs.r);
|
||||||
this.$root.api('admin/update-meta', {
|
this.chartDisk.data.datasets[1].data.push(stats.fs.w);
|
||||||
name: this.name,
|
if (this.chartCpuMem.data.datasets[0].data.length > 150) {
|
||||||
description: this.description,
|
this.chartCpuMem.data.labels.shift();
|
||||||
tosUrl: this.tosUrl,
|
this.chartCpuMem.data.datasets[0].data.shift();
|
||||||
bannerUrl: this.bannerUrl,
|
this.chartCpuMem.data.datasets[1].data.shift();
|
||||||
iconUrl: this.iconUrl,
|
this.chartCpuMem.data.datasets[2].data.shift();
|
||||||
maintainerName: this.maintainerName,
|
this.chartNet.data.labels.shift();
|
||||||
maintainerEmail: this.maintainerEmail,
|
this.chartNet.data.datasets[0].data.shift();
|
||||||
maxNoteTextLength: this.maxNoteTextLength,
|
this.chartNet.data.datasets[1].data.shift();
|
||||||
disableRegistration: !this.enableRegistration,
|
this.chartDisk.data.labels.shift();
|
||||||
disableLocalTimeline: !this.enableLocalTimeline,
|
this.chartDisk.data.datasets[0].data.shift();
|
||||||
disableGlobalTimeline: !this.enableGlobalTimeline,
|
this.chartDisk.data.datasets[1].data.shift();
|
||||||
enableRecaptcha: this.enableRecaptcha,
|
}
|
||||||
recaptchaSiteKey: this.recaptchaSiteKey,
|
this.chartCpuMem.update();
|
||||||
recaptchaSecretKey: this.recaptchaSecretKey,
|
this.chartNet.update();
|
||||||
proxyAccountId: this.proxyAccountId,
|
this.chartDisk.update();
|
||||||
cacheRemoteFiles: this.cacheRemoteFiles,
|
},
|
||||||
proxyRemoteFiles: this.proxyRemoteFiles,
|
|
||||||
localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
|
onStatsLog(statsLog) {
|
||||||
remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
|
for (const stats of statsLog.reverse()) {
|
||||||
blockedHosts: this.blockedHosts.split('\n') || [],
|
this.onStats(stats);
|
||||||
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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mk-instance-page {
|
.xhexznfu {
|
||||||
> .info {
|
> .stats {
|
||||||
> .table {
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: calc(0px - var(--margin) / 2);
|
||||||
|
margin-bottom: calc(var(--margin) / 2);
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
|
flex: 1 0 213px;
|
||||||
|
margin: calc(var(--margin) / 2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .chart {
|
||||||
|
> ._content {
|
||||||
|
> .table {
|
||||||
|
> .row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
> * {
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .cell {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
> .label {
|
||||||
|
font-size: 80%;
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
> .icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,381 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="mk-instance-monitor">
|
|
||||||
<section class="_card">
|
|
||||||
<div class="_title"><fa :icon="faMicrochip"/> {{ $t('cpuAndMemory') }}</div>
|
|
||||||
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
|
||||||
<canvas ref="cpumem"></canvas>
|
|
||||||
</div>
|
|
||||||
<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="faHdd"/> {{ $t('disk') }}</div>
|
|
||||||
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
|
||||||
<canvas ref="disk"></canvas>
|
|
||||||
</div>
|
|
||||||
<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">
|
|
||||||
<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" 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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
import { faTachometerAlt, faExchangeAlt, faMicrochip, faHdd } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import Chart from 'chart.js';
|
|
||||||
import i18n from '../../i18n';
|
|
||||||
|
|
||||||
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,
|
|
||||||
|
|
||||||
metaInfo() {
|
|
||||||
return {
|
|
||||||
title: `${this.$t('monitor')} | ${this.$t('instance')}`
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
connection: null,
|
|
||||||
serverInfo: null,
|
|
||||||
memUsage: 0,
|
|
||||||
chartCpuMem: null,
|
|
||||||
chartNet: null,
|
|
||||||
faTachometerAlt, faExchangeAlt, faMicrochip, faHdd
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
|
||||||
|
|
||||||
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.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.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',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.connection.off('stats', this.onStats);
|
|
||||||
this.connection.off('statsLog', this.onStatsLog);
|
|
||||||
this.connection.dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
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;
|
|
||||||
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
|
|
||||||
onStatsLog(statsLog) {
|
|
||||||
for (const stats of statsLog.reverse()) {
|
|
||||||
this.onStats(stats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.mk-instance-monitor {
|
|
||||||
> section {
|
|
||||||
> ._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;
|
|
||||||
|
|
||||||
> .icon {
|
|
||||||
margin-right: 4px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,413 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="meta" class="yihovjtf">
|
||||||
|
<portal to="icon"><fa :icon="faCog"/></portal>
|
||||||
|
<portal to="title">{{ $t('settings') }}</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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<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 { version, url } from '../../config';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import getAcct from '../../../misc/acct/render';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n,
|
||||||
|
|
||||||
|
metaInfo() {
|
||||||
|
return {
|
||||||
|
title: this.$t('instance') as string
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
MkButton,
|
||||||
|
MkInput,
|
||||||
|
MkTextarea,
|
||||||
|
MkSwitch,
|
||||||
|
MkInfo,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
version,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
meta() {
|
||||||
|
return this.$store.state.instance.meta;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (this.proxyAccountId) {
|
||||||
|
this.$root.api('users/show', { userId: this.proxyAccountId }).then(proxyAccount => {
|
||||||
|
this.proxyAccount = proxyAccount;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$root.api('admin/server-info').then(res => {
|
||||||
|
this.serverInfo = res;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$root.api('stats').then(res => {
|
||||||
|
this.stats = res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
};
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
addPinUser() {
|
||||||
|
this.$root.new(MkUserSelect, {}).$once('selected', user => {
|
||||||
|
this.pinnedUsers = this.pinnedUsers.trim();
|
||||||
|
this.pinnedUsers += '\n@' + getAcct(user);
|
||||||
|
this.pinnedUsers = this.pinnedUsers.trim();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseProxyAccount() {
|
||||||
|
this.$root.new(MkUserSelect, {}).$once('selected', user => {
|
||||||
|
this.proxyAccount = user;
|
||||||
|
this.proxyAccountId = user.id;
|
||||||
|
this.save(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.yihovjtf {
|
||||||
|
> .info {
|
||||||
|
> .table {
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -49,9 +49,8 @@ export const router = new VueRouter({
|
||||||
{ path: '/instance/emojis', component: page('instance/emojis') },
|
{ path: '/instance/emojis', component: page('instance/emojis') },
|
||||||
{ path: '/instance/users', component: page('instance/users') },
|
{ path: '/instance/users', component: page('instance/users') },
|
||||||
{ path: '/instance/files', component: page('instance/files') },
|
{ path: '/instance/files', component: page('instance/files') },
|
||||||
{ path: '/instance/monitor', component: page('instance/monitor') },
|
|
||||||
{ path: '/instance/queue', component: page('instance/queue') },
|
{ path: '/instance/queue', component: page('instance/queue') },
|
||||||
{ path: '/instance/stats', component: page('instance/stats') },
|
{ path: '/instance/settings', component: page('instance/settings') },
|
||||||
{ path: '/instance/federation', component: page('instance/federation') },
|
{ path: '/instance/federation', component: page('instance/federation') },
|
||||||
{ path: '/instance/announcements', component: page('instance/announcements') },
|
{ path: '/instance/announcements', component: page('instance/announcements') },
|
||||||
{ path: '/notes/:note', name: 'note', component: page('note') },
|
{ path: '/notes/:note', name: 'note', component: page('note') },
|
||||||
|
|
Loading…
Reference in New Issue