From de460720da468972a2b0d8f24b0a4c5ed401e92b Mon Sep 17 00:00:00 2001
From: Sam
Date: Sat, 6 May 2023 15:59:52 +0200
Subject: [PATCH] feat: expose active user counts in API
---
backend/db/metrics.go | 40 +++++++++++++++++++++++++++---
backend/routes/meta/meta.go | 40 +++++++++++++++++++++++++-----
frontend/src/lib/api/responses.ts | 9 ++++++-
frontend/src/routes/+layout.svelte | 2 +-
4 files changed, 79 insertions(+), 12 deletions(-)
diff --git a/backend/db/metrics.go b/backend/db/metrics.go
index c558cee..d658310 100644
--- a/backend/db/metrics.go
+++ b/backend/db/metrics.go
@@ -45,7 +45,35 @@ func (db *DB) initMetrics() (err error) {
Name: "pronouns_users_active",
Help: "The number of users active in the past 30 days",
}, func() float64 {
- count, err := db.ActiveUsers(context.Background())
+ count, err := db.ActiveUsers(context.Background(), ActiveMonth)
+ if err != nil {
+ log.Errorf("getting active user count for metrics: %v", err)
+ }
+ return float64(count)
+ }))
+ if err != nil {
+ return errors.Wrap(err, "registering active user count gauge")
+ }
+
+ err = prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
+ Name: "pronouns_users_active_week",
+ Help: "The number of users active in the past 7 days",
+ }, func() float64 {
+ count, err := db.ActiveUsers(context.Background(), ActiveWeek)
+ if err != nil {
+ log.Errorf("getting active user count for metrics: %v", err)
+ }
+ return float64(count)
+ }))
+ if err != nil {
+ return errors.Wrap(err, "registering active user count gauge")
+ }
+
+ err = prometheus.Register(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
+ Name: "pronouns_users_active_day",
+ Help: "The number of users active in the past 1 day",
+ }, func() float64 {
+ count, err := db.ActiveUsers(context.Background(), ActiveDay)
if err != nil {
log.Errorf("getting active user count for metrics: %v", err)
}
@@ -95,10 +123,14 @@ func (db *DB) TotalMemberCount(ctx context.Context) (numMembers int64, err error
return numMembers, nil
}
-const activeTime = 30 * 24 * time.Hour
+const (
+ ActiveMonth = 30 * 24 * time.Hour
+ ActiveWeek = 7 * 24 * time.Hour
+ ActiveDay = 24 * time.Hour
+)
-func (db *DB) ActiveUsers(ctx context.Context) (numUsers int64, err error) {
- t := time.Now().Add(-activeTime)
+func (db *DB) ActiveUsers(ctx context.Context, dur time.Duration) (numUsers int64, err error) {
+ t := time.Now().Add(-dur)
err = db.QueryRow(ctx, "SELECT COUNT(*) FROM users WHERE deleted_at IS NULL AND last_active > $1", t).Scan(&numUsers)
if err != nil {
return 0, errors.Wrap(err, "querying active user count")
diff --git a/backend/routes/meta/meta.go b/backend/routes/meta/meta.go
index 7b6c26e..45b6b6f 100644
--- a/backend/routes/meta/meta.go
+++ b/backend/routes/meta/meta.go
@@ -4,6 +4,7 @@ import (
"net/http"
"os"
+ "codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/server"
"emperror.dev/errors"
"github.com/go-chi/chi/v5"
@@ -21,11 +22,18 @@ func Mount(srv *server.Server, r chi.Router) {
}
type MetaResponse struct {
- GitRepository string `json:"git_repository"`
- GitCommit string `json:"git_commit"`
- Users int64 `json:"users"`
- Members int64 `json:"members"`
- RequireInvite bool `json:"require_invite"`
+ GitRepository string `json:"git_repository"`
+ GitCommit string `json:"git_commit"`
+ Users MetaUsers `json:"users"`
+ Members int64 `json:"members"`
+ RequireInvite bool `json:"require_invite"`
+}
+
+type MetaUsers struct {
+ Total int64 `json:"total"`
+ ActiveMonth int64 `json:"active_month"`
+ ActiveWeek int64 `json:"active_week"`
+ ActiveDay int64 `json:"active_day"`
}
func (s *Server) meta(w http.ResponseWriter, r *http.Request) error {
@@ -36,6 +44,21 @@ func (s *Server) meta(w http.ResponseWriter, r *http.Request) error {
return errors.Wrap(err, "querying user count")
}
+ activeMonth, err := s.DB.ActiveUsers(ctx, db.ActiveMonth)
+ if err != nil {
+ return errors.Wrap(err, "querying user count")
+ }
+
+ activeWeek, err := s.DB.ActiveUsers(ctx, db.ActiveWeek)
+ if err != nil {
+ return errors.Wrap(err, "querying user count")
+ }
+
+ activeDay, err := s.DB.ActiveUsers(ctx, db.ActiveDay)
+ if err != nil {
+ return errors.Wrap(err, "querying user count")
+ }
+
numMembers, err := s.DB.TotalMemberCount(ctx)
if err != nil {
return errors.Wrap(err, "querying user count")
@@ -44,7 +67,12 @@ func (s *Server) meta(w http.ResponseWriter, r *http.Request) error {
render.JSON(w, r, MetaResponse{
GitRepository: server.Repository,
GitCommit: server.Revision,
- Users: numUsers,
+ Users: MetaUsers{
+ Total: numUsers,
+ ActiveMonth: activeMonth,
+ ActiveWeek: activeWeek,
+ ActiveDay: activeDay,
+ },
Members: numMembers,
RequireInvite: os.Getenv("REQUIRE_INVITE") == "true",
})
diff --git a/frontend/src/lib/api/responses.ts b/frontend/src/lib/api/responses.ts
index 9af4647..5a343f8 100644
--- a/frontend/src/lib/api/responses.ts
+++ b/frontend/src/lib/api/responses.ts
@@ -8,11 +8,18 @@ export interface SignupResponse {
export interface MetaResponse {
git_repository: string;
git_commit: string;
- users: number;
+ users: MetaUsers;
members: number;
require_invite: boolean;
}
+export interface MetaUsers {
+ total: number;
+ active_month: number;
+ active_week: number;
+ active_day: number;
+}
+
export interface UrlsResponse {
discord?: string;
tumblr?: string;
diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte
index 0e13ce1..3ec6937 100644
--- a/frontend/src/routes/+layout.svelte
+++ b/frontend/src/routes/+layout.svelte
@@ -61,7 +61,7 @@
Privacy policy
- Users: {data.users} · Members: {data.members}
+ Users: {data.users.total} · Members: {data.members}