Resolve #5063
This commit is contained in:
parent
b0280355e8
commit
4f284e1bc0
4 changed files with 98 additions and 13 deletions
|
@ -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: "アカウント"
|
||||||
|
|
39
src/client/app/admin/views/db.vue
Normal file
39
src/client/app/admin/views/db.vue
Normal 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>
|
|
@ -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: {
|
||||||
|
|
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal 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;
|
||||||
|
});
|
Loading…
Reference in a new issue