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
This commit is contained in:
parent
f35688bab8
commit
56275bcfcb
17 changed files with 746 additions and 13 deletions
302
src/services/chart/instance.ts
Normal file
302
src/services/chart/instance.ts
Normal file
|
|
@ -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();
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue