diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2019-02-08 16:58:57 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-08 16:58:57 +0900 |
| commit | 56275bcfcbd1366b0e96b79915e810baed0548bb (patch) | |
| tree | b35c8a6bd15ef8046c00119f0271808970f4f939 /src/services | |
| parent | Supress logs during test (diff) | |
| download | misskey-56275bcfcbd1366b0e96b79915e810baed0548bb.tar.gz misskey-56275bcfcbd1366b0e96b79915e810baed0548bb.tar.bz2 misskey-56275bcfcbd1366b0e96b79915e810baed0548bb.zip | |
Introduce per-instance chart (#4183)
* Introduce per-instance chart
* Implement chart view in client
* Handle note deleting
* More chart srcs
* Add drive stats
* Improve drive stats
* Fix bug
* Add icon
Diffstat (limited to 'src/services')
| -rw-r--r-- | src/services/chart/instance.ts | 302 | ||||
| -rw-r--r-- | src/services/drive/add-file.ts | 13 | ||||
| -rw-r--r-- | src/services/drive/delete-file.ts | 12 | ||||
| -rw-r--r-- | src/services/following/create.ts | 7 | ||||
| -rw-r--r-- | src/services/following/delete.ts | 27 | ||||
| -rw-r--r-- | src/services/note/create.ts | 4 | ||||
| -rw-r--r-- | src/services/note/delete.ts | 17 |
7 files changed, 374 insertions, 8 deletions
diff --git a/src/services/chart/instance.ts b/src/services/chart/instance.ts new file mode 100644 index 0000000000..5af398b902 --- /dev/null +++ b/src/services/chart/instance.ts @@ -0,0 +1,302 @@ +import autobind from 'autobind-decorator'; +import Chart, { Obj } from '.'; +import User from '../../models/user'; +import Note from '../../models/note'; +import Following from '../../models/following'; +import DriveFile, { IDriveFile } from '../../models/drive-file'; + +/** + * インスタンスごとのチャート + */ +type InstanceLog = { + requests: { + /** + * 失敗したリクエスト数 + */ + failed: number; + + /** + * 成功したリクエスト数 + */ + succeeded: number; + + /** + * 受信したリクエスト数 + */ + received: number; + }; + + notes: { + /** + * 集計期間時点での、全投稿数 + */ + total: number; + + /** + * 増加した投稿数 + */ + inc: number; + + /** + * 減少した投稿数 + */ + dec: number; + }; + + users: { + /** + * 集計期間時点での、全ユーザー数 + */ + total: number; + + /** + * 増加したユーザー数 + */ + inc: number; + + /** + * 減少したユーザー数 + */ + dec: number; + }; + + following: { + /** + * 集計期間時点での、全フォロー数 + */ + total: number; + + /** + * 増加したフォロー数 + */ + inc: number; + + /** + * 減少したフォロー数 + */ + dec: number; + }; + + followers: { + /** + * 集計期間時点での、全フォロワー数 + */ + total: number; + + /** + * 増加したフォロワー数 + */ + inc: number; + + /** + * 減少したフォロワー数 + */ + dec: number; + }; + + drive: { + /** + * 集計期間時点での、全ドライブファイル数 + */ + totalFiles: number; + + /** + * 集計期間時点での、全ドライブファイルの合計サイズ + */ + totalUsage: number; + + /** + * 増加したドライブファイル数 + */ + incFiles: number; + + /** + * 増加したドライブ使用量 + */ + incUsage: number; + + /** + * 減少したドライブファイル数 + */ + decFiles: number; + + /** + * 減少したドライブ使用量 + */ + decUsage: number; + }; +}; + +class InstanceChart extends Chart<InstanceLog> { + constructor() { + super('instance', true); + } + + @autobind + protected async getTemplate(init: boolean, latest?: InstanceLog, group?: any): Promise<InstanceLog> { + const calcUsage = () => DriveFile + .aggregate([{ + $match: { + 'metadata._user.host': group, + 'metadata.deletedAt': { $exists: false } + } + }, { + $project: { + length: true + } + }, { + $group: { + _id: null, + usage: { $sum: '$length' } + } + }]) + .then(res => res.length > 0 ? res[0].usage : 0); + + const [ + notesCount, + usersCount, + followingCount, + followersCount, + driveFiles, + driveUsage, + ] = init ? await Promise.all([ + Note.count({ '_user.host': group }), + User.count({ host: group }), + Following.count({ '_follower.host': group }), + Following.count({ '_followee.host': group }), + DriveFile.count({ 'metadata._user.host': group }), + calcUsage(), + ]) : [ + latest ? latest.notes.total : 0, + latest ? latest.users.total : 0, + latest ? latest.following.total : 0, + latest ? latest.followers.total : 0, + latest ? latest.drive.totalFiles : 0, + latest ? latest.drive.totalUsage : 0, + ]; + + return { + requests: { + failed: 0, + succeeded: 0, + received: 0 + }, + notes: { + total: notesCount, + inc: 0, + dec: 0 + }, + users: { + total: usersCount, + inc: 0, + dec: 0 + }, + following: { + total: followingCount, + inc: 0, + dec: 0 + }, + followers: { + total: followersCount, + inc: 0, + dec: 0 + }, + drive: { + totalFiles: driveFiles, + totalUsage: driveUsage, + incFiles: 0, + incUsage: 0, + decFiles: 0, + decUsage: 0 + } + }; + } + + @autobind + public async requestReceived(host: string) { + await this.inc({ + requests: { + received: 1 + } + }, host); + } + + @autobind + public async requestSent(host: string, isSucceeded: boolean) { + const update: Obj = {}; + + if (isSucceeded) { + update.succeeded = 1; + } else { + update.failed = 1; + } + + await this.inc({ + requests: update + }, host); + } + + @autobind + public async newUser(host: string) { + await this.inc({ + users: { + total: 1, + inc: 1 + } + }, host); + } + + @autobind + public async updateNote(host: string, isAdditional: boolean) { + await this.inc({ + notes: { + total: isAdditional ? 1 : -1, + inc: isAdditional ? 1 : 0, + dec: isAdditional ? 0 : 1, + } + }, host); + } + + @autobind + public async updateFollowing(host: string, isAdditional: boolean) { + await this.inc({ + following: { + total: isAdditional ? 1 : -1, + inc: isAdditional ? 1 : 0, + dec: isAdditional ? 0 : 1, + } + }, host); + } + + @autobind + public async updateFollowers(host: string, isAdditional: boolean) { + await this.inc({ + followers: { + total: isAdditional ? 1 : -1, + inc: isAdditional ? 1 : 0, + dec: isAdditional ? 0 : 1, + } + }, host); + } + + @autobind + public async updateDrive(file: IDriveFile, isAdditional: boolean) { + const update: Obj = {}; + + update.totalFiles = isAdditional ? 1 : -1; + update.totalUsage = isAdditional ? file.length : -file.length; + if (isAdditional) { + update.incFiles = 1; + update.incUsage = file.length; + } else { + update.decFiles = 1; + update.decUsage = file.length; + } + + await this.inc({ + drive: update + }, file.metadata._user.host); + } +} + +export default new InstanceChart(); diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index 0e588d3442..9f3805f94b 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -13,17 +13,19 @@ import DriveFile, { IMetadata, getDriveFileBucket, IDriveFile } from '../../mode import DriveFolder from '../../models/drive-folder'; import { pack } from '../../models/drive-file'; import { publishMainStream, publishDriveStream } from '../stream'; -import { isLocalUser, IUser, IRemoteUser } from '../../models/user'; +import { isLocalUser, IUser, IRemoteUser, isRemoteUser } from '../../models/user'; import delFile from './delete-file'; import config from '../../config'; import { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic'; import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail'; import driveChart from '../../services/chart/drive'; import perUserDriveChart from '../../services/chart/per-user-drive'; +import instanceChart from '../../services/chart/instance'; import fetchMeta from '../../misc/fetch-meta'; import { GenerateVideoThumbnail } from './generate-video-thumbnail'; import { driveLogger } from './logger'; import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor'; +import Instance from '../../models/instance'; const logger = driveLogger.createSubLogger('register', 'yellow'); @@ -523,6 +525,15 @@ export default async function( // 統計を更新 driveChart.update(driveFile, true); perUserDriveChart.update(driveFile, true); + if (isRemoteUser(driveFile.metadata._user)) { + instanceChart.updateDrive(driveFile, true); + Instance.update({ host: driveFile.metadata._user.host }, { + $inc: { + driveUsage: driveFile.length, + driveFiles: 1 + } + }); + } return driveFile; } diff --git a/src/services/drive/delete-file.ts b/src/services/drive/delete-file.ts index 4211cd8291..c5c15ca20b 100644 --- a/src/services/drive/delete-file.ts +++ b/src/services/drive/delete-file.ts @@ -4,7 +4,10 @@ import DriveFileThumbnail, { DriveFileThumbnailChunk } from '../../models/drive- import config from '../../config'; import driveChart from '../../services/chart/drive'; import perUserDriveChart from '../../services/chart/per-user-drive'; +import instanceChart from '../../services/chart/instance'; import DriveFileWebpublic, { DriveFileWebpublicChunk } from '../../models/drive-file-webpublic'; +import Instance from '../../models/instance'; +import { isRemoteUser } from '../../models/user'; export default async function(file: IDriveFile, isExpired = false) { if (file.metadata.storage == 'minio') { @@ -84,4 +87,13 @@ export default async function(file: IDriveFile, isExpired = false) { // 統計を更新 driveChart.update(file, false); perUserDriveChart.update(file, false); + if (isRemoteUser(file.metadata._user)) { + instanceChart.updateDrive(file, false); + Instance.update({ host: file.metadata._user.host }, { + $inc: { + driveUsage: -file.length, + driveFiles: -1 + } + }); + } } diff --git a/src/services/following/create.ts b/src/services/following/create.ts index 65b80dcf84..05f4632582 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -12,6 +12,7 @@ import createFollowRequest from './requests/create'; import perUserFollowingChart from '../../services/chart/per-user-following'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; import Instance from '../../models/instance'; +import instanceChart from '../../services/chart/instance'; export default async function(follower: IUser, followee: IUser, requestId?: string) { // check blocking @@ -108,8 +109,7 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri } }); - // TODO - //perInstanceChart.newFollowing(); + instanceChart.updateFollowing(i.host, true); }); } else if (isLocalUser(follower) && isRemoteUser(followee)) { registerOrFetchInstanceDoc(followee.host).then(i => { @@ -119,8 +119,7 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri } }); - // TODO - //perInstanceChart.newFollower(); + instanceChart.updateFollowers(i.host, true); }); } //#endregion diff --git a/src/services/following/delete.ts b/src/services/following/delete.ts index 87eaf826e5..93f72b51d8 100644 --- a/src/services/following/delete.ts +++ b/src/services/following/delete.ts @@ -7,6 +7,9 @@ import renderUndo from '../../remote/activitypub/renderer/undo'; import { deliver } from '../../queue'; import perUserFollowingChart from '../../services/chart/per-user-following'; import Logger from '../../misc/logger'; +import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; +import Instance from '../../models/instance'; +import instanceChart from '../../services/chart/instance'; const logger = new Logger('following/delete'); @@ -41,6 +44,30 @@ export default async function(follower: IUser, followee: IUser) { }); //#endregion + //#region Update instance stats + if (isRemoteUser(follower) && isLocalUser(followee)) { + registerOrFetchInstanceDoc(follower.host).then(i => { + Instance.update({ _id: i._id }, { + $inc: { + followingCount: -1 + } + }); + + instanceChart.updateFollowing(i.host, false); + }); + } else if (isLocalUser(follower) && isRemoteUser(followee)) { + registerOrFetchInstanceDoc(followee.host).then(i => { + Instance.update({ _id: i._id }, { + $inc: { + followersCount: -1 + } + }); + + instanceChart.updateFollowers(i.host, false); + }); + } + //#endregion + perUserFollowingChart.update(follower, followee, false); // Publish unfollow event diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 0b71a9670c..126d698b08 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -24,6 +24,7 @@ import isQuote from '../../misc/is-quote'; import notesChart from '../../services/chart/notes'; import perUserNotesChart from '../../services/chart/per-user-notes'; import activeUsersChart from '../../services/chart/active-users'; +import instanceChart from '../../services/chart/instance'; import { erase, concat } from '../../prelude/array'; import insertNoteUnread from './unread'; @@ -229,8 +230,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise< } }); - // TODO - //perInstanceChart.newNote(); + instanceChart.updateNote(i.host, true); }); } diff --git a/src/services/note/delete.ts b/src/services/note/delete.ts index 8e8c20bfce..2b797545ed 100644 --- a/src/services/note/delete.ts +++ b/src/services/note/delete.ts @@ -1,5 +1,5 @@ import Note, { INote } from '../../models/note'; -import { IUser, isLocalUser } from '../../models/user'; +import { IUser, isLocalUser, isRemoteUser } from '../../models/user'; import { publishNoteStream } from '../stream'; import renderDelete from '../../remote/activitypub/renderer/delete'; import { renderActivity } from '../../remote/activitypub/renderer'; @@ -12,6 +12,9 @@ import config from '../../config'; import NoteUnread from '../../models/note-unread'; import read from './read'; import DriveFile from '../../models/drive-file'; +import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; +import Instance from '../../models/instance'; +import instanceChart from '../../services/chart/instance'; /** * 投稿を削除します。 @@ -91,4 +94,16 @@ export default async function(user: IUser, note: INote) { // 統計を更新 notesChart.update(note, false); perUserNotesChart.update(user, note, false); + + if (isRemoteUser(user)) { + registerOrFetchInstanceDoc(user.host).then(i => { + Instance.update({ _id: i._id }, { + $inc: { + notesCount: -1 + } + }); + + instanceChart.updateNote(i.host, false); + }); + } } |