127 lines
3.5 KiB
Go
127 lines
3.5 KiB
Go
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
|
||
|
|
}
|