internal/store/quotas.go

e6cd75b5f28bacf51620bb531320c30fd4e66bfd
gitbay/internal/store/quotas.go history · blame · raw

105 lines · 2887 bytes

7 symbols in this file
  1package store
  2
  3import (
  4	"database/sql"
  5	"time"
  6)
  7
  8// Limits is an owner's quota overrides; nil means the configured default
  9// applies. Orgs is the account's cap on organizations it creates and is
 10// always nil for an org.
 11type Limits struct {
 12	Repos *int64
 13	Bytes *int64
 14	Orgs  *int64
 15}
 16
 17func nullable(n sql.NullInt64) *int64 {
 18	if !n.Valid {
 19		return nil
 20	}
 21	return &n.Int64
 22}
 23
 24func orNull(p *int64) any {
 25	if p == nil {
 26		return nil
 27	}
 28	return *p
 29}
 30
 31// OwnerLimits reads the overrides of a user or an org.
 32func (s *Store) OwnerLimits(kind string, id int64) (Limits, error) {
 33	var repos, bytes, orgs sql.NullInt64
 34	var err error
 35	if kind == "org" {
 36		err = s.DB.QueryRow("SELECT repo_limit, byte_limit FROM orgs WHERE id = ?", id).Scan(&repos, &bytes)
 37	} else {
 38		err = s.DB.QueryRow("SELECT repo_limit, byte_limit, org_limit FROM users WHERE id = ?", id).Scan(&repos, &bytes, &orgs)
 39	}
 40	if err != nil {
 41		return Limits{}, err
 42	}
 43	return Limits{nullable(repos), nullable(bytes), nullable(orgs)}, nil
 44}
 45
 46// SetOwnerLimits writes the overrides; a nil field clears back to default.
 47func (s *Store) SetOwnerLimits(kind string, id int64, l Limits) error {
 48	var res sql.Result
 49	var err error
 50	if kind == "org" {
 51		res, err = s.DB.Exec("UPDATE orgs SET repo_limit = ?, byte_limit = ? WHERE id = ?", orNull(l.Repos), orNull(l.Bytes), id)
 52	} else {
 53		res, err = s.DB.Exec("UPDATE users SET repo_limit = ?, byte_limit = ?, org_limit = ? WHERE id = ?",
 54			orNull(l.Repos), orNull(l.Bytes), orNull(l.Orgs), id)
 55	}
 56	if err != nil {
 57		return err
 58	}
 59	if n, _ := res.RowsAffected(); n == 0 {
 60		return ErrNotFound
 61	}
 62	return nil
 63}
 64
 65// CreatedOrgCount counts the organizations an account created and that
 66// still exist.
 67func (s *Store) CreatedOrgCount(userID int64) (int64, error) {
 68	var n int64
 69	err := s.DB.QueryRow("SELECT COUNT(*) FROM orgs WHERE created_by = ?", userID).Scan(&n)
 70	return n, err
 71}
 72
 73// ReapPendingUsers deletes self-registered accounts still unverified
 74// after maxAge. A pending account owns nothing (it cannot create a
 75// repository before verifying), so DeleteUser has nothing to refuse; an
 76// account that somehow anchors content is left alone and reported.
 77func (s *Store) ReapPendingUsers(maxAge time.Duration) ([]string, error) {
 78	cutoff := fmtTime(time.Now().Add(-maxAge))
 79	rows, err := s.DB.Query("SELECT id, username FROM users WHERE pending = 1 AND created_at < ?", cutoff)
 80	if err != nil {
 81		return nil, err
 82	}
 83	type row struct {
 84		id   int64
 85		name string
 86	}
 87	var stale []row
 88	for rows.Next() {
 89		var r row
 90		if err := rows.Scan(&r.id, &r.name); err != nil {
 91			rows.Close()
 92			return nil, err
 93		}
 94		stale = append(stale, r)
 95	}
 96	rows.Close()
 97	var removed []string
 98	for _, r := range stale {
 99		if err := s.DeleteUser(r.id); err == nil {
100			removed = append(removed, r.name)
101			s.Audit(0, "pending.expired", map[string]any{"user": r.name})
102		}
103	}
104	return removed, nil
105}