mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-03 20:13:00 +00:00
141 lines
3.9 KiB
Go
141 lines
3.9 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type DB struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func New(databaseURL string) (*DB, error) {
|
|
config, err := pgxpool.ParseConfig(databaseURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse database config: %w", err)
|
|
}
|
|
|
|
pool, err := pgxpool.NewWithConfig(context.Background(), config)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create pool: %w", err)
|
|
}
|
|
|
|
if err := pool.Ping(context.Background()); err != nil {
|
|
return nil, fmt.Errorf("ping database: %w", err)
|
|
}
|
|
|
|
return &DB{pool: pool}, nil
|
|
}
|
|
|
|
func (db *DB) Close() {
|
|
db.pool.Close()
|
|
}
|
|
|
|
func (db *DB) Pool() *pgxpool.Pool {
|
|
return db.pool
|
|
}
|
|
|
|
func (db *DB) QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row {
|
|
return db.pool.QueryRow(ctx, sql, args...)
|
|
}
|
|
|
|
func (db *DB) Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error) {
|
|
return db.pool.Query(ctx, sql, args...)
|
|
}
|
|
|
|
func (db *DB) Exec(ctx context.Context, sql string, args ...interface{}) error {
|
|
_, err := db.pool.Exec(ctx, sql, args...)
|
|
return err
|
|
}
|
|
|
|
// Stats contains database statistics for the admin dashboard
|
|
type Stats struct {
|
|
TotalUsers int64 `json:"totalUsers"`
|
|
UsersToday int64 `json:"usersToday"`
|
|
UsersThisWeek int64 `json:"usersThisWeek"`
|
|
UsersThisMonth int64 `json:"usersThisMonth"`
|
|
ActiveUsers7Days int64 `json:"activeUsers7Days"`
|
|
ActiveUsers30Days int64 `json:"activeUsers30Days"`
|
|
MagicLinksSent int64 `json:"magicLinksSent"`
|
|
MagicLinksUsed int64 `json:"magicLinksUsed"`
|
|
MagicLinksPending int64 `json:"magicLinksPending"`
|
|
OAuthUsers int64 `json:"oauthUsers"`
|
|
PasswordUsers int64 `json:"passwordUsers"`
|
|
}
|
|
|
|
// GetStats returns database statistics for the admin dashboard
|
|
func (db *DB) GetStats(ctx context.Context) (*Stats, error) {
|
|
stats := &Stats{}
|
|
|
|
// Total users
|
|
err := db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users`).Scan(&stats.TotalUsers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Users created today
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE created_at >= CURRENT_DATE`).Scan(&stats.UsersToday)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Users created this week
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE created_at >= CURRENT_DATE - INTERVAL '7 days'`).Scan(&stats.UsersThisWeek)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Users created this month
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'`).Scan(&stats.UsersThisMonth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Active users (logged in) in last 7 days
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE last_login_at >= CURRENT_DATE - INTERVAL '7 days'`).Scan(&stats.ActiveUsers7Days)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Active users in last 30 days
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE last_login_at >= CURRENT_DATE - INTERVAL '30 days'`).Scan(&stats.ActiveUsers30Days)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Magic links sent (total)
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM magic_links`).Scan(&stats.MagicLinksSent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Magic links used
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM magic_links WHERE used = TRUE`).Scan(&stats.MagicLinksUsed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Pending magic links
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM magic_links WHERE used = FALSE AND expires_at > NOW()`).Scan(&stats.MagicLinksPending)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// OAuth users
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE provider != 'email'`).Scan(&stats.OAuthUsers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Password users
|
|
err = db.pool.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE password_hash IS NOT NULL`).Scan(&stats.PasswordUsers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return stats, nil
|
|
}
|