Skip to content

feat: admin actions#117

Merged
KyleTryon merged 25 commits intomainfrom
feat/admin-actions
Dec 16, 2025
Merged

feat: admin actions#117
KyleTryon merged 25 commits intomainfrom
feat/admin-actions

Conversation

@KyleTryon
Copy link
Contributor

@KyleTryon KyleTryon commented Dec 16, 2025

The PR adds "Actions" to users listed in the admin's user management console. In the three-dots in the last column, these new actions have been added.

  • Added a "Copy ID" button.
  • Added a "Resend verification email" button.

Additional edits:

  • Discovered some linting errors that extended to our Sentry implementation. Some more testing will be needed.

Backend: Admin Resend Verification Email

  • Added a new resendVerificationEmail mutation to the adminRouter, enabling admins to resend email verification links to unverified users. This includes security logging, Sentry tracing, and error handling.
  • Updated the SecurityAction type to include the new "admin_resend_verification" action for audit logs.
  • Added tests for various scenarios: successful resend, already verified users, disabled verification, user not found, audit logging, and admin-only access.
  • Updated test utilities to allow seeding users with a customizable emailVerified status. [1] [2]

Frontend: Admin User Actions

  • Enhanced the user actions dropdown in columns.tsx to include:
    • "Copy ID" (copies user ID to clipboard with a toast notification)
    • "Resend Verification Email" (visible for unverified users, triggers the backend mutation) [1] [2] [3] [4]
  • Added a new mutation handler and callback for resending verification emails in the admin users route, with success/error toasts and refetching. [1] [2]
  • Added comprehensive tests for the "Copy ID" action, covering rendering, clipboard interaction, toast notification, and menu order.

Testing & Mocks

  • Mocked the Better Auth module in backend tests to simulate email sending. [1] [2]

These changes collectively improve admin capabilities for user management and enhance the usability of the admin interface.

KyleTryon and others added 5 commits December 14, 2025 22:07
Enhance the user management interface by introducing a new "Copy ID" option in the user actions dropdown. This feature allows administrators to easily copy a user's ID to the clipboard, improving usability and efficiency in managing user data. A success toast notification is displayed upon copying the ID, providing immediate feedback to the user.
Add optional emailVerified parameter to seedTestUser helper function
to enable testing of email verification features. Defaults to true
to maintain backward compatibility with existing tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement admin endpoint to manually resend verification emails to
unverified users. Features:

- tRPC mutation with Zod validation
- Full Sentry instrumentation with span tracking
- Security audit logging for both success and failure
- Better Auth integration with automatic token refresh
- Comprehensive error handling (disabled verification, already
  verified, user not found)
- New security action type: admin_resend_verification

The endpoint creates a new verification token with fresh 1-hour
expiration through Better Auth's sendVerificationEmail API.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add "Resend Verification Email" action to user dropdown menu. Features:

- Conditional rendering: only shows for unverified users
- Mail icon from lucide-react
- React Query mutation with toast notifications
- Success/error feedback with sonner
- Automatic table refresh after email sent

The action allows admins to manually trigger verification emails
for users who haven't verified their email addresses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add 6 tests covering all scenarios for the resendVerificationEmail
endpoint:

- Successful email resend to unverified user
- Already verified user error handling
- Email verification disabled error handling
- User not found error handling
- Security audit logging verification
- Admin role requirement enforcement

Includes Better Auth mock setup to simulate email sending behavior.
All tests passing (113/113).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the admin user management interface by adding two new actions to the user dropdown menu: "Copy ID" and "Resend Verification Email". The implementation includes both backend and frontend changes with comprehensive backend testing and security logging.

Key Changes:

  • Added a new resendVerificationEmail mutation in the admin router with security logging, Sentry tracing, and comprehensive error handling
  • Enhanced the admin users UI with "Copy ID" and "Resend Verification Email" actions in the dropdown menu
  • Added backend tests covering various scenarios including success cases, error conditions, and security logging

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/app/src/routes/app/admin/users.tsx Added mutation setup and callback handler for resending verification emails with success/error toast notifications
packages/app/src/components/admin/users/columns.tsx Added "Copy ID" and "Resend Verification Email" menu items with clipboard API integration and conditional rendering based on email verification status
packages/app/src/components/admin/users/__tests__/columns.test.tsx Added comprehensive tests for the "Copy ID" functionality including rendering, clipboard interaction, toast notifications, and menu ordering
packages/api/src/routers/admin.ts Implemented resendVerificationEmail mutation with validation checks, Better Auth integration, security logging, and Sentry tracing
packages/api/src/routers/__tests__/admin.test.ts Added comprehensive test suite covering success scenarios, error conditions, security logging, and authorization checks with Better Auth mocking
packages/api/src/auth/security.ts Extended SecurityAction type to include the new admin_resend_verification action for audit logging
packages/api/src/test/setup.ts Enhanced seedTestUser utility to support customizable emailVerified status for testing purposes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Wraps clipboard.writeText in try-catch block to handle potential failures
such as permission denials or browser support issues, displaying an
appropriate error toast to the user.
Added comment documenting that Better Auth's sendVerificationEmail API
returns { status: boolean }, with references to test files that verify
this behavior, addressing potential confusion about the API contract.
Ensures audit logging errors in resend verification flow are captured
to Sentry, maintaining consistency with error handling patterns used
elsewhere in the codebase (e.g., auth.ts lines 679-686).
Rate limiting is not currently implemented for resendVerificationEmail.
Updated comment to be accurate and added TODO for future implementation
to prevent potential spam of verification emails.
Added test suite for Resend Verification Email feature including:
- Rendering for unverified users
- Conditional hiding for verified users
- Callback invocation with correct user ID
- Mail icon presence in menu item
@KyleTryon
Copy link
Contributor Author

