Files

127 lines
3.5 KiB
Go
Raw Permalink Normal View History

package support
import (
"context"
"errors"
"fmt"
"github.com/descrybe/descrybe-v2/apps/api/internal/auth"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
// ListAgents returns support_staff users and optionally full-admin staff.
func (s *Service) ListAgents(ctx context.Context, includePlatformAdmins bool, limit, offset int) ([]SupportAgent, int64, error) {
limit, offset = clampListBounds(limit, offset)
where := `(staff_role = 'support_staff'`
if includePlatformAdmins {
where += ` OR staff_role IN ('admin','developer') OR (is_platform_admin = true AND (staff_role IS NULL OR staff_role = ''))`
}
where += `) AND is_active = true`
var total int64
if err := s.Pool.QueryRow(ctx, `SELECT count(*) FROM users WHERE `+where).Scan(&total); err != nil {
if isUndefinedColumn(err) {
return []SupportAgent{}, 0, nil
}
return nil, 0, err
}
q := fmt.Sprintf(`
SELECT id, email, COALESCE(name, ''), is_platform_admin, COALESCE(staff_role, ''), is_active
FROM users
WHERE %s
ORDER BY email ASC
LIMIT $1 OFFSET $2`, where)
rows, err := s.Pool.Query(ctx, q, limit, offset)
if err != nil {
if isUndefinedColumn(err) {
return []SupportAgent{}, 0, nil
}
return nil, 0, err
}
defer rows.Close()
out := make([]SupportAgent, 0, limit)
for rows.Next() {
var a SupportAgent
var role string
if err := rows.Scan(&a.ID, &a.Email, &a.Name, &a.IsPlatformAdmin, &role, &a.IsActive); err != nil {
return nil, 0, err
}
a.StaffRole = role
access := auth.ResolveStaffAccess(a.IsPlatformAdmin, role)
a.IsSupportAgent = access.SupportDesk
a.IsPlatformAdmin = access.FullAdmin
out = append(out, a)
}
return out, total, rows.Err()
}
// SetSupportAgent grants or revokes staff_role=support_staff (does not grant full admin).
func (s *Service) SetSupportAgent(ctx context.Context, userID uuid.UUID, enable bool) (SupportAgent, error) {
var email, name string
var isAdmin, isActive bool
var staffRole *string
err := s.Pool.QueryRow(ctx, `
SELECT email, COALESCE(name, ''), is_platform_admin, staff_role, is_active
FROM users WHERE id = $1`, userID,
).Scan(&email, &name, &isAdmin, &staffRole, &isActive)
if errors.Is(err, pgx.ErrNoRows) {
return SupportAgent{}, ErrNotFound
}
if err != nil {
return SupportAgent{}, err
}
role := ""
if staffRole != nil {
role = *staffRole
}
access := auth.ResolveStaffAccess(isAdmin, role)
if enable {
if access.FullAdmin {
// Already has desk via admin/developer — leave role unchanged.
} else {
_, err = s.Pool.Exec(ctx, `
UPDATE users
SET staff_role = $2, is_platform_admin = true, updated_at = now()
WHERE id = $1`, userID, auth.StaffRoleSupportStaff)
if err != nil {
return SupportAgent{}, err
}
role = auth.StaffRoleSupportStaff
isAdmin = true
}
} else {
if role == auth.StaffRoleSupportStaff {
_, err = s.Pool.Exec(ctx, `
UPDATE users
SET staff_role = NULL, is_platform_admin = false, updated_at = now()
WHERE id = $1`, userID)
if err != nil {
return SupportAgent{}, err
}
role = ""
isAdmin = false
}
// Do not demote admin/developer via this endpoint.
}
access = auth.ResolveStaffAccess(isAdmin, role)
return SupportAgent{
ID: userID,
Email: email,
Name: name,
IsSupportAgent: access.SupportDesk,
IsPlatformAdmin: access.FullAdmin,
StaffRole: role,
IsActive: isActive,
}, nil
}
func isUndefinedColumn(err error) bool {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
return pgErr.Code == "42703"
}
return false
}