Improve admin dashboard
This commit is contained in:
		
							parent
							
								
									f9f2ca51ac
								
							
						
					
					
						commit
						aadd5b95b8
					
				
					 10 changed files with 183 additions and 8 deletions
				
			
		| 
						 | 
					@ -1082,6 +1082,8 @@ admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  accounts: "アカウント"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  notes: "投稿"
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  instances: "インスタンス"
 | 
				
			||||||
  this-instance: "このインスタンス"
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
  federated: "連合"
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										113
									
								
								src/client/app/admin/views/ap-log.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/client/app/admin/views/ap-log.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,113 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="hyhctythnmwihguaaapnbrbszsjqxpio">
 | 
				
			||||||
 | 
						<table>
 | 
				
			||||||
 | 
							<thead>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<th>%fa:exchange-alt% In/Out</th>
 | 
				
			||||||
 | 
									<th>%fa:server% Host</th>
 | 
				
			||||||
 | 
									<th>%fa:bolt% Activity</th>
 | 
				
			||||||
 | 
									<th>%fa:user% Actor</th>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</thead>
 | 
				
			||||||
 | 
							<tbody>
 | 
				
			||||||
 | 
								<tr v-for="log in logs" :key="log.id">
 | 
				
			||||||
 | 
									<td :class="log.direction">{{ log.direction == 'in' ? '<' : '>' }} {{ log.direction }}</td>
 | 
				
			||||||
 | 
									<td>{{ log.host }}</td>
 | 
				
			||||||
 | 
									<td>{{ log.activity }}</td>
 | 
				
			||||||
 | 
									<td>@{{ log.actor }}</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</tbody>
 | 
				
			||||||
 | 
						</table>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								logs: [],
 | 
				
			||||||
 | 
								connection: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							this.connection = (this as any).os.stream.useSharedConnection('apLog');
 | 
				
			||||||
 | 
							this.connection.on('stats', this.onLog);
 | 
				
			||||||
 | 
							this.connection.on('statsLog', this.onLogs);
 | 
				
			||||||
 | 
							this.connection.send('requestLog', {
 | 
				
			||||||
 | 
								id: Math.random().toString().substr(2, 8),
 | 
				
			||||||
 | 
								length: 50
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							setInterval(() => {
 | 
				
			||||||
 | 
								this.onLog({
 | 
				
			||||||
 | 
									direction: ['in', 'out'][Math.floor(Math.random() * 2)],
 | 
				
			||||||
 | 
									activity: 'Create',
 | 
				
			||||||
 | 
									host: 'misskey.ai',
 | 
				
			||||||
 | 
									actor: 'foobar'
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}, 1000);
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						beforeDestroy() {
 | 
				
			||||||
 | 
							this.connection.dispose();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onLog(log) {
 | 
				
			||||||
 | 
								log.id = Math.random();
 | 
				
			||||||
 | 
								this.logs.unshift(log);
 | 
				
			||||||
 | 
								if (this.logs.length > 50) this.logs.pop();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onLogs(logs) {
 | 
				
			||||||
 | 
								logs.reverse().forEach(log => this.onLog(log));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.hyhctythnmwihguaaapnbrbszsjqxpio
 | 
				
			||||||
 | 
						display block
 | 
				
			||||||
 | 
						padding 16px
 | 
				
			||||||
 | 
						height 250px
 | 
				
			||||||
 | 
						overflow auto
 | 
				
			||||||
 | 
						box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
						background var(--face)
 | 
				
			||||||
 | 
						border-radius 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> table
 | 
				
			||||||
 | 
							width 100%
 | 
				
			||||||
 | 
							max-width 100%
 | 
				
			||||||
 | 
							overflow auto
 | 
				
			||||||
 | 
							border-spacing 0
 | 
				
			||||||
 | 
							border-collapse collapse
 | 
				
			||||||
 | 
							color #555
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							thead
 | 
				
			||||||
 | 
								font-weight bold
 | 
				
			||||||
 | 
								border-bottom solid 2px #eee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								tr
 | 
				
			||||||
 | 
									th
 | 
				
			||||||
 | 
										text-align left
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tbody
 | 
				
			||||||
 | 
								tr
 | 
				
			||||||
 | 
									&:nth-child(odd)
 | 
				
			||||||
 | 
										background #fbfbfb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							th, td
 | 
				
			||||||
 | 
								padding 8px 16px
 | 
				
			||||||
 | 
								min-width 128px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							td.in
 | 
				
			||||||
 | 
								color #d26755
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							td.out
 | 
				
			||||||
 | 
								color #55bb83
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
		<p><b>Node</b><span>{{ meta.node }}</span></p>
 | 
							<p><b>Node</b><span>{{ meta.node }}</span></p>
 | 
				
			||||||
		<p>藍ちゃかわいい</p>
 | 
							<p>藍ちゃかわいい</p>
 | 
				
			||||||
	</header>
 | 
						</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div v-if="stats" class="stats">
 | 
						<div v-if="stats" class="stats">
 | 
				
			||||||
		<div>
 | 
							<div>
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
| 
						 | 
					@ -34,22 +35,22 @@
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div>
 | 
							<div>
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
				<div>%fa:user%</div>
 | 
									<div>%fa:database%</div>
 | 
				
			||||||
				<div>
 | 
									<div>
 | 
				
			||||||
					<span>%i18n:@accounts%</span>
 | 
										<span>%i18n:@drive%</span>
 | 
				
			||||||
					<b>{{ stats.usersCount | number }}</b>
 | 
										<b>{{ stats.driveUsageLocal | bytes }}</b>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
				<span>%fa:globe% %i18n:@federated%</span>
 | 
									<span>%fa:home% %i18n:@this-instance%</span>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div>
 | 
							<div>
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
				<div>%fa:pencil-alt%</div>
 | 
									<div>%fa:hdd R%</div>
 | 
				
			||||||
				<div>
 | 
									<div>
 | 
				
			||||||
					<span>%i18n:@notes%</span>
 | 
										<span>%i18n:@instances%</span>
 | 
				
			||||||
					<b>{{ stats.notesCount | number }}</b>
 | 
										<b>{{ stats.instances | number }}</b>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
| 
						 | 
					@ -65,6 +66,10 @@
 | 
				
			||||||
	<div class="cpu-memory">
 | 
						<div class="cpu-memory">
 | 
				
			||||||
		<x-cpu-memory :connection="connection"/>
 | 
							<x-cpu-memory :connection="connection"/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class="ap">
 | 
				
			||||||
 | 
							<x-ap-log/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,11 +77,13 @@
 | 
				
			||||||
import Vue from "vue";
 | 
					import Vue from "vue";
 | 
				
			||||||
import XCpuMemory from "./cpu-memory.vue";
 | 
					import XCpuMemory from "./cpu-memory.vue";
 | 
				
			||||||
import XCharts from "./charts.vue";
 | 
					import XCharts from "./charts.vue";
 | 
				
			||||||
 | 
					import XApLog from "./ap-log.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XCpuMemory,
 | 
							XCpuMemory,
 | 
				
			||||||
		XCharts
 | 
							XCharts,
 | 
				
			||||||
 | 
							XApLog
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.filter('bytes', (v, digits = 0) => {
 | 
					Vue.filter('bytes', (v, digits = 0) => {
 | 
				
			||||||
 | 
						if (v == null) return '?';
 | 
				
			||||||
	const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
 | 
						const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
 | 
				
			||||||
	if (v == 0) return '0';
 | 
						if (v == 0) return '0';
 | 
				
			||||||
	const isMinus = v < 0;
 | 
						const isMinus = v < 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.filter('number', (n) => {
 | 
					Vue.filter('number', (n) => {
 | 
				
			||||||
 | 
						if (n == null) return 'N/A';
 | 
				
			||||||
	return n.toLocaleString();
 | 
						return n.toLocaleString();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import perform from '../../../remote/activitypub/perform';
 | 
				
			||||||
import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person';
 | 
					import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person';
 | 
				
			||||||
import { toUnicode } from 'punycode';
 | 
					import { toUnicode } from 'punycode';
 | 
				
			||||||
import { URL } from 'url';
 | 
					import { URL } from 'url';
 | 
				
			||||||
 | 
					import { publishApLogStream } from '../../../stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = debug('misskey:queue:inbox');
 | 
					const log = debug('misskey:queue:inbox');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,6 +62,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
 | 
				
			||||||
		}) as IRemoteUser;
 | 
							}) as IRemoteUser;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#region Log
 | 
				
			||||||
 | 
						publishApLogStream({
 | 
				
			||||||
 | 
							direction: 'in',
 | 
				
			||||||
 | 
							activity: activity.type,
 | 
				
			||||||
 | 
							host: user.host,
 | 
				
			||||||
 | 
							actor: user.username
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update activityの場合は、ここで署名検証/更新処理まで実施して終了
 | 
						// Update activityの場合は、ここで署名検証/更新処理まで実施して終了
 | 
				
			||||||
	if (activity.type === 'Update') {
 | 
						if (activity.type === 'Update') {
 | 
				
			||||||
		if (activity.object && activity.object.type === 'Person') {
 | 
							if (activity.object && activity.object.type === 'Person') {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ const crypto = require('crypto');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import { ILocalUser } from '../../models/user';
 | 
					import { ILocalUser } from '../../models/user';
 | 
				
			||||||
 | 
					import { publishApLogStream } from '../../stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = debug('misskey:activitypub:deliver');
 | 
					const log = debug('misskey:activitypub:deliver');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,4 +65,13 @@ export default (user: ILocalUser, url: string, object: any) => new Promise((reso
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req.end(data);
 | 
						req.end(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#region Log
 | 
				
			||||||
 | 
						publishApLogStream({
 | 
				
			||||||
 | 
							direction: 'out',
 | 
				
			||||||
 | 
							activity: object.type,
 | 
				
			||||||
 | 
							host: null,
 | 
				
			||||||
 | 
							actor: user.username
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						//#endregion
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										24
									
								
								src/server/api/stream/channels/ap-log.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/server/api/stream/channels/ap-log.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					import autobind from 'autobind-decorator';
 | 
				
			||||||
 | 
					import Channel from '../channel';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class extends Channel {
 | 
				
			||||||
 | 
						public readonly chName = 'apLog';
 | 
				
			||||||
 | 
						public static shouldShare = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@autobind
 | 
				
			||||||
 | 
						public async init(params: any) {
 | 
				
			||||||
 | 
							// Subscribe events
 | 
				
			||||||
 | 
							this.subscriber.on('apLog', this.onLog);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@autobind
 | 
				
			||||||
 | 
						private async onLog(log: any) {
 | 
				
			||||||
 | 
							this.send('log', log);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@autobind
 | 
				
			||||||
 | 
						public dispose() {
 | 
				
			||||||
 | 
							// Unsubscribe events
 | 
				
			||||||
 | 
							this.subscriber.off('apLog', this.onLog);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import messaging from './messaging';
 | 
				
			||||||
import messagingIndex from './messaging-index';
 | 
					import messagingIndex from './messaging-index';
 | 
				
			||||||
import drive from './drive';
 | 
					import drive from './drive';
 | 
				
			||||||
import hashtag from './hashtag';
 | 
					import hashtag from './hashtag';
 | 
				
			||||||
 | 
					import apLog from './ap-log';
 | 
				
			||||||
import gamesReversi from './games/reversi';
 | 
					import gamesReversi from './games/reversi';
 | 
				
			||||||
import gamesReversiGame from './games/reversi-game';
 | 
					import gamesReversiGame from './games/reversi-game';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,7 @@ export default {
 | 
				
			||||||
	messagingIndex,
 | 
						messagingIndex,
 | 
				
			||||||
	drive,
 | 
						drive,
 | 
				
			||||||
	hashtag,
 | 
						hashtag,
 | 
				
			||||||
 | 
						apLog,
 | 
				
			||||||
	gamesReversi,
 | 
						gamesReversi,
 | 
				
			||||||
	gamesReversiGame
 | 
						gamesReversiGame
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,6 +100,10 @@ class Publisher {
 | 
				
			||||||
	public publishHashtagStream = (note: any): void => {
 | 
						public publishHashtagStream = (note: any): void => {
 | 
				
			||||||
		this.publish('hashtag', null, note);
 | 
							this.publish('hashtag', null, note);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public publishApLogStream = (log: any): void => {
 | 
				
			||||||
 | 
							this.publish('apLog', null, log);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const publisher = new Publisher();
 | 
					const publisher = new Publisher();
 | 
				
			||||||
| 
						 | 
					@ -119,3 +123,4 @@ export const publishLocalTimelineStream = publisher.publishLocalTimelineStream;
 | 
				
			||||||
export const publishHybridTimelineStream = publisher.publishHybridTimelineStream;
 | 
					export const publishHybridTimelineStream = publisher.publishHybridTimelineStream;
 | 
				
			||||||
export const publishGlobalTimelineStream = publisher.publishGlobalTimelineStream;
 | 
					export const publishGlobalTimelineStream = publisher.publishGlobalTimelineStream;
 | 
				
			||||||
export const publishHashtagStream = publisher.publishHashtagStream;
 | 
					export const publishHashtagStream = publisher.publishHashtagStream;
 | 
				
			||||||
 | 
					export const publishApLogStream = publisher.publishApLogStream;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue