chore: update admin apis
This commit is contained in:
@@ -19,6 +19,11 @@ type AdminUser struct {
|
||||
BannedAt *time.Time `json:"banned_at"`
|
||||
PostCount int `json:"post_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// Connected auth methods. HasPassword means email+password login is set up;
|
||||
// Providers lists linked OAuth providers (e.g. "google", "facebook").
|
||||
HasPassword bool `json:"has_password"`
|
||||
Providers []string `json:"providers"`
|
||||
}
|
||||
|
||||
// AdminPost is a row in the admin posts table.
|
||||
|
||||
@@ -37,6 +37,9 @@ func (r *AdminRepo) ListUsers(ctx context.Context, q string, limit, offset int)
|
||||
u.email_verified_at, u.banned_at,
|
||||
(SELECT COUNT(*) FROM posts p WHERE p.user_id = u.id AND p.deleted_at IS NULL) AS post_count,
|
||||
u.created_at,
|
||||
(u.password_hash IS NOT NULL) AS has_password,
|
||||
(SELECT COALESCE(array_agg(oa.provider ORDER BY oa.provider), ARRAY[]::text[])
|
||||
FROM oauth_accounts oa WHERE oa.user_id = u.id) AS providers,
|
||||
COUNT(*) OVER() AS total
|
||||
FROM users u
|
||||
WHERE ($1 = '' OR u.name ILIKE '%'||$1||'%' OR u.handle ILIKE '%'||$1||'%'
|
||||
@@ -53,7 +56,8 @@ func (r *AdminRepo) ListUsers(ctx context.Context, q string, limit, offset int)
|
||||
for rows.Next() {
|
||||
var u model.AdminUser
|
||||
if err := rows.Scan(&u.ID, &u.Email, &u.Handle, &u.Name, &u.AvatarURL,
|
||||
&u.Role, &u.Plan, &u.EmailVerifiedAt, &u.BannedAt, &u.PostCount, &u.CreatedAt, &total); err != nil {
|
||||
&u.Role, &u.Plan, &u.EmailVerifiedAt, &u.BannedAt, &u.PostCount, &u.CreatedAt,
|
||||
&u.HasPassword, &u.Providers, &total); err != nil {
|
||||
return nil, 0, fmt.Errorf("scan admin user: %w", err)
|
||||
}
|
||||
out = append(out, u)
|
||||
@@ -178,8 +182,9 @@ func (r *AdminRepo) ListFeedback(ctx context.Context, status string, limit, offs
|
||||
return out, total, rows.Err()
|
||||
}
|
||||
|
||||
// SaveReply stores an admin reply on a feedback row, stamps replied_at, and
|
||||
// resolves it (if not already). It returns the recipient's email (the
|
||||
// SaveReply stores an admin reply on a feedback row and stamps replied_at. It
|
||||
// does NOT change resolved_at — replying and resolving are independent actions
|
||||
// (resolve via ResolveFeedback). It returns the recipient's email (the
|
||||
// submitter's account email, else the anonymous contact_email — may be nil if
|
||||
// neither is known) and name so the caller can send the reply email. ok is
|
||||
// false when the id doesn't exist.
|
||||
@@ -187,8 +192,7 @@ func (r *AdminRepo) SaveReply(ctx context.Context, id, message string) (email *s
|
||||
err = r.pg.Pool.QueryRow(ctx, `
|
||||
UPDATE feedback f
|
||||
SET reply_message = $2,
|
||||
replied_at = now(),
|
||||
resolved_at = COALESCE(resolved_at, now())
|
||||
replied_at = now()
|
||||
WHERE f.id = $1
|
||||
RETURNING COALESCE((SELECT email FROM users WHERE id = f.user_id), f.contact_email),
|
||||
COALESCE((SELECT name FROM users WHERE id = f.user_id), '')`,
|
||||
|
||||
@@ -88,9 +88,10 @@ func (s *AdminService) NotifyUser(ctx context.Context, userID, title, body strin
|
||||
return s.notif.NotifyAdminMessage(ctx, userID, title, body)
|
||||
}
|
||||
|
||||
// ReplyFeedback stores the reply (resolving the row) and emails it to the
|
||||
// submitter when an address is known. Returns found=false if the id is
|
||||
// unknown, and emailed=true when a reply email was actually sent.
|
||||
// ReplyFeedback stores the reply and emails it to the submitter when an
|
||||
// address is known. It does not resolve the row (resolve is a separate
|
||||
// action). Returns found=false if the id is unknown, and emailed=true when a
|
||||
// reply email was actually sent.
|
||||
func (s *AdminService) ReplyFeedback(ctx context.Context, id, message string) (found, emailed bool, err error) {
|
||||
email, name, ok, err := s.repo.SaveReply(ctx, id, message)
|
||||
if err != nil || !ok {
|
||||
|
||||
Reference in New Issue
Block a user