From 073612a10f876245089a78bb067ffd403b47c5e7 Mon Sep 17 00:00:00 2001 From: Andrea Vos Date: Wed, 25 Nov 2020 00:54:02 +0100 Subject: [PATCH] #101 stats - server --- nuxt.config.js | 9 ++------ routes/profile.vue | 7 +++++- routes/profileEditor.vue | 7 +++++- server/index.js | 2 ++ server/routes/admin.js | 48 +++++++++++++++++++++++++++++++++++++++- src/helpers.js | 27 ++++++++++++++++++++++ 6 files changed, 90 insertions(+), 10 deletions(-) diff --git a/nuxt.config.js b/nuxt.config.js index 4ed682f91..d2768de88 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -1,17 +1,12 @@ import { loadSuml } from './server/loader'; import fs from 'fs'; -import {buildDict} from "./src/helpers"; +import {buildDict, buildLocaleList} from "./src/helpers"; const config = loadSuml('config'); const translations = loadSuml('translations'); const locale = config.locale; -const locales = buildDict(function* () { - for (let locale of process.env.LOCALES.split('|')) { - const [code, name, url] = locale.split(','); - yield [code, {name, url}]; - } -}); +const locales = buildLocaleList(); const title = translations.title; const description = translations.description; const banner = process.env.BASE_URL + '/api/banner/zaimki.png'; diff --git a/routes/profile.vue b/routes/profile.vue index a8eb107cf..6b3885172 100644 --- a/routes/profile.vue +++ b/routes/profile.vue @@ -145,7 +145,12 @@ for (let pronoun in this.profile.pronouns) { if (!this.profile.pronouns.hasOwnProperty(pronoun)) { continue; } - const link = decodeURIComponent(pronoun.replace(new RegExp('^' + this.$base), '').replace(new RegExp('^/'), '')); + const link = decodeURIComponent( + pronoun + .replace(new RegExp('^' + this.$base), '') + .replace(new RegExp('^' + this.$base.replace(/^https?:\/\//, '')), '') + .replace(new RegExp('^/'), '') + ); const pronounEntity = buildPronoun(pronouns, link); if (pronounEntity) { diff --git a/routes/profileEditor.vue b/routes/profileEditor.vue index ab6e900cb..3161643b0 100644 --- a/routes/profileEditor.vue +++ b/routes/profileEditor.vue @@ -189,7 +189,12 @@ this.$router.push(`/@${this.$user().username}`) }, validatePronoun(pronoun) { - const link = decodeURIComponent(pronoun.replace(new RegExp('^' + this.$base), '').replace(new RegExp('^/'), '')); + const link = decodeURIComponent( + pronoun + .replace(new RegExp('^' + this.$base), '') + .replace(new RegExp('^' + this.$base.replace(/^https?:\/\//, '')), '') + .replace(new RegExp('^/'), '') + ); return buildPronoun(pronouns, link) ? null : 'profile.pronounsNotFound' }, diff --git a/server/index.js b/server/index.js index f67126e03..ff3e254a6 100644 --- a/server/index.js +++ b/server/index.js @@ -6,6 +6,7 @@ import cookieParser from 'cookie-parser'; import grant from "grant"; import router from "./routes/user"; import { loadSuml } from './loader'; +import {buildLocaleList} from "../src/helpers"; const app = express() @@ -21,6 +22,7 @@ app.use(session({ app.use(async function (req, res, next) { req.config = loadSuml('config'); + req.locales = buildLocaleList(); req.rawUser = authenticate(req); req.user = req.rawUser && req.rawUser.authenticated ? req.rawUser : null; req.admin = req.user && req.user.roles === 'admin'; diff --git a/server/routes/admin.js b/server/routes/admin.js index eea30a462..24d37753d 100644 --- a/server/routes/admin.js +++ b/server/routes/admin.js @@ -2,7 +2,7 @@ import { Router } from 'express'; import SQL from 'sql-template-strings'; import avatar from '../avatar'; import {config as socialLoginConfig} from "../social"; -import {now} from "../../src/helpers"; +import {now, sortByValue} from "../../src/helpers"; const router = Router(); @@ -46,4 +46,50 @@ router.get('/admin/users', async (req, res) => { return res.json(groupedUsers); }); +router.get('/admin/stats', async (req, res) => { + if (!req.admin) { + return res.status(401).json({error: 'Unauthorised'}); + } + + const users = { + overall: (await req.db.get(SQL`SELECT count(*) AS c FROM users`)).c, + admins: (await req.db.get(SQL`SELECT count(*) AS c FROM users WHERE roles=${'admin'}`)).c, + }; + + const locales = {}; + for (let locale in req.locales) { + if (!req.locales.hasOwnProperty(locale)) { continue; } + const profiles = await req.db.all(SQL`SELECT pronouns FROM profiles WHERE locale=${locale}`); + const pronouns = {} + for (let profile of profiles) { + const pr = JSON.parse(profile.pronouns); + for (let pronoun in pr) { + if (!pr.hasOwnProperty(pronoun)) { continue; } + + if (pronoun.includes(',') || pr[pronoun] < 0) { + continue; + } + const p = pronoun.replace(/^.*:\/\//, '').replace(/^\//, '').toLowerCase().replace(/^[a-z]+\.[^/]+\//, ''); + if (pronouns[p] === undefined) { + pronouns[p] = 0; + } + pronouns[p]++; + } + } + + locales[locale] = { + name: req.locales[locale].name, + url: req.locales[locale].url, + profiles: profiles.length, + pronouns: sortByValue(pronouns, true), + nouns: { + approved: (await req.db.get(SQL`SELECT count(*) AS c FROM nouns WHERE locale=${locale} AND approved=1`)).c, + awaiting: (await req.db.get(SQL`SELECT count(*) AS c FROM nouns WHERE locale=${locale} AND approved=0`)).c, + } + }; + } + + return res.json({ users, locales }); +}); + export default router; diff --git a/src/helpers.js b/src/helpers.js index e1820dac5..36b9a936b 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -131,3 +131,30 @@ export const now = function () { export const isEmoji = char => { return !!char.match(/^(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])$/) } + +export const buildLocaleList = () => { + return buildDict(function* () { + for (let locale of process.env.LOCALES.split('|')) { + const [code, name, url] = locale.split(','); + yield [code, {name, url}]; + } + }) +} + +export const zip = (list, reverse) => { + return buildDict(function* () { + for (let [k, v] of list) { + yield reverse ? [v, k] : [k, v]; + } + }); +} + +export const sortByValue = (obj, reverse = false) => { + const sortedArray = []; + for (let i in obj) { + if (obj.hasOwnProperty(i)) { + sortedArray.push([parseInt(obj[i]), i]); + } + } + return zip(sortedArray.sort((a, b) => reverse ? b[0] - a[0] : a[0] - b[0]), true); +}