Files
Nip-05-api/internal/user/repo_query.go
2026-04-29 02:35:00 +00:00

116 lines
3.1 KiB
Go

package user
import (
"context"
"time"
)
type ListFilter struct {
ActiveOnly bool
Search string
Limit int
}
func (r *Repo) List(ctx context.Context, f ListFilter) ([]*User, error) {
q := `SELECT ` + userCols + ` FROM users WHERE 1=1`
args := []any{}
if f.ActiveOnly {
q += ` AND is_active = 1`
}
if f.Search != "" {
q += ` AND (username LIKE ? COLLATE NOCASE OR pubkey LIKE ?)`
args = append(args, "%"+f.Search+"%", "%"+f.Search+"%")
}
q += ` ORDER BY created_at DESC`
if f.Limit > 0 {
q += ` LIMIT ?`
args = append(args, f.Limit)
}
rows, err := r.db.QueryContext(ctx, q, args...)
if err != nil {
return nil, err
}
return r.collect(rows)
}
func (r *Repo) ActiveByName(ctx context.Context) (map[string]string, error) {
rows, err := r.db.QueryContext(ctx, `SELECT username, pubkey FROM users WHERE is_active = 1`)
if err != nil {
return nil, err
}
defer rows.Close()
out := map[string]string{}
for rows.Next() {
var u, p string
if err := rows.Scan(&u, &p); err != nil {
return nil, err
}
out[u] = p
}
return out, rows.Err()
}
func (r *Repo) collect(rows interface {
Next() bool
Scan(...any) error
Err() error
Close() error
}) ([]*User, error) {
defer rows.Close()
out := []*User{}
for rows.Next() {
u, err := scanUser(rows)
if err != nil {
return nil, err
}
out = append(out, u)
}
return out, rows.Err()
}
func (r *Repo) ListPendingReminders(ctx context.Context, days int, now time.Time) ([]*User, error) {
low := now.Add(time.Duration(days)*24*time.Hour - 12*time.Hour).UTC().Format(time.RFC3339)
high := now.Add(time.Duration(days)*24*time.Hour + 12*time.Hour).UTC().Format(time.RFC3339)
rows, err := r.db.QueryContext(ctx, `SELECT `+userCols+` FROM users
WHERE is_active = 1
AND subscription_type = 'yearly'
AND expires_at BETWEEN ? AND ?
AND (expiring_reminder_sent_at IS NULL OR expiring_reminder_sent_at < ?)`,
low, high, now.Add(-23*time.Hour).UTC().Format(time.RFC3339))
if err != nil {
return nil, err
}
return r.collect(rows)
}
func (r *Repo) ListExpired(ctx context.Context, now time.Time) ([]*User, error) {
rows, err := r.db.QueryContext(ctx, `SELECT `+userCols+` FROM users
WHERE is_active = 1 AND subscription_type = 'yearly' AND expires_at < ?`,
now.UTC().Format(time.RFC3339))
if err != nil {
return nil, err
}
return r.collect(rows)
}
func (r *Repo) ListGraceExpired(ctx context.Context, cutoff time.Time) ([]*User, error) {
rows, err := r.db.QueryContext(ctx, `SELECT `+userCols+` FROM users
WHERE is_active = 0 AND deactivated_at IS NOT NULL AND deactivated_at < ?`,
cutoff.UTC().Format(time.RFC3339))
if err != nil {
return nil, err
}
return r.collect(rows)
}
func (r *Repo) ListForSync(ctx context.Context, staleBefore time.Time) ([]*User, error) {
rows, err := r.db.QueryContext(ctx, `SELECT `+userCols+` FROM users
WHERE is_active = 1 AND manual_username = 0
AND (last_synced_at IS NULL OR last_synced_at < ?)`,
staleBefore.UTC().Format(time.RFC3339))
if err != nil {
return nil, err
}
return r.collect(rows)
}