From bc34ac82cf4effe2baba8471315ea4a78dae416a Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 18 Aug 2018 03:52:24 +0900 Subject: Show some charts in control panel --- src/server/api/endpoints/aggregation/notes.ts | 110 ++++++++++++++++++++++++++ src/server/api/endpoints/aggregation/posts.ts | 84 -------------------- src/server/api/endpoints/aggregation/users.ts | 99 +++++++++++++++-------- 3 files changed, 175 insertions(+), 118 deletions(-) create mode 100644 src/server/api/endpoints/aggregation/notes.ts delete mode 100644 src/server/api/endpoints/aggregation/posts.ts (limited to 'src/server/api/endpoints/aggregation') diff --git a/src/server/api/endpoints/aggregation/notes.ts b/src/server/api/endpoints/aggregation/notes.ts new file mode 100644 index 0000000000..b745c86631 --- /dev/null +++ b/src/server/api/endpoints/aggregation/notes.ts @@ -0,0 +1,110 @@ +import $ from 'cafy'; +import Note from '../../../../models/note'; + +/** + * Aggregate notes + */ +export default (params: any) => new Promise(async (res, rej) => { + // Get 'limit' parameter + const [limit = 365, limitErr] = $.num.optional.range(1, 365).get(params.limit); + if (limitErr) return rej('invalid limit param'); + + const query = [{ + $project: { + renoteId: '$renoteId', + replyId: '$replyId', + user: '$_user', + createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST + } + }, { + $project: { + date: { + year: { $year: '$createdAt' }, + month: { $month: '$createdAt' }, + day: { $dayOfMonth: '$createdAt' } + }, + type: { + $cond: { + if: { $ne: ['$renoteId', null] }, + then: 'renote', + else: { + $cond: { + if: { $ne: ['$replyId', null] }, + then: 'reply', + else: 'note' + } + } + } + }, + origin: { + $cond: { + if: { $eq: ['$user.host', null] }, + then: 'local', + else: 'remote' + } + } + } + }, { + $group: { + _id: { + date: '$date', + type: '$type', + origin: '$origin' + }, + count: { $sum: 1 } + } + }, { + $group: { + _id: '$_id.date', + data: { + $addToSet: { + type: '$_id.type', + origin: '$_id.origin', + count: '$count' + } + } + } + }] as any; + + const datas = await Note.aggregate(query); + + datas.forEach((data: any) => { + data.date = data._id; + delete data._id; + + data.localNotes = (data.data.filter((x: any) => x.type == 'note' && x.origin == 'local')[0] || { count: 0 }).count; + data.localRenotes = (data.data.filter((x: any) => x.type == 'renote' && x.origin == 'local')[0] || { count: 0 }).count; + data.localReplies = (data.data.filter((x: any) => x.type == 'reply' && x.origin == 'local')[0] || { count: 0 }).count; + data.remoteNotes = (data.data.filter((x: any) => x.type == 'note' && x.origin == 'remote')[0] || { count: 0 }).count; + data.remoteRenotes = (data.data.filter((x: any) => x.type == 'renote' && x.origin == 'remote')[0] || { count: 0 }).count; + data.remoteReplies = (data.data.filter((x: any) => x.type == 'reply' && x.origin == 'remote')[0] || { count: 0 }).count; + + delete data.data; + }); + + const graph = []; + + for (let i = 0; i < limit; i++) { + const day = new Date(new Date().setDate(new Date().getDate() - i)); + + const data = datas.filter((d: any) => + d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() + )[0]; + + if (data) { + graph.push(data); + } else { + graph.push({ + date: { year: day.getFullYear(), month: day.getMonth() + 1, day: day.getDate() }, + localNotes: 0, + localRenotes: 0, + localReplies: 0, + remoteNotes: 0, + remoteRenotes: 0, + remoteReplies: 0 + }); + } + } + + res(graph); +}); diff --git a/src/server/api/endpoints/aggregation/posts.ts b/src/server/api/endpoints/aggregation/posts.ts deleted file mode 100644 index 629bb19108..0000000000 --- a/src/server/api/endpoints/aggregation/posts.ts +++ /dev/null @@ -1,84 +0,0 @@ -import $ from 'cafy'; -import Note from '../../../../models/note'; - -/** - * Aggregate notes - */ -export default (params: any) => new Promise(async (res, rej) => { - // Get 'limit' parameter - const [limit = 365, limitErr] = $.num.optional.range(1, 365).get(params.limit); - if (limitErr) return rej('invalid limit param'); - - const datas = await Note - .aggregate([ - { $project: { - renoteId: '$renoteId', - replyId: '$replyId', - createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST - }}, - { $project: { - date: { - year: { $year: '$createdAt' }, - month: { $month: '$createdAt' }, - day: { $dayOfMonth: '$createdAt' } - }, - type: { - $cond: { - if: { $ne: ['$renoteId', null] }, - then: 'renote', - else: { - $cond: { - if: { $ne: ['$replyId', null] }, - then: 'reply', - else: 'note' - } - } - } - }} - }, - { $group: { _id: { - date: '$date', - type: '$type' - }, count: { $sum: 1 } } }, - { $group: { - _id: '$_id.date', - data: { $addToSet: { - type: '$_id.type', - count: '$count' - }} - } } - ]); - - datas.forEach((data: any) => { - data.date = data._id; - delete data._id; - - data.notes = (data.data.filter((x: any) => x.type == 'note')[0] || { count: 0 }).count; - data.renotes = (data.data.filter((x: any) => x.type == 'renote')[0] || { count: 0 }).count; - data.replies = (data.data.filter((x: any) => x.type == 'reply')[0] || { count: 0 }).count; - - delete data.data; - }); - - const graph = []; - - for (let i = 0; i < limit; i++) { - const day = new Date(new Date().setDate(new Date().getDate() - i)); - - const data = datas.filter((d: any) => - d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() - )[0]; - - if (data) { - graph.push(data); - } else { - graph.push({ - notes: 0, - renotes: 0, - replies: 0 - }); - } - } - - res(graph); -}); diff --git a/src/server/api/endpoints/aggregation/users.ts b/src/server/api/endpoints/aggregation/users.ts index f1e41cf170..2e397545de 100644 --- a/src/server/api/endpoints/aggregation/users.ts +++ b/src/server/api/endpoints/aggregation/users.ts @@ -9,46 +9,77 @@ export default (params: any) => new Promise(async (res, rej) => { const [limit = 365, limitErr] = $.num.optional.range(1, 365).get(params.limit); if (limitErr) return rej('invalid limit param'); - const users = await User - .find({}, { - sort: { - _id: -1 + const query = [{ + $project: { + host: '$host', + createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST + } + }, { + $project: { + date: { + year: { $year: '$createdAt' }, + month: { $month: '$createdAt' }, + day: { $dayOfMonth: '$createdAt' } }, - fields: { - _id: false, - createdAt: true, - deletedAt: true + origin: { + $cond: { + if: { $eq: ['$host', null] }, + then: 'local', + else: 'remote' + } } - }); + } + }, { + $group: { + _id: { + date: '$date', + origin: '$origin' + }, + count: { $sum: 1 } + } + }, { + $group: { + _id: '$_id.date', + data: { + $addToSet: { + type: '$_id.type', + origin: '$_id.origin', + count: '$count' + } + } + } + }] as any; + + const datas = await User.aggregate(query); + + datas.forEach((data: any) => { + data.date = data._id; + delete data._id; + + data.local = (data.data.filter((x: any) => x.origin == 'local')[0] || { count: 0 }).count; + data.remote = (data.data.filter((x: any) => x.origin == 'remote')[0] || { count: 0 }).count; + + delete data.data; + }); const graph = []; for (let i = 0; i < limit; i++) { - let dayStart = new Date(new Date().setDate(new Date().getDate() - i)); - dayStart = new Date(dayStart.setMilliseconds(0)); - dayStart = new Date(dayStart.setSeconds(0)); - dayStart = new Date(dayStart.setMinutes(0)); - dayStart = new Date(dayStart.setHours(0)); - - let dayEnd = new Date(new Date().setDate(new Date().getDate() - i)); - dayEnd = new Date(dayEnd.setMilliseconds(999)); - dayEnd = new Date(dayEnd.setSeconds(59)); - dayEnd = new Date(dayEnd.setMinutes(59)); - dayEnd = new Date(dayEnd.setHours(23)); - // day = day.getTime(); - - const total = users.filter(u => - u.createdAt < dayEnd && (u.deletedAt == null || u.deletedAt > dayEnd) - ).length; - - const created = users.filter(u => - u.createdAt < dayEnd && u.createdAt > dayStart - ).length; - - graph.push({ - total: total, - created: created - }); + const day = new Date(new Date().setDate(new Date().getDate() - i)); + + const data = datas.filter((d: any) => + d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() + )[0]; + + if (data) { + graph.push(data); + } else { + graph.push({ + date: { year: day.getFullYear(), month: day.getMonth() + 1, day: day.getDate() }, + local: 0, + remote: 0 + }); + } } res(graph); -- cgit v1.2.3-freya From 160185b108fd297959c58ca3e397d9830509094c Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 18 Aug 2018 05:43:09 +0900 Subject: Fix bug --- src/server/api/endpoints/aggregation/notes.ts | 15 ++++++++++----- src/server/api/endpoints/aggregation/users.ts | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'src/server/api/endpoints/aggregation') diff --git a/src/server/api/endpoints/aggregation/notes.ts b/src/server/api/endpoints/aggregation/notes.ts index b745c86631..59dd46ae2e 100644 --- a/src/server/api/endpoints/aggregation/notes.ts +++ b/src/server/api/endpoints/aggregation/notes.ts @@ -1,15 +1,20 @@ import $ from 'cafy'; import Note from '../../../../models/note'; +export const meta = { + requireCredential: true, + requireAdmin: true +}; + /** * Aggregate notes */ export default (params: any) => new Promise(async (res, rej) => { - // Get 'limit' parameter - const [limit = 365, limitErr] = $.num.optional.range(1, 365).get(params.limit); - if (limitErr) return rej('invalid limit param'); - const query = [{ + createdAt: { + $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) + } + }, { $project: { renoteId: '$renoteId', replyId: '$replyId', @@ -84,7 +89,7 @@ export default (params: any) => new Promise(async (res, rej) => { const graph = []; - for (let i = 0; i < limit; i++) { + for (let i = 0; i < 365; i++) { const day = new Date(new Date().setDate(new Date().getDate() - i)); const data = datas.filter((d: any) => diff --git a/src/server/api/endpoints/aggregation/users.ts b/src/server/api/endpoints/aggregation/users.ts index 2e397545de..bb36fbfe46 100644 --- a/src/server/api/endpoints/aggregation/users.ts +++ b/src/server/api/endpoints/aggregation/users.ts @@ -1,15 +1,20 @@ import $ from 'cafy'; import User from '../../../../models/user'; +export const meta = { + requireCredential: true, + requireAdmin: true +}; + /** * Aggregate users */ export default (params: any) => new Promise(async (res, rej) => { - // Get 'limit' parameter - const [limit = 365, limitErr] = $.num.optional.range(1, 365).get(params.limit); - if (limitErr) return rej('invalid limit param'); - const query = [{ + createdAt: { + $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) + } + }, { $project: { host: '$host', createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST @@ -64,7 +69,7 @@ export default (params: any) => new Promise(async (res, rej) => { const graph = []; - for (let i = 0; i < limit; i++) { + for (let i = 0; i < 365; i++) { const day = new Date(new Date().setDate(new Date().getDate() - i)); const data = datas.filter((d: any) => -- cgit v1.2.3-freya From 31aaf559ac7aa50190a2b783fa6c7da84ad48eeb Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 18 Aug 2018 05:46:57 +0900 Subject: Fix bug --- src/server/api/endpoints/aggregation/notes.ts | 7 ++++--- src/server/api/endpoints/aggregation/users.ts | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src/server/api/endpoints/aggregation') diff --git a/src/server/api/endpoints/aggregation/notes.ts b/src/server/api/endpoints/aggregation/notes.ts index 59dd46ae2e..77ed07ef4b 100644 --- a/src/server/api/endpoints/aggregation/notes.ts +++ b/src/server/api/endpoints/aggregation/notes.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import Note from '../../../../models/note'; export const meta = { @@ -11,8 +10,10 @@ export const meta = { */ export default (params: any) => new Promise(async (res, rej) => { const query = [{ - createdAt: { - $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) + $match: { + createdAt: { + $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) + } } }, { $project: { diff --git a/src/server/api/endpoints/aggregation/users.ts b/src/server/api/endpoints/aggregation/users.ts index bb36fbfe46..d016484238 100644 --- a/src/server/api/endpoints/aggregation/users.ts +++ b/src/server/api/endpoints/aggregation/users.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import User from '../../../../models/user'; export const meta = { @@ -11,8 +10,10 @@ export const meta = { */ export default (params: any) => new Promise(async (res, rej) => { const query = [{ - createdAt: { - $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) + $match: { + createdAt: { + $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) + } } }, { $project: { -- cgit v1.2.3-freya From 0481de6629536f7f3144321d9e4fe2144c62d7f0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 19 Aug 2018 00:27:23 +0900 Subject: wip --- cli/migration/7.0.0.js | 4 +- .../views/pages/admin/admin.notes-chart.chart.vue | 25 +-- .../views/pages/admin/admin.notes-chart.vue | 17 +- .../views/pages/admin/admin.users-chart.chart.vue | 14 +- .../views/pages/admin/admin.users-chart.vue | 17 +- src/client/app/desktop/views/pages/admin/admin.vue | 12 +- src/client/app/stats/style.styl | 10 - src/client/app/stats/tags/index.tag | 209 --------------------- src/client/app/stats/tags/index.ts | 1 - src/client/app/status/style.styl | 10 - src/client/app/status/tags/index.tag | 201 -------------------- src/client/app/status/tags/index.ts | 1 - src/models/chart.ts | 153 --------------- src/models/stats.ts | 153 +++++++++++++++ src/server/api/endpoints/admin/chart.ts | 97 ++++++++++ src/server/api/endpoints/aggregation/notes.ts | 116 ------------ src/server/api/endpoints/aggregation/users.ts | 92 --------- src/services/update-chart.ts | 18 +- 18 files changed, 298 insertions(+), 852 deletions(-) delete mode 100644 src/client/app/stats/style.styl delete mode 100644 src/client/app/stats/tags/index.tag delete mode 100644 src/client/app/stats/tags/index.ts delete mode 100644 src/client/app/status/style.styl delete mode 100644 src/client/app/status/tags/index.tag delete mode 100644 src/client/app/status/tags/index.ts delete mode 100644 src/models/chart.ts create mode 100644 src/models/stats.ts create mode 100644 src/server/api/endpoints/admin/chart.ts delete mode 100644 src/server/api/endpoints/aggregation/notes.ts delete mode 100644 src/server/api/endpoints/aggregation/users.ts (limited to 'src/server/api/endpoints/aggregation') diff --git a/cli/migration/7.0.0.js b/cli/migration/7.0.0.js index d40670c6c0..fa5e363db8 100644 --- a/cli/migration/7.0.0.js +++ b/cli/migration/7.0.0.js @@ -1,4 +1,4 @@ -const { default: Chart } = require('../../built/models/chart'); +const { default: Stats } = require('../../built/models/stats'); const { default: User } = require('../../built/models/user'); const { default: Note } = require('../../built/models/note'); const { default: DriveFile } = require('../../built/models/drive-file'); @@ -80,7 +80,7 @@ async function main() { return 0; }); - await Chart.insert({ + await Stats.insert({ date: today, users: { local: { diff --git a/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue index 52bd8e7c77..3e9462f035 100644 --- a/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue +++ b/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue @@ -29,7 +29,7 @@ import Vue from 'vue'; export default Vue.extend({ props: { - data: { + chart: { required: true }, type: { @@ -39,7 +39,6 @@ export default Vue.extend({ }, data() { return { - chart: this.data, viewBoxX: 365, viewBoxY: 70, pointsNote: null, @@ -49,21 +48,17 @@ export default Vue.extend({ }; }, created() { - this.chart.forEach(d => { - d.notes = this.type == 'local' ? d.localNotes : d.remoteNotes; - d.replies = this.type == 'local' ? d.localReplies : d.remoteReplies; - d.renotes = this.type == 'local' ? d.localRenotes : d.remoteRenotes; - }); - - this.chart.forEach(d => { - d.total = d.notes + d.replies + d.renotes; - }); - - const peak = Math.max.apply(null, this.chart.map(d => d.total)); + const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.notes.local.diff : d.notes.remote.diff)); if (peak != 0) { - const data = this.chart.slice().reverse(); - this.pointsNote = data.map((d, i) => `${i},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' '); + const data = this.chart.slice().reverse().map(x => ({ + normal: this.type == 'local' ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal, + replies: this.type == 'local' ? x.notes.local.diffs.replies : x.notes.remote.diffs.replies, + renotes: this.type == 'local' ? x.notes.local.diffs.renotes : x.notes.remote.diffs.renotes, + total: this.type == 'local' ? x.notes.local.diff : x.notes.remote.diff + })); + + this.pointsNote = data.map((d, i) => `${i},${(1 - (d.normal / peak)) * this.viewBoxY}`).join(' '); this.pointsReply = data.map((d, i) => `${i},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '); this.pointsRenote = data.map((d, i) => `${i},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' '); this.pointsTotal = data.map((d, i) => `${i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' '); diff --git a/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue b/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue index 8b33e59ec3..e4d396d9c6 100644 --- a/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue +++ b/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue @@ -3,11 +3,11 @@
%i18n:@title%
%i18n:@local%
- +
%i18n:@remote%
- +
@@ -20,15 +20,10 @@ export default Vue.extend({ components: { XChart }, - data() { - return { - data: null - }; - }, - created() { - (this as any).api('aggregation/notes').then(res => { - this.data = res; - }); + props: { + chart: { + required: true + } } }); diff --git a/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue index 10eab85279..c2ab4a78e3 100644 --- a/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue +++ b/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue @@ -13,7 +13,7 @@ import Vue from 'vue'; export default Vue.extend({ props: { - data: { + chart: { required: true }, type: { @@ -23,21 +23,19 @@ export default Vue.extend({ }, data() { return { - chart: this.data, viewBoxX: 365, viewBoxY: 70, points: null }; }, created() { - this.chart.forEach(d => { - d.count = this.type == 'local' ? d.local : d.remote; - }); - - const peak = Math.max.apply(null, this.chart.map(d => d.count)); + const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.users.local.diff : d.users.remote.diff)); if (peak != 0) { - const data = this.chart.slice().reverse(); + const data = this.chart.slice().reverse().map(x => ({ + count: this.type == 'local' ? x.users.local.diff : x.users.remote.diff + })); + this.points = data.map((d, i) => `${i},${(1 - (d.count / peak)) * this.viewBoxY}`).join(' '); } } diff --git a/src/client/app/desktop/views/pages/admin/admin.users-chart.vue b/src/client/app/desktop/views/pages/admin/admin.users-chart.vue index bbd342e515..e620012702 100644 --- a/src/client/app/desktop/views/pages/admin/admin.users-chart.vue +++ b/src/client/app/desktop/views/pages/admin/admin.users-chart.vue @@ -3,11 +3,11 @@
%i18n:@title%
%i18n:@local%
- +
%i18n:@remote%
- +
@@ -20,15 +20,10 @@ export default Vue.extend({ components: { XChart }, - data() { - return { - data: null - }; - }, - created() { - (this as any).api('aggregation/users').then(res => { - this.data = res; - }); + props: { + chart: { + required: true + } } }); diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue index 7c1dace78c..add95a1a09 100644 --- a/src/client/app/desktop/views/pages/admin/admin.vue +++ b/src/client/app/desktop/views/pages/admin/admin.vue @@ -11,8 +11,8 @@
- - + +
@@ -48,9 +48,15 @@ export default Vue.extend({ }, data() { return { - page: 'dashboard' + page: 'dashboard', + chart: null }; }, + created() { + (this as any).api('admin/chart').then(chart => { + this.chart = chart; + }); + }, methods: { nav(page: string) { this.page = page; diff --git a/src/client/app/stats/style.styl b/src/client/app/stats/style.styl deleted file mode 100644 index 5ae230ea56..0000000000 --- a/src/client/app/stats/style.styl +++ /dev/null @@ -1,10 +0,0 @@ -@import "../app" -@import "../reset" - -html - color #456267 - background #fff - -body - margin 0 - padding 0 diff --git a/src/client/app/stats/tags/index.tag b/src/client/app/stats/tags/index.tag deleted file mode 100644 index f8944c0832..0000000000 --- a/src/client/app/stats/tags/index.tag +++ /dev/null @@ -1,209 +0,0 @@ - -

MisskeyStatistics

-
- - -
- - - -
- - -

%i18n:stats.notes-count% { stats.notesCount }

- - - -
- - -

%i18n:stats.users-count% { stats.usersCount }

- - - -
- - - - Black ... Total
Blue ... Notes
Red ... Replies
Green ... Renotes
- - - - -
- - -
- - - - - - - - - diff --git a/src/client/app/stats/tags/index.ts b/src/client/app/stats/tags/index.ts deleted file mode 100644 index f41151949f..0000000000 --- a/src/client/app/stats/tags/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./index.tag'); diff --git a/src/client/app/status/style.styl b/src/client/app/status/style.styl deleted file mode 100644 index 5ae230ea56..0000000000 --- a/src/client/app/status/style.styl +++ /dev/null @@ -1,10 +0,0 @@ -@import "../app" -@import "../reset" - -html - color #456267 - background #fff - -body - margin 0 - padding 0 diff --git a/src/client/app/status/tags/index.tag b/src/client/app/status/tags/index.tag deleted file mode 100644 index 899467097a..0000000000 --- a/src/client/app/status/tags/index.tag +++ /dev/null @@ -1,201 +0,0 @@ - -

MisskeyStatus

-

%fa:info-circle%%i18n:status.all-systems-maybe-operational%

-
- - -
- - - -
- - -

CPU { percentage }%

- - - -
- - -

MEM { percentage }%

- - - -
- - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/client/app/status/tags/index.ts b/src/client/app/status/tags/index.ts deleted file mode 100644 index f41151949f..0000000000 --- a/src/client/app/status/tags/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./index.tag'); diff --git a/src/models/chart.ts b/src/models/chart.ts deleted file mode 100644 index a2154be4fc..0000000000 --- a/src/models/chart.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as mongo from 'mongodb'; -import db from '../db/mongodb'; - -const Chart = db.get('chart'); -Chart.createIndex('date', { unique: true }); -export default Chart; - -export interface IChart { - _id: mongo.ObjectID; - - date: Date; - - /** - * ユーザーに関する統計 - */ - users: { - local: { - /** - * この日時点での、ローカルのユーザーの総計 - */ - total: number; - - /** - * ローカルのユーザー数の前日比 - */ - diff: number; - }; - - remote: { - /** - * この日時点での、リモートのユーザーの総計 - */ - total: number; - - /** - * リモートのユーザー数の前日比 - */ - diff: number; - }; - }; - - /** - * 投稿に関する統計 - */ - notes: { - local: { - /** - * この日時点での、ローカルの投稿の総計 - */ - total: number; - - /** - * ローカルの投稿数の前日比 - */ - diff: number; - - diffs: { - /** - * ローカルの通常の投稿数の前日比 - */ - normal: number; - - /** - * ローカルのリプライの投稿数の前日比 - */ - reply: number; - - /** - * ローカルのRenoteの投稿数の前日比 - */ - renote: number; - }; - }; - - remote: { - /** - * この日時点での、リモートの投稿の総計 - */ - total: number; - - /** - * リモートの投稿数の前日比 - */ - diff: number; - - diffs: { - /** - * リモートの通常の投稿数の前日比 - */ - normal: number; - - /** - * リモートのリプライの投稿数の前日比 - */ - reply: number; - - /** - * リモートのRenoteの投稿数の前日比 - */ - renote: number; - }; - }; - }; - - /** - * ドライブ(のファイル)に関する統計 - */ - drive: { - local: { - /** - * この日時点での、ローカルのドライブファイル数の総計 - */ - totalCount: number; - - /** - * この日時点での、ローカルのドライブファイルサイズの総計 - */ - totalSize: number; - - /** - * ローカルのドライブファイル数の前日比 - */ - diffCount: number; - - /** - * ローカルのドライブファイルサイズの前日比 - */ - diffSize: number; - }; - - remote: { - /** - * この日時点での、リモートのドライブファイル数の総計 - */ - totalCount: number; - - /** - * この日時点での、リモートのドライブファイルサイズの総計 - */ - totalSize: number; - - /** - * リモートのドライブファイル数の前日比 - */ - diffCount: number; - - /** - * リモートのドライブファイルサイズの前日比 - */ - diffSize: number; - }; - }; -} diff --git a/src/models/stats.ts b/src/models/stats.ts new file mode 100644 index 0000000000..7bff475c63 --- /dev/null +++ b/src/models/stats.ts @@ -0,0 +1,153 @@ +import * as mongo from 'mongodb'; +import db from '../db/mongodb'; + +const Stats = db.get('stats'); +Stats.createIndex({ date: -1 }, { unique: true }); +export default Stats; + +export interface IStats { + _id: mongo.ObjectID; + + date: Date; + + /** + * ユーザーに関する統計 + */ + users: { + local: { + /** + * この日時点での、ローカルのユーザーの総計 + */ + total: number; + + /** + * ローカルのユーザー数の前日比 + */ + diff: number; + }; + + remote: { + /** + * この日時点での、リモートのユーザーの総計 + */ + total: number; + + /** + * リモートのユーザー数の前日比 + */ + diff: number; + }; + }; + + /** + * 投稿に関する統計 + */ + notes: { + local: { + /** + * この日時点での、ローカルの投稿の総計 + */ + total: number; + + /** + * ローカルの投稿数の前日比 + */ + diff: number; + + diffs: { + /** + * ローカルの通常の投稿数の前日比 + */ + normal: number; + + /** + * ローカルのリプライの投稿数の前日比 + */ + reply: number; + + /** + * ローカルのRenoteの投稿数の前日比 + */ + renote: number; + }; + }; + + remote: { + /** + * この日時点での、リモートの投稿の総計 + */ + total: number; + + /** + * リモートの投稿数の前日比 + */ + diff: number; + + diffs: { + /** + * リモートの通常の投稿数の前日比 + */ + normal: number; + + /** + * リモートのリプライの投稿数の前日比 + */ + reply: number; + + /** + * リモートのRenoteの投稿数の前日比 + */ + renote: number; + }; + }; + }; + + /** + * ドライブ(のファイル)に関する統計 + */ + drive: { + local: { + /** + * この日時点での、ローカルのドライブファイル数の総計 + */ + totalCount: number; + + /** + * この日時点での、ローカルのドライブファイルサイズの総計 + */ + totalSize: number; + + /** + * ローカルのドライブファイル数の前日比 + */ + diffCount: number; + + /** + * ローカルのドライブファイルサイズの前日比 + */ + diffSize: number; + }; + + remote: { + /** + * この日時点での、リモートのドライブファイル数の総計 + */ + totalCount: number; + + /** + * この日時点での、リモートのドライブファイルサイズの総計 + */ + totalSize: number; + + /** + * リモートのドライブファイル数の前日比 + */ + diffCount: number; + + /** + * リモートのドライブファイルサイズの前日比 + */ + diffSize: number; + }; + }; +} diff --git a/src/server/api/endpoints/admin/chart.ts b/src/server/api/endpoints/admin/chart.ts new file mode 100644 index 0000000000..4ad29a7015 --- /dev/null +++ b/src/server/api/endpoints/admin/chart.ts @@ -0,0 +1,97 @@ +import Stats, { IStats } from '../../../../models/stats'; + +type Omit = Pick>; + +export const meta = { + requireCredential: true, + requireAdmin: true +}; + +export default (params: any) => new Promise(async (res, rej) => { + const now = new Date(); + const y = now.getFullYear(); + const m = now.getMonth(); + const d = now.getDate(); + + const stats = await Stats.find({ + date: { + $gt: new Date(y - 1, m, d) + } + }, { + sort: { + date: -1 + }, + fields: { + _id: 0 + } + }); + + const chart: Array> = []; + + for (let i = 364; i >= 0; i--) { + const day = new Date(y, m, d - i); + + const stat = stats.find(s => s.date.getTime() == day.getTime()); + + if (stat) { + chart.push(stat); + } else { // 隙間埋め + const mostRecent = stats.find(s => s.date.getTime() < day.getTime()); + if (mostRecent) { + chart.push(Object.assign({}, mostRecent, { + date: day + })); + } else { + chart.push({ + date: day, + users: { + local: { + total: 0, + diff: 0 + }, + remote: { + total: 0, + diff: 0 + } + }, + notes: { + local: { + total: 0, + diff: 0, + diffs: { + normal: 0, + reply: 0, + renote: 0 + } + }, + remote: { + total: 0, + diff: 0, + diffs: { + normal: 0, + reply: 0, + renote: 0 + } + } + }, + drive: { + local: { + totalCount: 0, + totalSize: 0, + diffCount: 0, + diffSize: 0 + }, + remote: { + totalCount: 0, + totalSize: 0, + diffCount: 0, + diffSize: 0 + } + } + }); + } + } + } + + res(chart); +}); diff --git a/src/server/api/endpoints/aggregation/notes.ts b/src/server/api/endpoints/aggregation/notes.ts deleted file mode 100644 index 77ed07ef4b..0000000000 --- a/src/server/api/endpoints/aggregation/notes.ts +++ /dev/null @@ -1,116 +0,0 @@ -import Note from '../../../../models/note'; - -export const meta = { - requireCredential: true, - requireAdmin: true -}; - -/** - * Aggregate notes - */ -export default (params: any) => new Promise(async (res, rej) => { - const query = [{ - $match: { - createdAt: { - $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) - } - } - }, { - $project: { - renoteId: '$renoteId', - replyId: '$replyId', - user: '$_user', - createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST - } - }, { - $project: { - date: { - year: { $year: '$createdAt' }, - month: { $month: '$createdAt' }, - day: { $dayOfMonth: '$createdAt' } - }, - type: { - $cond: { - if: { $ne: ['$renoteId', null] }, - then: 'renote', - else: { - $cond: { - if: { $ne: ['$replyId', null] }, - then: 'reply', - else: 'note' - } - } - } - }, - origin: { - $cond: { - if: { $eq: ['$user.host', null] }, - then: 'local', - else: 'remote' - } - } - } - }, { - $group: { - _id: { - date: '$date', - type: '$type', - origin: '$origin' - }, - count: { $sum: 1 } - } - }, { - $group: { - _id: '$_id.date', - data: { - $addToSet: { - type: '$_id.type', - origin: '$_id.origin', - count: '$count' - } - } - } - }] as any; - - const datas = await Note.aggregate(query); - - datas.forEach((data: any) => { - data.date = data._id; - delete data._id; - - data.localNotes = (data.data.filter((x: any) => x.type == 'note' && x.origin == 'local')[0] || { count: 0 }).count; - data.localRenotes = (data.data.filter((x: any) => x.type == 'renote' && x.origin == 'local')[0] || { count: 0 }).count; - data.localReplies = (data.data.filter((x: any) => x.type == 'reply' && x.origin == 'local')[0] || { count: 0 }).count; - data.remoteNotes = (data.data.filter((x: any) => x.type == 'note' && x.origin == 'remote')[0] || { count: 0 }).count; - data.remoteRenotes = (data.data.filter((x: any) => x.type == 'renote' && x.origin == 'remote')[0] || { count: 0 }).count; - data.remoteReplies = (data.data.filter((x: any) => x.type == 'reply' && x.origin == 'remote')[0] || { count: 0 }).count; - - delete data.data; - }); - - const graph = []; - - for (let i = 0; i < 365; i++) { - const day = new Date(new Date().setDate(new Date().getDate() - i)); - - const data = datas.filter((d: any) => - d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() - )[0]; - - if (data) { - graph.push(data); - } else { - graph.push({ - date: { year: day.getFullYear(), month: day.getMonth() + 1, day: day.getDate() }, - localNotes: 0, - localRenotes: 0, - localReplies: 0, - remoteNotes: 0, - remoteRenotes: 0, - remoteReplies: 0 - }); - } - } - - res(graph); -}); diff --git a/src/server/api/endpoints/aggregation/users.ts b/src/server/api/endpoints/aggregation/users.ts deleted file mode 100644 index d016484238..0000000000 --- a/src/server/api/endpoints/aggregation/users.ts +++ /dev/null @@ -1,92 +0,0 @@ -import User from '../../../../models/user'; - -export const meta = { - requireCredential: true, - requireAdmin: true -}; - -/** - * Aggregate users - */ -export default (params: any) => new Promise(async (res, rej) => { - const query = [{ - $match: { - createdAt: { - $gt: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) - } - } - }, { - $project: { - host: '$host', - createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST - } - }, { - $project: { - date: { - year: { $year: '$createdAt' }, - month: { $month: '$createdAt' }, - day: { $dayOfMonth: '$createdAt' } - }, - origin: { - $cond: { - if: { $eq: ['$host', null] }, - then: 'local', - else: 'remote' - } - } - } - }, { - $group: { - _id: { - date: '$date', - origin: '$origin' - }, - count: { $sum: 1 } - } - }, { - $group: { - _id: '$_id.date', - data: { - $addToSet: { - type: '$_id.type', - origin: '$_id.origin', - count: '$count' - } - } - } - }] as any; - - const datas = await User.aggregate(query); - - datas.forEach((data: any) => { - data.date = data._id; - delete data._id; - - data.local = (data.data.filter((x: any) => x.origin == 'local')[0] || { count: 0 }).count; - data.remote = (data.data.filter((x: any) => x.origin == 'remote')[0] || { count: 0 }).count; - - delete data.data; - }); - - const graph = []; - - for (let i = 0; i < 365; i++) { - const day = new Date(new Date().setDate(new Date().getDate() - i)); - - const data = datas.filter((d: any) => - d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate() - )[0]; - - if (data) { - graph.push(data); - } else { - graph.push({ - date: { year: day.getFullYear(), month: day.getMonth() + 1, day: day.getDate() }, - local: 0, - remote: 0 - }); - } - } - - res(graph); -}); diff --git a/src/services/update-chart.ts b/src/services/update-chart.ts index 9175d61f74..7998baca9d 100644 --- a/src/services/update-chart.ts +++ b/src/services/update-chart.ts @@ -1,11 +1,11 @@ import { INote } from '../models/note'; -import Chart, { IChart } from '../models/chart'; +import Stats, { IStats } from '../models/stats'; import { isLocalUser, IUser } from '../models/user'; import { IDriveFile } from '../models/drive-file'; type Omit = Pick>; -async function getTodayStats(): Promise { +async function getTodayStats(): Promise { const now = new Date(); const y = now.getFullYear(); const m = now.getMonth(); @@ -13,7 +13,7 @@ async function getTodayStats(): Promise { const today = new Date(y, m, d); // 今日の統計 - const todayStats = await Chart.findOne({ + const todayStats = await Stats.findOne({ date: today }); @@ -23,7 +23,7 @@ async function getTodayStats(): Promise { // * 昨日何もチャートを更新するような出来事がなかった場合は、 // 統計がそもそも作られずドキュメントが存在しないということがあり得るため、 // 「昨日の」と決め打ちせずに「もっとも最近の」とします - const mostRecentStats = await Chart.findOne({}, { + const mostRecentStats = await Stats.findOne({}, { sort: { date: -1 } @@ -33,7 +33,7 @@ async function getTodayStats(): Promise { // * Misskeyインスタンスを建てて初めてのチャート更新時など if (mostRecentStats == null) { // 空の統計を作成 - const chart: Omit = { + const chart: Omit = { date: today, users: { local: { @@ -81,12 +81,12 @@ async function getTodayStats(): Promise { } }; - const stats = await Chart.insert(chart); + const stats = await Stats.insert(chart); return stats; } else { // 今日の統計を初期挿入 - const chart: Omit = { + const chart: Omit = { date: today, users: { local: { @@ -134,7 +134,7 @@ async function getTodayStats(): Promise { } }; - const stats = await Chart.insert(chart); + const stats = await Stats.insert(chart); return stats; } @@ -146,7 +146,7 @@ async function getTodayStats(): Promise { async function update(inc: any) { const stats = await getTodayStats(); - await Chart.findOneAndUpdate({ + await Stats.findOneAndUpdate({ _id: stats._id }, { $inc: inc -- cgit v1.2.3-freya