diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b628b91ab0..f353654d17 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -403,6 +403,12 @@ regenerate: "再生成"
 fontSize: "フォントサイズ"
 noFollowRequests: "フォロー申請はありません"
 openImageInNewTab: "画像を新しいタブで開く"
+dashboard: "ダッシュボード"
+local: "ローカル"
+remote: "リモート"
+total: "合計"
+weekOverWeekChanges: "前週比"
+dayOverDayChanges: "前日比"
 
 _ago:
   unknown: "謎"
diff --git a/src/client/app.vue b/src/client/app.vue
index 1bfcd9e158..9a984a27ff 100644
--- a/src/client/app.vue
+++ b/src/client/app.vue
@@ -413,9 +413,14 @@ export default Vue.extend({
 			this.$root.menu({
 				items: [{
 					type: 'link',
-					text: this.$t('statistics'),
-					to: '/instance/stats',
-					icon: faChartBar,
+					text: this.$t('dashboard'),
+					to: '/instance',
+					icon: faTachometerAlt,
+				}, null, {
+					type: 'link',
+					text: this.$t('settings'),
+					to: '/instance/settings',
+					icon: faCog,
 				}, {
 					type: 'link',
 					text: this.$t('customEmojis'),
@@ -431,11 +436,6 @@ export default Vue.extend({
 					text: this.$t('files'),
 					to: '/instance/files',
 					icon: faCloud,
-				}, {
-					type: 'link',
-					text: this.$t('monitor'),
-					to: '/instance/monitor',
-					icon: faTachometerAlt,
 				}, {
 					type: 'link',
 					text: this.$t('jobQueue'),
@@ -451,11 +451,6 @@ export default Vue.extend({
 					text: this.$t('announcements'),
 					to: '/instance/announcements',
 					icon: faBroadcastTower,
-				}, null, {
-					type: 'link',
-					text: this.$t('general'),
-					to: '/instance',
-					icon: faCog,
 				}],
 				align: 'left',
 				fixed: true,
diff --git a/src/client/pages/instance/stats.vue b/src/client/components/instance-stats.vue
similarity index 72%
rename from src/client/pages/instance/stats.vue
rename to src/client/components/instance-stats.vue
index 4883d8c873..a2625f4ab6 100644
--- a/src/client/pages/instance/stats.vue
+++ b/src/client/components/instance-stats.vue
@@ -1,8 +1,91 @@
 <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">
 		<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;">
 				<mk-select v-model="chartSrc" style="margin: 0; flex: 1;">
 					<optgroup :label="$t('federation')">
@@ -40,10 +123,10 @@
 
 <script lang="ts">
 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 i18n from '../../i18n';
-import MkSelect from '../../components/ui/select.vue';
+import i18n from '../i18n';
+import MkSelect from './ui/select.vue';
 
 const chartLimit = 90;
 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({
 	i18n,
 
-	metaInfo() {
-		return {
-			title: `${this.$t('statistics')} | ${this.$t('instance')}`
-		};
-	},
-
 	components: {
 		MkSelect
 	},
 
 	data() {
 		return {
+			info: null,
+			notesLocalWoW: 0,
+			notesLocalDoD: 0,
+			notesRemoteWoW: 0,
+			notesRemoteDoD: 0,
+			usersLocalWoW: 0,
+			usersLocalDoD: 0,
+			usersRemoteWoW: 0,
+			usersRemoteDoD: 0,
 			now: null,
 			chart: null,
 			chartInstance: null,
 			chartSrc: 'notes',
 			chartSpan: 'hour',
-			faChartBar
+			faChartBar, faUser, faPencilAlt
 		}
 	},
 
@@ -121,6 +207,8 @@ export default Vue.extend({
 	},
 
 	async created() {
+		this.info = await this.$root.api('stats');
+
 		this.now = new Date();
 
 		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.renderChart();
@@ -489,3 +586,80 @@ export default Vue.extend({
 	}
 });
 </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>
diff --git a/src/client/pages/about.vue b/src/client/pages/about.vue
index e39600b487..a3a4b6ac73 100644
--- a/src/client/pages/about.vue
+++ b/src/client/pages/about.vue
@@ -12,14 +12,12 @@
 			<div><b>{{ $t('administrator') }}</b><span>{{ meta.maintainerName }}</span></div>
 			<div><b></b><span>{{ meta.maintainerEmail }}</span></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><b>Misskey</b><span>v{{ version }}</span></div>
 		</div>
 	</section>
+
+	<mk-instance-stats style="margin-top: var(--margin);"/>
 </div>
 </template>
 
@@ -28,6 +26,7 @@ import Vue from 'vue';
 import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
 import { version } from '../config';
 import i18n from '../i18n';
+import MkInstanceStats from '../components/instance-stats.vue';
 
 export default Vue.extend({
 	i18n,
@@ -38,10 +37,13 @@ export default Vue.extend({
 		};
 	},
 
+	components: {
+		MkInstanceStats
+	},
+
 	data() {
 		return {
 			version,
-			stats: null,
 			serverInfo: null,
 			faInfoCircle
 		}
@@ -52,12 +54,6 @@ export default Vue.extend({
 			return this.$store.state.instance.meta;
 		},
 	},
-
-	created() {
-		this.$root.api('stats').then(res => {
-			this.stats = res;
-		});
-	},
 });
 </script>
 
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>
+	<mk-instance-stats style="margin-bottom: var(--margin);"/>
+
+	<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>
-		<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 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>
+	<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">
-			<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 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="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>
+	<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>
-	</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 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.$root.api('admin/server-info').then(res => {
-			this.serverInfo = res;
+		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('stats').then(res => {
-			this.stats = 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.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() {
-		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();
-		});
+	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;
+
+			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();
 		},
 
-		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
-				});
-			});
+		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);
 
-				> * {
-					flex: 1;
+		> 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;
+
+							> .icon {
+								margin-right: 4px;
+								display: none;
+							}
+						}
+					}
 				}
 			}
 		}
diff --git a/src/client/pages/instance/monitor.vue b/src/client/pages/instance/monitor.vue
deleted file mode 100644
index b75755126b..0000000000
--- a/src/client/pages/instance/monitor.vue
+++ /dev/null
@@ -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>
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
new file mode 100644
index 0000000000..914565298a
--- /dev/null
+++ b/src/client/pages/instance/settings.vue
@@ -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>
diff --git a/src/client/router.ts b/src/client/router.ts
index a226cc9734..fe3de70a05 100644
--- a/src/client/router.ts
+++ b/src/client/router.ts
@@ -49,9 +49,8 @@ export const router = new VueRouter({
 		{ path: '/instance/emojis', component: page('instance/emojis') },
 		{ path: '/instance/users', component: page('instance/users') },
 		{ path: '/instance/files', component: page('instance/files') },
-		{ path: '/instance/monitor', component: page('instance/monitor') },
 		{ 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/announcements', component: page('instance/announcements') },
 		{ path: '/notes/:note', name: 'note', component: page('note') },