@sentry review

CRITICAL: The mutation was returning a Promise instead of the resolved
value, violating tRPC output schema. This would cause runtime errors
when client code attempts to access data.success or data.message.
Changed from destructuring assignment to explicit indexing to comply
with noUncheckedIndexedAccess TypeScript setting, making the code
safer and more explicit about potential undefined values.
Added typeof check to verify result.status exists and is a boolean
before evaluating it, making the code more robust against potential
API response format changes.
Added critical promise-handling rules that would have caught the
missing await on Sentry.startSpan:
- @typescript-eslint/no-floating-promises: catches unawaited promises
- @typescript-eslint/no-misused-promises: catches promise misuse
- @typescript-eslint/await-thenable: ensures only thenables are awaited
- @typescript-eslint/require-await: warns about async without await

These rules will prevent similar critical bugs in the future.
Fixed 6 critical errors found by new ESLint promise rules:
- Added void operator to CLI scripts and IIFE entry points
- Fixed db/utils.ts to call stmt.execute() instead of awaiting stmt
- Removed incorrect await from synchronous sentry.captureException calls

These new rules successfully caught real bugs that could have caused
runtime issues.
Fixed async functions that didn't use await by:
- Wrapping synchronous Sentry calls in Promise.resolve() for adapter methods
- Removing async keyword from initialization functions that return promises
- Removing unused eslint-disable directives

These changes maintain API compatibility while fixing ESLint warnings.
Updated sentry-integration.md to accurately reflect implementation:
- Fixed inaccurate Node.js entry point description
- Added comprehensive "Import Patterns" section explaining when to use
  direct SDK imports vs shim
- Documented dependency injection pattern used in hono/app.ts
- Added table clarifying import strategies for different file types

This provides clearer guidance for developers on how to properly
import and use Sentry across different parts of the codebase.
Removed async from 3 functions that were synchronous:
- hono/app.ts: Error handler (line 111)
- hono/app.ts: Debug Sentry route (line 139)
- subscriptions.ts: importStatus query (line 2263)

All operations in these functions are synchronous, so async keyword
was unnecessary and triggered require-await warnings.

ESLint now reports: 0 errors, 0 warnings ✅
@KyleTryon
Copy link
Contributor Author

@sentry review

Comment on lines 43 to +46
} else {
// better-sqlite3: Execute sequentially
for (const stmt of statements) {
await stmt;
await stmt.execute();

This comment was marked as outdated.

Comment on lines +748 to +760
// Send verification email using Better Auth API
// This creates a new token with a fresh 1-hour expiration
const result = await auth.api.sendVerificationEmail({
body: {
email: user.email,
callbackURL: `${frontendUrl}/app/articles`,
},
});

// Check if the operation was successful
// Better Auth API returns object with { status: boolean }
// verified in: packages/api/scripts/test-verification-email.ts
// and mocked in: packages/api/src/routers/__tests__/admin.test.ts

This comment was marked as outdated.

Comment on lines +798 to +812
userId: ctx.user.userId,
action: "admin_resend_verification",
ipAddress: undefined,
userAgent: undefined,
success: false,
metadata: {
targetUserId: input.userId,
error: (error as Error).message,
},
});
} catch (auditError) {
console.error(
"Failed to log failed resend verification:",
auditError
);

This comment was marked as outdated.

Comment on lines +72 to 75
void (async () => {
try {
// Run migrations
await runMigrationsIfNeeded(env);

This comment was marked as outdated.

Comment on lines +253 to +263
toast.error("Failed to copy User ID to clipboard");
}
}}
>
<Copy className="mr-2 h-4 w-4" />
Copy ID
</DropdownMenuItem>
{!user.emailVerified && (
<DropdownMenuItem
onClick={() => actions.onResendVerificationEmail(user.id)}
>

This comment was marked as outdated.

Comment on lines +107 to +108
render(
<ActionsCell

This comment was marked as outdated.

@@ -673,6 +677,182 @@ export const adminRouter = router({
return { success: true };
}),

This comment was marked as outdated.

Fixed two critical issues identified by Sentry bot:

1. admin.ts: Made security event logging non-blocking
   - Wrapped logSecurityEvent in try-catch after email send succeeds
   - Ensures success response is returned even if logging fails
   - Logs audit errors to console and Sentry without blocking operation

2. columns.tsx: Added clipboard availability check
   - Check if navigator.clipboard exists before using it
   - Handles non-secure contexts and older browsers gracefully
   - Shows appropriate error message if clipboard unavailable
@KyleTryon KyleTryon merged commit c36b1a6 into main Dec 16, 2025
12 of 14 checks passed
@KyleTryon KyleTryon deleted the feat/admin-actions branch January 8, 2026 19:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants