Resolve #2963
This commit is contained in:
		
							parent
							
								
									aa50d0ee11
								
							
						
					
					
						commit
						969b6dbcad
					
				
					 14 changed files with 584 additions and 420 deletions
				
			
		| 
						 | 
				
			
			@ -112,12 +112,42 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	created() {
 | 
			
		||||
		(this as any).api('chart', {
 | 
			
		||||
			limit: 35
 | 
			
		||||
		}).then(chart => {
 | 
			
		||||
			this.chart = chart;
 | 
			
		||||
		});
 | 
			
		||||
	async created() {
 | 
			
		||||
		const limit = 35;
 | 
			
		||||
 | 
			
		||||
		const [perHour, perDay] = await Promise.all([Promise.all([
 | 
			
		||||
			(this as any).api('charts/users', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/notes', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/drive', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/network', { limit: limit, span: 'hour' })
 | 
			
		||||
		]), Promise.all([
 | 
			
		||||
			(this as any).api('charts/users', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/notes', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/drive', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/network', { limit: limit, span: 'day' })
 | 
			
		||||
		])]);
 | 
			
		||||
 | 
			
		||||
		const chart = {
 | 
			
		||||
			perHour: [],
 | 
			
		||||
			perDay: []
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		for (let i = 0; i < limit; i++) {
 | 
			
		||||
			chart.perHour.push({
 | 
			
		||||
				users: perHour[0][i],
 | 
			
		||||
				notes: perHour[1][i],
 | 
			
		||||
				drive: perHour[2][i],
 | 
			
		||||
				network: perHour[3][i]
 | 
			
		||||
			});
 | 
			
		||||
			chart.perDay.push({
 | 
			
		||||
				users: perDay[0][i],
 | 
			
		||||
				notes: perDay[1][i],
 | 
			
		||||
				drive: perDay[2][i],
 | 
			
		||||
				network: perDay[3][i]
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.chart = chart;
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			@ -586,7 +616,7 @@ export default Vue.extend({
 | 
			
		|||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: data.map(x => ({ t: x.date, y: x.incomingRequests }))
 | 
			
		||||
					data: data.map(x => ({ t: x.date, y: x.incoming }))
 | 
			
		||||
				}]
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -594,7 +624,7 @@ export default Vue.extend({
 | 
			
		|||
		networkTimeChart(): any {
 | 
			
		||||
			const data = this.stats.slice().reverse().map(x => ({
 | 
			
		||||
				date: new Date(x.date),
 | 
			
		||||
				time: x.network.requests != 0 ? (x.network.totalTime / x.network.requests) : 0,
 | 
			
		||||
				time: x.network.incomingRequests != 0 ? (x.network.totalTime / x.network.incomingRequests) : 0,
 | 
			
		||||
			}));
 | 
			
		||||
 | 
			
		||||
			return [{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type'
 | 
			
		|||
import { IDriveFile } from '../../../models/drive-file';
 | 
			
		||||
import Meta from '../../../models/meta';
 | 
			
		||||
import htmlToMFM from '../../../mfm/html-to-mfm';
 | 
			
		||||
import { coreChart } from '../../../services/stats';
 | 
			
		||||
import { usersChart } from '../../../services/stats';
 | 
			
		||||
import { URL } from 'url';
 | 
			
		||||
import { resolveNote } from './note';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +180,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
 | 
			
		|||
		}
 | 
			
		||||
	}, { upsert: true });
 | 
			
		||||
 | 
			
		||||
	coreChart.updateUserStats(user, true);
 | 
			
		||||
	usersChart.update(user, true);
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	//#region アイコンとヘッダー画像をフェッチ
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
import $ from 'cafy';
 | 
			
		||||
import getParams from '../get-params';
 | 
			
		||||
import { coreChart } from '../../../services/stats';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': 'インスタンスの統計を取得します。'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		limit: $.num.optional.range(1, 100).note({
 | 
			
		||||
			default: 30,
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '最大数'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) throw psErr;
 | 
			
		||||
 | 
			
		||||
	const [statsPerDay, statsPerHour] = await Promise.all([
 | 
			
		||||
		coreChart.getStats('day', ps.limit),
 | 
			
		||||
		coreChart.getStats('hour', ps.limit)
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	res({
 | 
			
		||||
		perDay: statsPerDay,
 | 
			
		||||
		perHour: statsPerHour
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										33
									
								
								src/server/api/endpoints/charts/drive.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/server/api/endpoints/charts/drive.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import $ from 'cafy';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
import { driveChart } from '../../../../services/stats';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': 'ドライブの統計を取得します。'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		span: $.str.or(['day', 'hour']).note({
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '集計のスパン'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
 | 
			
		||||
		limit: $.num.optional.range(1, 100).note({
 | 
			
		||||
			default: 30,
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) throw psErr;
 | 
			
		||||
 | 
			
		||||
	const stats = await driveChart.getStats(ps.span as any, ps.limit);
 | 
			
		||||
 | 
			
		||||
	res(stats);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										33
									
								
								src/server/api/endpoints/charts/network.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/server/api/endpoints/charts/network.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import $ from 'cafy';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
import { networkChart } from '../../../../services/stats';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': 'ネットワークの統計を取得します。'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		span: $.str.or(['day', 'hour']).note({
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '集計のスパン'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
 | 
			
		||||
		limit: $.num.optional.range(1, 100).note({
 | 
			
		||||
			default: 30,
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) throw psErr;
 | 
			
		||||
 | 
			
		||||
	const stats = await networkChart.getStats(ps.span as any, ps.limit);
 | 
			
		||||
 | 
			
		||||
	res(stats);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										33
									
								
								src/server/api/endpoints/charts/notes.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/server/api/endpoints/charts/notes.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import $ from 'cafy';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
import { notesChart } from '../../../../services/stats';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': '投稿の統計を取得します。'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		span: $.str.or(['day', 'hour']).note({
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '集計のスパン'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
 | 
			
		||||
		limit: $.num.optional.range(1, 100).note({
 | 
			
		||||
			default: 30,
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) throw psErr;
 | 
			
		||||
 | 
			
		||||
	const stats = await notesChart.getStats(ps.span as any, ps.limit);
 | 
			
		||||
 | 
			
		||||
	res(stats);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										33
									
								
								src/server/api/endpoints/charts/users.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/server/api/endpoints/charts/users.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import $ from 'cafy';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
import { usersChart } from '../../../../services/stats';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': 'ユーザーの統計を取得します。'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		span: $.str.or(['day', 'hour']).note({
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '集計のスパン'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
 | 
			
		||||
		limit: $.num.optional.range(1, 100).note({
 | 
			
		||||
			default: 30,
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) throw psErr;
 | 
			
		||||
 | 
			
		||||
	const stats = await usersChart.getStats(ps.span as any, ps.limit);
 | 
			
		||||
 | 
			
		||||
	res(stats);
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ import generateUserToken from '../common/generate-native-user-token';
 | 
			
		|||
import config from '../../../config';
 | 
			
		||||
import Meta from '../../../models/meta';
 | 
			
		||||
import RegistrationTicket from '../../../models/registration-tickets';
 | 
			
		||||
import { coreChart } from '../../../services/stats';
 | 
			
		||||
import { usersChart } from '../../../services/stats';
 | 
			
		||||
 | 
			
		||||
if (config.recaptcha) {
 | 
			
		||||
	recaptcha.init({
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ export default async (ctx: Koa.Context) => {
 | 
			
		|||
	}, { upsert: true });
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	coreChart.updateUserStats(account, true);
 | 
			
		||||
	usersChart.update(account, true);
 | 
			
		||||
 | 
			
		||||
	const res = await pack(account, account, {
 | 
			
		||||
		detail: true,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ const requestStats = require('request-stats');
 | 
			
		|||
import activityPub from './activitypub';
 | 
			
		||||
import webFinger from './webfinger';
 | 
			
		||||
import config from '../config';
 | 
			
		||||
import { coreChart } from '../services/stats';
 | 
			
		||||
import { networkChart } from '../services/stats';
 | 
			
		||||
import apiServer from './api';
 | 
			
		||||
 | 
			
		||||
// Init app
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +104,7 @@ export default () => new Promise(resolve => {
 | 
			
		|||
		const outgoingBytes = queue.reduce((a, b) => a + b.res.bytes, 0);
 | 
			
		||||
		queue = [];
 | 
			
		||||
 | 
			
		||||
		coreChart.updateNetworkStats(requests, time, incomingBytes, outgoingBytes);
 | 
			
		||||
		networkChart.update(requests, time, incomingBytes, outgoingBytes);
 | 
			
		||||
	}, 5000);
 | 
			
		||||
	//#endregion
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ import { isLocalUser, IUser, IRemoteUser } from '../../models/user';
 | 
			
		|||
import delFile from './delete-file';
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
 | 
			
		||||
import { coreChart } from '../stats';
 | 
			
		||||
import { driveChart } from '../stats';
 | 
			
		||||
 | 
			
		||||
const log = debug('misskey:drive:add-file');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -389,7 +389,7 @@ export default async function(
 | 
			
		|||
	});
 | 
			
		||||
 | 
			
		||||
	// 統計を更新
 | 
			
		||||
	coreChart.updateDriveStats(driveFile, true);
 | 
			
		||||
	driveChart.update(driveFile, true);
 | 
			
		||||
 | 
			
		||||
	return driveFile;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import * as Minio from 'minio';
 | 
			
		|||
import DriveFile, { DriveFileChunk, IDriveFile } from '../../models/drive-file';
 | 
			
		||||
import DriveFileThumbnail, { DriveFileThumbnailChunk } from '../../models/drive-file-thumbnail';
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
import { coreChart } from '../stats';
 | 
			
		||||
import { driveChart } from '../stats';
 | 
			
		||||
 | 
			
		||||
export default async function(file: IDriveFile, isExpired = false) {
 | 
			
		||||
	if (file.metadata.storage == 'minio') {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,5 +48,5 @@ export default async function(file: IDriveFile, isExpired = false) {
 | 
			
		|||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	// 統計を更新
 | 
			
		||||
	coreChart.updateDriveStats(file, false);
 | 
			
		||||
	driveChart.update(file, false);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ import registerHashtag from '../register-hashtag';
 | 
			
		|||
import isQuote from '../../misc/is-quote';
 | 
			
		||||
import { TextElementMention } from '../../mfm/parse/elements/mention';
 | 
			
		||||
import { TextElementHashtag } from '../../mfm/parse/elements/hashtag';
 | 
			
		||||
import { coreChart } from '../stats';
 | 
			
		||||
import { notesChart } from '../stats';
 | 
			
		||||
import { erase, unique } from '../../prelude/array';
 | 
			
		||||
import insertNoteUnread from './unread';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +165,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// 統計を更新
 | 
			
		||||
	coreChart.updateNoteStats(note, true);
 | 
			
		||||
	notesChart.update(note, true);
 | 
			
		||||
 | 
			
		||||
	// ハッシュタグ登録
 | 
			
		||||
	tags.map(tag => registerHashtag(user, tag));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ import pack from '../../remote/activitypub/renderer';
 | 
			
		|||
import { deliver } from '../../queue';
 | 
			
		||||
import Following from '../../models/following';
 | 
			
		||||
import renderTombstone from '../../remote/activitypub/renderer/tombstone';
 | 
			
		||||
import { coreChart } from '../stats';
 | 
			
		||||
import { notesChart } from '../stats';
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
import NoteUnread from '../../models/note-unread';
 | 
			
		||||
import read from './read';
 | 
			
		||||
| 
						 | 
				
			
			@ -63,5 +63,5 @@ export default async function(user: IUser, note: INote) {
 | 
			
		|||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	// 統計を更新
 | 
			
		||||
	coreChart.updateNoteStats(note, false);
 | 
			
		||||
	notesChart.update(note, false);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,11 @@ type Span = 'day' | 'hour';
 | 
			
		|||
type ChartDocument<T extends Obj> = {
 | 
			
		||||
	_id: mongo.ObjectID;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 集計のグループ
 | 
			
		||||
	 */
 | 
			
		||||
	group?: any;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 集計日時
 | 
			
		||||
	 */
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +45,7 @@ abstract class Chart<T> {
 | 
			
		|||
	constructor(dbCollectionName: string) {
 | 
			
		||||
		this.collection = db.get<ChartDocument<T>>(dbCollectionName);
 | 
			
		||||
		this.collection.createIndex({ span: -1, date: -1 }, { unique: true });
 | 
			
		||||
		this.collection.createIndex('group');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected async getCurrentStats(span: Span, group?: Obj): Promise<ChartDocument<T>> {
 | 
			
		||||
| 
						 | 
				
			
			@ -55,10 +61,11 @@ abstract class Chart<T> {
 | 
			
		|||
			null;
 | 
			
		||||
 | 
			
		||||
		// 現在(今日または今のHour)の統計
 | 
			
		||||
		const currentStats = await this.collection.findOne(Object.assign({}, {
 | 
			
		||||
		const currentStats = await this.collection.findOne({
 | 
			
		||||
			group: group,
 | 
			
		||||
			span: span,
 | 
			
		||||
			date: current
 | 
			
		||||
		}, group));
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		if (currentStats) {
 | 
			
		||||
			return currentStats;
 | 
			
		||||
| 
						 | 
				
			
			@ -69,9 +76,10 @@ abstract class Chart<T> {
 | 
			
		|||
			// * 昨日何もチャートを更新するような出来事がなかった場合は、
 | 
			
		||||
			// * 統計がそもそも作られずドキュメントが存在しないということがあり得るため、
 | 
			
		||||
			// * 「昨日の」と決め打ちせずに「もっとも最近の」とします
 | 
			
		||||
			const mostRecentStats = await this.collection.findOne(Object.assign({}, {
 | 
			
		||||
			const mostRecentStats = await this.collection.findOne({
 | 
			
		||||
				group: group,
 | 
			
		||||
				span: span
 | 
			
		||||
			}, group), {
 | 
			
		||||
			}, {
 | 
			
		||||
				sort: {
 | 
			
		||||
					date: -1
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -81,11 +89,12 @@ abstract class Chart<T> {
 | 
			
		|||
				// 現在の統計を初期挿入
 | 
			
		||||
				const data = this.generateEmptyStats(mostRecentStats.data);
 | 
			
		||||
 | 
			
		||||
				const stats = await this.collection.insert(Object.assign({}, {
 | 
			
		||||
				const stats = await this.collection.insert({
 | 
			
		||||
					group: group,
 | 
			
		||||
					span: span,
 | 
			
		||||
					date: current,
 | 
			
		||||
					data: data
 | 
			
		||||
				}, group));
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				return stats;
 | 
			
		||||
			} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,18 +104,19 @@ abstract class Chart<T> {
 | 
			
		|||
				// 空の統計を作成
 | 
			
		||||
				const data = this.generateInitialStats();
 | 
			
		||||
 | 
			
		||||
				const stats = await this.collection.insert(Object.assign({}, {
 | 
			
		||||
				const stats = await this.collection.insert({
 | 
			
		||||
					group: group,
 | 
			
		||||
					span: span,
 | 
			
		||||
					date: current,
 | 
			
		||||
					data: data
 | 
			
		||||
				}, group));
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				return stats;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected update(inc: Partial<T>, group?: Obj): void {
 | 
			
		||||
	protected inc(inc: Partial<T>, group?: Obj): void {
 | 
			
		||||
		const query: Obj = {};
 | 
			
		||||
 | 
			
		||||
		const dive = (path: string, x: Obj) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -151,12 +161,13 @@ abstract class Chart<T> {
 | 
			
		|||
			span == 'day' ? new Date(y, m, d - range) :
 | 
			
		||||
			span == 'hour' ? new Date(y, m, d, h - range) : null;
 | 
			
		||||
 | 
			
		||||
		const stats = await this.collection.find(Object.assign({
 | 
			
		||||
		const stats = await this.collection.find({
 | 
			
		||||
			group: group,
 | 
			
		||||
			span: span,
 | 
			
		||||
			date: {
 | 
			
		||||
				$gt: gt
 | 
			
		||||
			}
 | 
			
		||||
		}, group), {
 | 
			
		||||
		}, {
 | 
			
		||||
			sort: {
 | 
			
		||||
				date: -1
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			@ -189,356 +200,82 @@ abstract class Chart<T> {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CoreStats = {
 | 
			
		||||
	/**
 | 
			
		||||
	 * ユーザーに関する統計
 | 
			
		||||
	 */
 | 
			
		||||
	users: {
 | 
			
		||||
		local: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全ユーザー数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			total: number;
 | 
			
		||||
//#region Users stats
 | 
			
		||||
/**
 | 
			
		||||
 * ユーザーに関する統計
 | 
			
		||||
 */
 | 
			
		||||
type UsersStats = {
 | 
			
		||||
	local: {
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全ユーザー数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		total: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加したユーザー数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			inc: number;
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加したユーザー数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		inc: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少したユーザー数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			dec: number;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		remote: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全ユーザー数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			total: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加したユーザー数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			inc: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少したユーザー数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			dec: number;
 | 
			
		||||
		};
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少したユーザー数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		dec: number;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 投稿に関する統計
 | 
			
		||||
	 */
 | 
			
		||||
	notes: {
 | 
			
		||||
		local: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全投稿数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			total: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加した投稿数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			inc: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少した投稿数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			dec: number;
 | 
			
		||||
 | 
			
		||||
			diffs: {
 | 
			
		||||
				/**
 | 
			
		||||
				 * 通常の投稿数の差分 (ローカル)
 | 
			
		||||
				 */
 | 
			
		||||
				normal: number;
 | 
			
		||||
 | 
			
		||||
				/**
 | 
			
		||||
				 * リプライの投稿数の差分 (ローカル)
 | 
			
		||||
				 */
 | 
			
		||||
				reply: number;
 | 
			
		||||
 | 
			
		||||
				/**
 | 
			
		||||
				 * Renoteの投稿数の差分 (ローカル)
 | 
			
		||||
				 */
 | 
			
		||||
				renote: number;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		remote: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全投稿数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			total: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加した投稿数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			inc: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少した投稿数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			dec: number;
 | 
			
		||||
 | 
			
		||||
			diffs: {
 | 
			
		||||
				/**
 | 
			
		||||
				 * 通常の投稿数の差分 (リモート)
 | 
			
		||||
				 */
 | 
			
		||||
				normal: number;
 | 
			
		||||
 | 
			
		||||
				/**
 | 
			
		||||
				 * リプライの投稿数の差分 (リモート)
 | 
			
		||||
				 */
 | 
			
		||||
				reply: number;
 | 
			
		||||
 | 
			
		||||
				/**
 | 
			
		||||
				 * Renoteの投稿数の差分 (リモート)
 | 
			
		||||
				 */
 | 
			
		||||
				renote: number;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * ドライブ(のファイル)に関する統計
 | 
			
		||||
	 */
 | 
			
		||||
	drive: {
 | 
			
		||||
		local: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全ドライブファイル数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			totalCount: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全ドライブファイルの合計サイズ (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			totalSize: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加したドライブファイル数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			incCount: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加したドライブ使用量 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			incSize: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少したドライブファイル数 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			decCount: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少したドライブ使用量 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			decSize: number;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		remote: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全ドライブファイル数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			totalCount: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 集計期間時点での、全ドライブファイルの合計サイズ (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			totalSize: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加したドライブファイル数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			incCount: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 増加したドライブ使用量 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			incSize: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少したドライブファイル数 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			decCount: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * 減少したドライブ使用量 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			decSize: number;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * ネットワークに関する統計
 | 
			
		||||
	 */
 | 
			
		||||
	network: {
 | 
			
		||||
	remote: {
 | 
			
		||||
		/**
 | 
			
		||||
		 * 受信したリクエスト数
 | 
			
		||||
		 * 集計期間時点での、全ユーザー数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		incomingRequests: number;
 | 
			
		||||
		total: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 送信したリクエスト数
 | 
			
		||||
		 * 増加したユーザー数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		outgoingRequests: number;
 | 
			
		||||
		inc: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 応答時間の合計
 | 
			
		||||
		 * TIP: (totalTime / incomingRequests) でひとつのリクエストに平均でどれくらいの時間がかかったか知れる
 | 
			
		||||
		 * 減少したユーザー数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		totalTime: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 合計受信データ量
 | 
			
		||||
		 */
 | 
			
		||||
		incomingBytes: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 合計送信データ量
 | 
			
		||||
		 */
 | 
			
		||||
		outgoingBytes: number;
 | 
			
		||||
		dec: number;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CoreChart extends Chart<CoreStats> {
 | 
			
		||||
class UsersChart extends Chart<UsersStats> {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super('coreStats');
 | 
			
		||||
		super('usersStats');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected generateInitialStats(): CoreStats {
 | 
			
		||||
	protected generateInitialStats(): UsersStats {
 | 
			
		||||
		return {
 | 
			
		||||
			users: {
 | 
			
		||||
				local: {
 | 
			
		||||
					total: 0,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0
 | 
			
		||||
				},
 | 
			
		||||
				remote: {
 | 
			
		||||
					total: 0,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0
 | 
			
		||||
				}
 | 
			
		||||
			local: {
 | 
			
		||||
				total: 0,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0
 | 
			
		||||
			},
 | 
			
		||||
			notes: {
 | 
			
		||||
				local: {
 | 
			
		||||
					total: 0,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0,
 | 
			
		||||
					diffs: {
 | 
			
		||||
						normal: 0,
 | 
			
		||||
						reply: 0,
 | 
			
		||||
						renote: 0
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				remote: {
 | 
			
		||||
					total: 0,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0,
 | 
			
		||||
					diffs: {
 | 
			
		||||
						normal: 0,
 | 
			
		||||
						reply: 0,
 | 
			
		||||
						renote: 0
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			drive: {
 | 
			
		||||
				local: {
 | 
			
		||||
					totalCount: 0,
 | 
			
		||||
					totalSize: 0,
 | 
			
		||||
					incCount: 0,
 | 
			
		||||
					incSize: 0,
 | 
			
		||||
					decCount: 0,
 | 
			
		||||
					decSize: 0
 | 
			
		||||
				},
 | 
			
		||||
				remote: {
 | 
			
		||||
					totalCount: 0,
 | 
			
		||||
					totalSize: 0,
 | 
			
		||||
					incCount: 0,
 | 
			
		||||
					incSize: 0,
 | 
			
		||||
					decCount: 0,
 | 
			
		||||
					decSize: 0
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			network: {
 | 
			
		||||
				incomingRequests: 0,
 | 
			
		||||
				outgoingRequests: 0,
 | 
			
		||||
				totalTime: 0,
 | 
			
		||||
				incomingBytes: 0,
 | 
			
		||||
				outgoingBytes: 0
 | 
			
		||||
			remote: {
 | 
			
		||||
				total: 0,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected generateEmptyStats(mostRecentStats: CoreStats): CoreStats {
 | 
			
		||||
	protected generateEmptyStats(mostRecentStats: UsersStats): UsersStats {
 | 
			
		||||
		return {
 | 
			
		||||
			users: {
 | 
			
		||||
				local: {
 | 
			
		||||
					total: mostRecentStats.users.local.total,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0
 | 
			
		||||
				},
 | 
			
		||||
				remote: {
 | 
			
		||||
					total: mostRecentStats.users.remote.total,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0
 | 
			
		||||
				}
 | 
			
		||||
			local: {
 | 
			
		||||
				total: mostRecentStats.local.total,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0
 | 
			
		||||
			},
 | 
			
		||||
			notes: {
 | 
			
		||||
				local: {
 | 
			
		||||
					total: mostRecentStats.notes.local.total,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0,
 | 
			
		||||
					diffs: {
 | 
			
		||||
						normal: 0,
 | 
			
		||||
						reply: 0,
 | 
			
		||||
						renote: 0
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				remote: {
 | 
			
		||||
					total: mostRecentStats.notes.remote.total,
 | 
			
		||||
					inc: 0,
 | 
			
		||||
					dec: 0,
 | 
			
		||||
					diffs: {
 | 
			
		||||
						normal: 0,
 | 
			
		||||
						reply: 0,
 | 
			
		||||
						renote: 0
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			drive: {
 | 
			
		||||
				local: {
 | 
			
		||||
					totalCount: mostRecentStats.drive.local.totalCount,
 | 
			
		||||
					totalSize: mostRecentStats.drive.local.totalSize,
 | 
			
		||||
					incCount: 0,
 | 
			
		||||
					incSize: 0,
 | 
			
		||||
					decCount: 0,
 | 
			
		||||
					decSize: 0
 | 
			
		||||
				},
 | 
			
		||||
				remote: {
 | 
			
		||||
					totalCount: mostRecentStats.drive.remote.totalCount,
 | 
			
		||||
					totalSize: mostRecentStats.drive.remote.totalSize,
 | 
			
		||||
					incCount: 0,
 | 
			
		||||
					incSize: 0,
 | 
			
		||||
					decCount: 0,
 | 
			
		||||
					decSize: 0
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			network: {
 | 
			
		||||
				incomingRequests: 0,
 | 
			
		||||
				outgoingRequests: 0,
 | 
			
		||||
				totalTime: 0,
 | 
			
		||||
				incomingBytes: 0,
 | 
			
		||||
				outgoingBytes: 0
 | 
			
		||||
			remote: {
 | 
			
		||||
				total: mostRecentStats.remote.total,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async updateUserStats(user: IUser, isAdditional: boolean) {
 | 
			
		||||
		const origin = isLocalUser(user) ? 'local' : 'remote';
 | 
			
		||||
 | 
			
		||||
	public async update(user: IUser, isAdditional: boolean) {
 | 
			
		||||
		const update: Obj = {};
 | 
			
		||||
 | 
			
		||||
		update.total = isAdditional ? 1 : -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -548,18 +285,145 @@ class CoreChart extends Chart<CoreStats> {
 | 
			
		|||
			update.dec = 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const inc: Obj = {
 | 
			
		||||
			users: {}
 | 
			
		||||
		await this.inc({
 | 
			
		||||
			[isLocalUser(user) ? 'local' : 'remote']: update
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const usersChart = new UsersChart();
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
//#region Notes stats
 | 
			
		||||
/**
 | 
			
		||||
 * 投稿に関する統計
 | 
			
		||||
 */
 | 
			
		||||
type NotesStats = {
 | 
			
		||||
	local: {
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全投稿数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		total: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加した投稿数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		inc: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少した投稿数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		dec: number;
 | 
			
		||||
 | 
			
		||||
		diffs: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 通常の投稿数の差分 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			normal: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * リプライの投稿数の差分 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			reply: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * Renoteの投稿数の差分 (ローカル)
 | 
			
		||||
			 */
 | 
			
		||||
			renote: number;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
		inc.users[origin] = update;
 | 
			
		||||
	remote: {
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全投稿数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		total: number;
 | 
			
		||||
 | 
			
		||||
		await this.update(inc);
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加した投稿数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		inc: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少した投稿数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		dec: number;
 | 
			
		||||
 | 
			
		||||
		diffs: {
 | 
			
		||||
			/**
 | 
			
		||||
			 * 通常の投稿数の差分 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			normal: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * リプライの投稿数の差分 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			reply: number;
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * Renoteの投稿数の差分 (リモート)
 | 
			
		||||
			 */
 | 
			
		||||
			renote: number;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NotesChart extends Chart<NotesStats> {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super('notesStats');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async updateNoteStats(note: INote, isAdditional: boolean) {
 | 
			
		||||
		const origin = isLocalUser(note._user) ? 'local' : 'remote';
 | 
			
		||||
	protected generateInitialStats(): NotesStats {
 | 
			
		||||
		return {
 | 
			
		||||
			local: {
 | 
			
		||||
				total: 0,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0,
 | 
			
		||||
				diffs: {
 | 
			
		||||
					normal: 0,
 | 
			
		||||
					reply: 0,
 | 
			
		||||
					renote: 0
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			remote: {
 | 
			
		||||
				total: 0,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0,
 | 
			
		||||
				diffs: {
 | 
			
		||||
					normal: 0,
 | 
			
		||||
					reply: 0,
 | 
			
		||||
					renote: 0
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected generateEmptyStats(mostRecentStats: NotesStats): NotesStats {
 | 
			
		||||
		return {
 | 
			
		||||
			local: {
 | 
			
		||||
				total: mostRecentStats.local.total,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0,
 | 
			
		||||
				diffs: {
 | 
			
		||||
					normal: 0,
 | 
			
		||||
					reply: 0,
 | 
			
		||||
					renote: 0
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			remote: {
 | 
			
		||||
				total: mostRecentStats.remote.total,
 | 
			
		||||
				inc: 0,
 | 
			
		||||
				dec: 0,
 | 
			
		||||
				diffs: {
 | 
			
		||||
					normal: 0,
 | 
			
		||||
					reply: 0,
 | 
			
		||||
					renote: 0
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async update(note: INote, isAdditional: boolean) {
 | 
			
		||||
		const update: Obj = {};
 | 
			
		||||
 | 
			
		||||
		update.total = isAdditional ? 1 : -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -578,18 +442,133 @@ class CoreChart extends Chart<CoreStats> {
 | 
			
		|||
			update.diffs.normal = isAdditional ? 1 : -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const inc: Obj = {
 | 
			
		||||
			notes: {}
 | 
			
		||||
		};
 | 
			
		||||
		await this.inc({
 | 
			
		||||
			[isLocalUser(note._user) ? 'local' : 'remote']: update
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		inc.notes[origin] = update;
 | 
			
		||||
export const notesChart = new NotesChart();
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
		await this.update(inc);
 | 
			
		||||
//#region Drive stats
 | 
			
		||||
/**
 | 
			
		||||
 * ドライブに関する統計
 | 
			
		||||
 */
 | 
			
		||||
type DriveStats = {
 | 
			
		||||
	local: {
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全ドライブファイル数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		totalCount: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全ドライブファイルの合計サイズ (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		totalSize: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加したドライブファイル数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		incCount: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加したドライブ使用量 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		incSize: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少したドライブファイル数 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		decCount: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少したドライブ使用量 (ローカル)
 | 
			
		||||
		 */
 | 
			
		||||
		decSize: number;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	remote: {
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全ドライブファイル数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		totalCount: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 集計期間時点での、全ドライブファイルの合計サイズ (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		totalSize: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加したドライブファイル数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		incCount: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 増加したドライブ使用量 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		incSize: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少したドライブファイル数 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		decCount: number;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 減少したドライブ使用量 (リモート)
 | 
			
		||||
		 */
 | 
			
		||||
		decSize: number;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DriveChart extends Chart<DriveStats> {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super('driveStats');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async updateDriveStats(file: IDriveFile, isAdditional: boolean) {
 | 
			
		||||
		const origin = isLocalUser(file.metadata._user) ? 'local' : 'remote';
 | 
			
		||||
	protected generateInitialStats(): DriveStats {
 | 
			
		||||
		return {
 | 
			
		||||
			local: {
 | 
			
		||||
				totalCount: 0,
 | 
			
		||||
				totalSize: 0,
 | 
			
		||||
				incCount: 0,
 | 
			
		||||
				incSize: 0,
 | 
			
		||||
				decCount: 0,
 | 
			
		||||
				decSize: 0
 | 
			
		||||
			},
 | 
			
		||||
			remote: {
 | 
			
		||||
				totalCount: 0,
 | 
			
		||||
				totalSize: 0,
 | 
			
		||||
				incCount: 0,
 | 
			
		||||
				incSize: 0,
 | 
			
		||||
				decCount: 0,
 | 
			
		||||
				decSize: 0
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected generateEmptyStats(mostRecentStats: DriveStats): DriveStats {
 | 
			
		||||
		return {
 | 
			
		||||
			local: {
 | 
			
		||||
				totalCount: mostRecentStats.local.totalCount,
 | 
			
		||||
				totalSize: mostRecentStats.local.totalSize,
 | 
			
		||||
				incCount: 0,
 | 
			
		||||
				incSize: 0,
 | 
			
		||||
				decCount: 0,
 | 
			
		||||
				decSize: 0
 | 
			
		||||
			},
 | 
			
		||||
			remote: {
 | 
			
		||||
				totalCount: mostRecentStats.remote.totalCount,
 | 
			
		||||
				totalSize: mostRecentStats.remote.totalSize,
 | 
			
		||||
				incCount: 0,
 | 
			
		||||
				incSize: 0,
 | 
			
		||||
				decCount: 0,
 | 
			
		||||
				decSize: 0
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async update(file: IDriveFile, isAdditional: boolean) {
 | 
			
		||||
		const update: Obj = {};
 | 
			
		||||
 | 
			
		||||
		update.totalCount = isAdditional ? 1 : -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -602,27 +581,83 @@ class CoreChart extends Chart<CoreStats> {
 | 
			
		|||
			update.decSize = file.length;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const inc: Obj = {
 | 
			
		||||
			drive: {}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		inc.drive[origin] = update;
 | 
			
		||||
 | 
			
		||||
		await this.update(inc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async updateNetworkStats(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number) {
 | 
			
		||||
		const inc: Partial<CoreStats> = {
 | 
			
		||||
			network: {
 | 
			
		||||
				incomingRequests: incomingRequests,
 | 
			
		||||
				totalTime: time,
 | 
			
		||||
				incomingBytes: incomingBytes,
 | 
			
		||||
				outgoingBytes: outgoingBytes
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		await this.update(inc);
 | 
			
		||||
		await this.inc({
 | 
			
		||||
			[isLocalUser(file.metadata._user) ? 'local' : 'remote']: update
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const coreChart = new CoreChart();
 | 
			
		||||
export const driveChart = new DriveChart();
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
//#region Network stats
 | 
			
		||||
/**
 | 
			
		||||
 * ネットワークに関する統計
 | 
			
		||||
 */
 | 
			
		||||
type NetworkStats = {
 | 
			
		||||
	/**
 | 
			
		||||
	 * 受信したリクエスト数
 | 
			
		||||
	 */
 | 
			
		||||
	incomingRequests: number;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 送信したリクエスト数
 | 
			
		||||
	 */
 | 
			
		||||
	outgoingRequests: number;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 応答時間の合計
 | 
			
		||||
	 * TIP: (totalTime / incomingRequests) でひとつのリクエストに平均でどれくらいの時間がかかったか知れる
 | 
			
		||||
	 */
 | 
			
		||||
	totalTime: number;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 合計受信データ量
 | 
			
		||||
	 */
 | 
			
		||||
	incomingBytes: number;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 合計送信データ量
 | 
			
		||||
	 */
 | 
			
		||||
	outgoingBytes: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NetworkChart extends Chart<NetworkStats> {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super('networkStats');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected generateInitialStats(): NetworkStats {
 | 
			
		||||
		return {
 | 
			
		||||
			incomingRequests: 0,
 | 
			
		||||
			outgoingRequests: 0,
 | 
			
		||||
			totalTime: 0,
 | 
			
		||||
			incomingBytes: 0,
 | 
			
		||||
			outgoingBytes: 0
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected generateEmptyStats(mostRecentStats: NetworkStats): NetworkStats {
 | 
			
		||||
		return {
 | 
			
		||||
			incomingRequests: 0,
 | 
			
		||||
			outgoingRequests: 0,
 | 
			
		||||
			totalTime: 0,
 | 
			
		||||
			incomingBytes: 0,
 | 
			
		||||
			outgoingBytes: 0
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number) {
 | 
			
		||||
		const inc: Partial<NetworkStats> = {
 | 
			
		||||
			incomingRequests: incomingRequests,
 | 
			
		||||
			totalTime: time,
 | 
			
		||||
			incomingBytes: incomingBytes,
 | 
			
		||||
			outgoingBytes: outgoingBytes
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		await this.inc(inc);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const networkChart = new NetworkChart();
 | 
			
		||||
//#endregion
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue