From c49ad4d5332e12fc875245883dfb58954a38086e Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Sun, 8 Jan 2023 12:06:19 -0500 Subject: [PATCH] Expose public instance stats from the API --- graphql/resolvers/index.js | 3 ++ graphql/resolvers/statsResolver.js | 58 ++++++++++++++++++++++++++++++ graphql/schema.js | 35 ++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 graphql/resolvers/statsResolver.js diff --git a/graphql/resolvers/index.js b/graphql/resolvers/index.js index 31d0943a7..71c3c667f 100644 --- a/graphql/resolvers/index.js +++ b/graphql/resolvers/index.js @@ -3,6 +3,7 @@ const { Article, Query: ArticleQuery, Mutation: ArticleMutation } = require('./a const { Tag, Query: TagQuery, Mutation: TagMutation } = require('./tagResolver') const { Version, Query: VersionQuery, Mutation: VersionMutation } = require('./versionResolver') const { Mutation: AuthMutation } = require('./authResolver') +const { InstanceUsageStats, Query: StatsQuery } = require('./statsResolver') const { EmailAddressResolver, JWTResolver, HexColorCodeResolver, DateTimeResolver } = require('graphql-scalars') module.exports = { @@ -17,12 +18,14 @@ module.exports = { Article, Tag, Version, + InstanceUsageStats, // Root queries & mutations Query: { ...UserQuery, ...ArticleQuery, ...TagQuery, ...VersionQuery, + ...StatsQuery, }, Mutation: { ...UserMutation, diff --git a/graphql/resolvers/statsResolver.js b/graphql/resolvers/statsResolver.js new file mode 100644 index 000000000..4e57b7ad6 --- /dev/null +++ b/graphql/resolvers/statsResolver.js @@ -0,0 +1,58 @@ + +const Article = require('../models/article.js') +const User = require('../models/user.js') + +// incorrect, because we also inject it at runtime, based on Git tag +const { version } = require('../package.json') + +module.exports = { + Query: { + async stats () { + return { + version + } + } + }, + + InstanceUsageStats: { + async articles () { + const [total, years] = await Promise.all([ + Article.estimatedDocumentCount(), + Article.aggregate([ + { $project: { createdYear: {$year: "$createdAt"} } }, + { $group: { _id: '$createdYear', count: {$sum: 1} } } + ]) + ]) + + return { + total, + years: years.map(({ _id: year, count }) => ({ year, count })) + } + }, + + async users () { + const [total, [{count: local}], [{count: openid}], years] = await Promise.all([ + User.estimatedDocumentCount(), + User.aggregate([ + { $match:{ authType: "local"} }, + { $count: 'count'} + ]), + User.aggregate([ + { $match:{ authType: { $ne : "local" }} }, + { $count: 'count'} + ]), + User.aggregate([ + { $project: { createdYear: {$year: "$createdAt"} } }, + { $group: { _id: '$createdYear', count: {$sum: 1} } } + ]) + ]) + + return { + total, + local, + openid, + years: years.map(({ _id: year, count }) => ({ year, count })) + } + } + } +} diff --git a/graphql/schema.js b/graphql/schema.js index 8d09b78ec..ca204e9ea 100644 --- a/graphql/schema.js +++ b/graphql/schema.js @@ -93,6 +93,38 @@ type ArticleContributor { roles: [String] } +type InstanceUsageStats { + version: String + users: InstanceUserStats + articles: InstanceArticleStats +} + +interface InstanceObjectUsageStats { + total: Int! + # currentYear: Int! + years: [InstanceObjectUsageYearlyStats] +} + +type InstanceObjectUsageYearlyStats { + year: Int + count: Int + # variation: Int +} + +type InstanceUserStats implements InstanceObjectUsageStats { + total: Int! + # currentYear: Int! + local: Int + openid: Int + years: [InstanceObjectUsageYearlyStats] +} + +type InstanceArticleStats implements InstanceObjectUsageStats { + total: Int! + # currentYear: Int! + years: [InstanceObjectUsageYearlyStats] +} + input VersionInput { article: ID! major: Boolean @@ -150,6 +182,9 @@ type Query { "Fetch version info" version(version: ID!): Version + + "Fetch instance stats" + stats: InstanceUsageStats } type Mutation {