Skip to content

feat: duplicate email prevention and email/name normalization#221

Open
angelospk wants to merge 1 commit intoschemalabz:mainfrom
angelospk:fix/add-validation-to-prevent-duplicate
Open

feat: duplicate email prevention and email/name normalization#221
angelospk wants to merge 1 commit intoschemalabz:mainfrom
angelospk:fix/add-validation-to-prevent-duplicate

Conversation

@angelospk
Copy link
Contributor

@angelospk angelospk commented Feb 25, 2026

Closes #120

Description

Summary:
This Pull Request improves the reliability, security, and developer experience of the user creation and management flow. It adds comprehensive input normalization, prevents silent failures on duplicate user creation, and ensures cleaner error feedback and database state.

Changes Made:

  • Data Layer & Validation (src/lib/db/users.ts):

    • Input Normalization: Added normalizeEmail, normalizeName, and normalizeAdminUserData functions. These automatically trim extra whitespaces, convert emails to lowercase, and handle empty names before they reach the database layer.
    • Duplicate Email Prevention: Added proactive checks in createUser and updateUser to ensure no conflicting emails exist.
    • Clearer Error Surfacing: Handled both custom checks and Prisma duplicate constraint (P2002) errors to consistently throw: "A user with this email already exists.". This fixes the root cause of the silent UI failures.
  • Utilities (src/lib/utils.ts):

    • Whitespace Safety: Hardened the klitiki name formatting function to correctly parse, trim, and evaluate strings containing extra or trailing spaces.
  • API Routes (src/app/api/admin/users/route.ts):

    • Cleanup of Orphan Records: The invite verification token is now immediately proactively cleared if the email sending step fails.
    • Improved Logging Security: Removed Personally Identifiable Information (PII) from invite failure logs. The logs now safely rely solely on user.id instead of the email.
  • Frontend UI (src/components/admin/users/user-dialog.tsx):

    • Refactored Validation: Removed redundant client-side empty-email checks, centralizing validation.
    • Enhanced Error Feedback: Added runtime-safe parsing for server validation errors so that the Duplicate Email message actually renders properly instead of displaying [object Object].
  • Testing (src/lib/__tests__/users.test.ts, src/app/api/admin/users/route.test.ts, src/lib/__tests__/utils.test.ts):

    • Extensive tests added to assert new behavior for updateUser/createUser normalization, constraints, duplicate email errors, klitiki whitespaces, and route cleanup edge cases.

Testing:

  • Verified visually that the "Duplicate Email" error now properly surfaces in the UI.
  • All new and existing test suites pass under the current test setup (Next.js and db).

Greptile Summary

This PR adds comprehensive input normalization and duplicate email prevention to the user management flow. The implementation normalizes emails to lowercase and trims whitespace before database operations, preventing silent failures and improving data consistency.

Key improvements:

  • Email normalization (trim + lowercase) and name normalization (trim, empty → null) in the database layer
  • Proactive duplicate email checks with case-insensitive matching before DB operations
  • Verification token cleanup when invite email sending fails, preventing orphaned tokens
  • Enhanced error handling that maps Prisma P2002 errors to user-friendly messages
  • Improved frontend error parsing to display server validation errors correctly
  • Fixed klitiki utility to handle extra whitespace with regex split
  • Comprehensive test coverage for all new behaviors

Technical highlights:

  • The normalization happens at the DB layer (createUser/updateUser), ensuring consistency
  • Both proactive checks and Prisma error handling provide defense-in-depth for duplicates
  • Verification token cleanup uses a composite key for precise deletion
  • Frontend error parsing safely handles both string and object error responses

Confidence Score: 5/5

  • Safe to merge - well-tested normalization and error handling improvements
  • The PR includes comprehensive test coverage, defensive programming with multiple layers of duplicate prevention, proper error handling, and addresses the root cause of silent UI failures. The normalization logic is straightforward and well-tested.
  • No files require special attention

Important Files Changed

Filename Overview
src/lib/db/users.ts Added normalization functions and duplicate email prevention with proper error handling
src/app/api/admin/users/route.ts Added verification token cleanup on email failure and improved error response handling
src/components/admin/users/user-dialog.tsx Enhanced error parsing and added toast notifications for better user feedback
src/lib/utils.ts Fixed klitiki to handle whitespace properly with trim and regex split

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User submits form] --> B[Frontend: user-dialog.tsx]
    B --> C[POST /api/admin/users]
    C --> D[Normalize email/name]
    D --> E{Check duplicate email<br/>case-insensitive}
    E -->|Exists| F[Return 409 error]
    E -->|Not found| G[Create user in DB]
    G -->|Success| H[Generate verification token]
    H --> I[Send invite email]
    I -->|Email fails| J[Delete verification token]
    I -->|Email succeeds| K[Keep token]
    J --> L[Log error, return 200]
    K --> L
    L --> M[Frontend shows toast]
    F --> N[Frontend shows error toast]
    G -->|P2002 error| O[Map to duplicate message]
    O --> F
Loading

Last reviewed commit: ccfa416

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

7 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 76 to 77
const normalizedEmail = email.trim().toLowerCase()
const normalizedName = name.trim()
Copy link

Choose a reason for hiding this comment

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

duplicates normalization already done in the database layer (createUser calls normalizeAdminUserData)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/api/admin/users/route.ts
Line: 76-77

Comment:
duplicates normalization already done in the database layer (`createUser` calls `normalizeAdminUserData`)

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 116 to 117
const email = String(formData.get("email") ?? "").trim().toLowerCase()
const normalizedName = String(formData.get("name") ?? "").trim()
Copy link

Choose a reason for hiding this comment

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

duplicates normalization already done in the database layer (createUser/updateUser call normalizeAdminUserData)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/admin/users/user-dialog.tsx
Line: 116-117

Comment:
duplicates normalization already done in the database layer (`createUser`/`updateUser` call `normalizeAdminUserData`)

How can I resolve this? If you propose a fix, please make it concise.

…licate-email handling, invite cleanup) and add test coverage

- centralize email/name normalization in createUser/updateUser
- prevent duplicate emails case-insensitively and map conflicts to clear API errors
- improve invite flow robustness with verification-token cleanup on email failure
- improve admin user dialog/API error surfacing and feedback
- add route/db tests for admin users plus klitiki whitespace-normalization tests
@angelospk angelospk force-pushed the fix/add-validation-to-prevent-duplicate branch from 575fd22 to ccfa416 Compare February 25, 2026 19:07
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.

Unable to create user in /admin

1 participant