Skip to content

Commit

Permalink
Add admin stats route
Browse files Browse the repository at this point in the history
Add some super basic usage stats
  • Loading branch information
bcomnes committed Jul 9, 2023
1 parent ab75528 commit 315ddcd
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 1 deletion.
2 changes: 1 addition & 1 deletion routes/api/admin/flags/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAdminFlags } from './get-admin-flags.js'
import { putAdminFlags } from './put-admin-flags.js'

export default async function bookmarksRoutes (fastify, opts) {
export default async function adminFlagsRoutes (fastify, opts) {
await Promise.all([
getAdminFlags(fastify, opts),
putAdminFlags(fastify, opts)
Expand Down
62 changes: 62 additions & 0 deletions routes/api/admin/stats/get-admin-stats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import SQL from '@nearform/sql'

export async function getAdminStats (fastify, opts) {
fastify.get(
'/',
{
preHandler: fastify.auth([
fastify.verifyJWT,
fastify.verifyAdmin
], {
relation: 'and'
}),
schema: {
hide: true
}
},
// Get admin flags
async function getAdminFlagsHandler (request, reply) {
const monthBookmarkCountQuery = SQL`
select u.id, u.username, u.email, count(*) as bookmark_count
from users u
left join bookmarks b
on u.id = b.owner_id
where b.created_at >= NOW() - INTERVAL '1 month'
group by u.id, u.username, u.email
order by bookmark_count desc;
`

const monthBookmarkCountResults = await fastify.pg.query(monthBookmarkCountQuery)

const totalCountQuery = SQL`
select count(*) as bookmark_count
from bookmarks b;
`

const totalCountResults = await fastify.pg.query(totalCountQuery)

const totalUsersQuery = SQL`
select count(*) as users_count
from users b;
`

const totalUsersQueryResults = await fastify.pg.query(totalUsersQuery)

const recentUsersQuery = SQL`
select u.id, u.username, u.email
from users u
where u.created_at >= NOW() - INTERVAL '3 month'
order by created_at desc;
`

const recentUsersResults = await fastify.pg.query(recentUsersQuery)

return {
totalUsers: totalUsersQueryResults.rows,
totalBookmarks: totalCountResults.rows,
recentUsers: recentUsersResults.rows,
bookmarkStats: monthBookmarkCountResults.rows
}
}
)
}
7 changes: 7 additions & 0 deletions routes/api/admin/stats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getAdminStats } from './get-admin-stats.js'

export default async function adminStatsRoutes (fastify, opts) {
await Promise.all([
getAdminStats(fastify, opts)
])
}
1 change: 1 addition & 0 deletions web/admin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ title: "👨‍💼 Admin Pages"
# Admin pannel

- [Flags](./flags/)
- [Stats](./stats/)
60 changes: 60 additions & 0 deletions web/admin/stats/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint-env browser */
import { Component, html, render, useEffect, useState } from 'uland-isomorphic'
import { useUser } from '../../hooks/useUser.js'
import { useLSP } from '../../hooks/useLSP.js'

export const page = Component(() => {
const state = useLSP()
const { user, loading } = useUser()

useEffect(() => {
if (!user && !loading) {
const redirectTarget = `${window.location.pathname}${window.location.search}`
window.location.replace(`/login?redirect=${encodeURIComponent(redirectTarget)}`)
}
}, [user])

const [stats, setStats] = useState()
const [statsLoading, setStatsLoading] = useState(false)
const [statsError, setStatsError] = useState(false)

useEffect(() => {
async function getStats () {
setStatsLoading(true)
setStatsError(null)

const response = await fetch(`${state.apiUrl}/admin/stats`, {
method: 'get',
headers: {
'accept-encoding': 'application/json'
}
})

if (response.ok && response.headers.get('content-type')?.includes('application/json')) {
const body = await response.json()
setStats(body)
} else {
throw new Error(`${response.status} ${response.statusText}: ${await response.text()}`)
}
}

if (user) {
getStats()
.then(() => { console.log('stats done') })
.catch(err => { console.error(err); setStatsError(err) })
.finally(() => { setStatsLoading(false) })
}
}, [state.apiUrl])

return html`
<div class="bc-admin-stats">
${statsLoading ? html`<p>loading...</p>` : null}
${stats ? html`<pre><code>${JSON.stringify(stats, null, ' ')}</code></pre>` : null}
${statsError ? html`<p>${statsError.message}</p>` : null}
</div>
`
})

if (typeof window !== 'undefined') {
render(document.querySelector('.bc-main'), page)
}
11 changes: 11 additions & 0 deletions web/admin/stats/client.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import t from 'tap'
import { page } from './client.js'
import { render } from 'uland-isomorphic'

t.test('Testing is set up and working', async t => {
let rendered
t.doesNotThrow(() => {
rendered = render(String, page)
}, 'page renders without error')
t.equal(typeof rendered, 'string', 'page renders to string')
})
6 changes: 6 additions & 0 deletions web/admin/stats/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { html } from 'uland-isomorphic'
import { page } from './client.js'

export default () => {
return html`${page()}`
}
3 changes: 3 additions & 0 deletions web/admin/stats/page.vars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
title: '📈 Stats'
}

0 comments on commit 315ddcd

Please sign in to comment.