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}