This commit is contained in:
syuilo 2019-06-18 16:49:58 +09:00
parent b0280355e8
commit 4f284e1bc0
No known key found for this signature in database
GPG key ID: BDC4C49D06AB9D69
4 changed files with 98 additions and 13 deletions

View file

@ -1226,8 +1226,12 @@ admin/views/index.vue:
abuse: "スパム報告" abuse: "スパム報告"
queue: "ジョブキュー" queue: "ジョブキュー"
logs: "ログ" logs: "ログ"
db: "データベース"
back-to-misskey: "Misskeyに戻る" back-to-misskey: "Misskeyに戻る"
admin/views/db.vue:
tables: "テーブル"
admin/views/dashboard.vue: admin/views/dashboard.vue:
dashboard: "ダッシュボード" dashboard: "ダッシュボード"
accounts: "アカウント" accounts: "アカウント"

View file

@ -0,0 +1,39 @@
<template>
<div>
<ui-card>
<template #title><fa :icon="faDatabase"/> {{ $t('tables') }}</template>
<section v-if="tables">
<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count }} {{ tables[table].size | bytes }}</div>
</section>
</ui-card>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/db.vue'),
data() {
return {
tables: null,
faDatabase
};
},
mounted() {
this.fetch();
},
methods: {
fetch() {
this.$root.api('admin/get-table-stats').then(tables => {
this.tables = tables;
});
},
}
});
</script>

View file

@ -22,6 +22,7 @@
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li> <li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li> <li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li> <li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li> <li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li> <li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li> <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
@ -43,6 +44,7 @@
<div v-if="page == 'instance'"><x-instance/></div> <div v-if="page == 'instance'"><x-instance/></div>
<div v-if="page == 'queue'"><x-queue/></div> <div v-if="page == 'queue'"><x-queue/></div>
<div v-if="page == 'logs'"><x-logs/></div> <div v-if="page == 'logs'"><x-logs/></div>
<div v-if="page == 'db'"><x-db/></div>
<div v-if="page == 'moderators'"><x-moderators/></div> <div v-if="page == 'moderators'"><x-moderators/></div>
<div v-if="page == 'users'"><x-users/></div> <div v-if="page == 'users'"><x-users/></div>
<div v-if="page == 'emoji'"><x-emoji/></div> <div v-if="page == 'emoji'"><x-emoji/></div>
@ -59,19 +61,20 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { version } from '../../config'; import { version } from '../../config';
import XDashboard from "./dashboard.vue"; import XDashboard from './dashboard.vue';
import XInstance from "./instance.vue"; import XInstance from './instance.vue';
import XQueue from "./queue.vue"; import XQueue from './queue.vue';
import XLogs from "./logs.vue"; import XLogs from './logs.vue';
import XModerators from "./moderators.vue"; import XDb from './db.vue';
import XEmoji from "./emoji.vue"; import XModerators from './moderators.vue';
import XAnnouncements from "./announcements.vue"; import XEmoji from './emoji.vue';
import XUsers from "./users.vue"; import XAnnouncements from './announcements.vue';
import XDrive from "./drive.vue"; import XUsers from './users.vue';
import XAbuse from "./abuse.vue"; import XDrive from './drive.vue';
import XFederation from "./federation.vue"; import XAbuse from './abuse.vue';
import XFederation from './federation.vue';
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream } from '@fortawesome/free-solid-svg-icons'; import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream, faDatabase } from '@fortawesome/free-solid-svg-icons';
import { faGrin } from '@fortawesome/free-regular-svg-icons'; import { faGrin } from '@fortawesome/free-regular-svg-icons';
// Detect the user agent // Detect the user agent
@ -85,6 +88,7 @@ export default Vue.extend({
XInstance, XInstance,
XQueue, XQueue,
XLogs, XLogs,
XDb,
XModerators, XModerators,
XEmoji, XEmoji,
XAnnouncements, XAnnouncements,
@ -108,7 +112,8 @@ export default Vue.extend({
faGlobe, faGlobe,
faExclamationCircle, faExclamationCircle,
faTasks, faTasks,
faStream faStream,
faDatabase,
}; };
}, },
methods: { methods: {

View file

@ -0,0 +1,37 @@
import define from '../../define';
import { getConnection } from 'typeorm';
export const meta = {
requireCredential: false,
desc: {
'en-US': 'Get table stats'
},
tags: ['meta'],
params: {
},
};
export default define(meta, async () => {
const sizes = await
getConnection().query(`
SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size"
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind <> 'i'
AND nspname !~ '^pg_toast';`)
.then(recs => {
const res = {} as Record<string, { count: number; size: number; }>;
for (const rec of recs) {
res[rec.table] = {
count: parseInt(rec.count, 10),
size: parseInt(rec.size, 10),
};
}
return res;
});
return sizes;
});