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 }