Table of Contents

Class AdminService

Namespace
Builvero.Application.Services
Assembly
Builvero.Application.dll

Provides administrative operations for user management, invitation management, and bulk email communications.

public class AdminService : IAdminService
Inheritance
AdminService
Implements
Inherited Members

Remarks

This service handles all administrative tasks including:

  • User management (blocking, unblocking, role changes, profile retrieval)
  • Invitation code creation and management
  • Bulk email sending to users by role (Admin Updates feature)
  • Project listing for administrative purposes
All operations require appropriate authorization policies (AdminRead or AdminWrite) at the controller level. Profile photo URLs are automatically converted to presigned URLs for secure access, preventing plain S3 URL leakage.

Constructors

AdminService(IUserRepository, IInvitationRepository, IProjectRepository, ITokenGenerator, IPasswordHasher, IS3Service, IEmailSender, IHostEnvironment, ILogger<AdminService>)

Initializes a new instance of the AdminService class.

public AdminService(IUserRepository userRepository, IInvitationRepository invitationRepository, IProjectRepository projectRepository, ITokenGenerator tokenGenerator, IPasswordHasher passwordHasher, IS3Service s3Service, IEmailSender emailSender, IHostEnvironment environment, ILogger<AdminService> logger)

Parameters

userRepository IUserRepository

Repository for user data access operations.

invitationRepository IInvitationRepository

Repository for invitation code management.

projectRepository IProjectRepository

Repository for project data access operations.

tokenGenerator ITokenGenerator

Service for generating secure tokens and invitation codes.

passwordHasher IPasswordHasher

Service for hashing and verifying passwords using BCrypt.

s3Service IS3Service

Service for S3 operations including presigned URL generation.

emailSender IEmailSender

Service for sending emails via SendGrid.

environment IHostEnvironment

Host environment information for environment-aware behavior.

logger ILogger<AdminService>

Logger for recording service operations and errors.

Methods

BlockUserAsync(Guid, CancellationToken)

Blocks a user account, preventing them from logging in or accessing the platform.

public Task BlockUserAsync(Guid userId, CancellationToken cancellationToken = default)

Parameters

userId Guid

The unique identifier of the user to block.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task
Provides administrative operations for user management, invitation management, and bulk email communications.

Remarks

This operation sets the user's status to Blocked and updates the UpdatedAt timestamp. Blocked users cannot authenticate or access any platform features.

Exceptions

Exception

Thrown when the user is not found.

ChangeUserRoleAsync(Guid, string, CancellationToken)

Changes the role assigned to a user, affecting their permissions and access level.

public Task ChangeUserRoleAsync(Guid userId, string role, CancellationToken cancellationToken = default)

Parameters

userId Guid

The unique identifier of the user whose role should be changed.

role string

The new role as a string (e.g., "Admin", "Moderator", "User"). Must match a UserRole enum value.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task
Provides administrative operations for user management, invitation management, and bulk email communications.

Remarks

Role changes take effect immediately. Changing a user's role to "Admin" grants them full system access. The role string is case-sensitive and must exactly match an enum value name.

Exceptions

Exception

Thrown when the role is invalid or the user is not found.

CreateInvitationAsync(Guid, CreateInvitationRequest, string, CancellationToken)

Creates a new invitation code that can be used for user registration.

public Task<InvitationDto> CreateInvitationAsync(Guid createdByUserId, CreateInvitationRequest request, string baseUrl, CancellationToken cancellationToken = default)

Parameters

createdByUserId Guid

The unique identifier of the admin user creating the invitation.

request CreateInvitationRequest

The invitation creation request containing max uses, expiration date, and optional label.

baseUrl string

The base URL of the application, used to construct the full invitation link.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<InvitationDto>

An InvitationDto containing the created invitation details including the generated code and full invitation link.

Remarks

A unique invitation code is automatically generated. The invitation link is constructed as: {baseUrl}/auth/signup.html?invite={code} Invitations can have a maximum number of uses and an optional expiration date. Once created, the invitation is immediately active.

GetInvitationsAsync(int, int, CancellationToken)

Retrieves a paginated list of all invitation codes in the system.

public Task<(List<InvitationDto> Invitations, int TotalCount)> GetInvitationsAsync(int page, int pageSize, CancellationToken cancellationToken = default)

Parameters

page int

The page number (1-based) to retrieve.

pageSize int

The number of invitations per page.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<(List<InvitationDto> Invitations, int TotalCount)>

A tuple containing the list of invitations and the total count of all invitations.

Remarks

The invitation status is calculated based on expiration date, max uses, and current usage count. Status values are: "Active", "Exhausted" (used up), or "Expired" (past expiration date).

GetUserProfileAsync(Guid, CancellationToken)

Retrieves the complete profile information for a user, including profile photo with presigned URL.

public Task<ProfileDto> GetUserProfileAsync(Guid userId, CancellationToken cancellationToken = default)

Parameters

userId Guid

The unique identifier of the user whose profile should be retrieved.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<ProfileDto>

A ProfileDto containing all profile information including education, experience, skills, and builder tags.

Remarks

Profile photo URLs are automatically converted to presigned URLs (15-minute TTL) for secure access. This prevents plain S3 URLs from being exposed. The profile photo object key is also returned for reference.

Exceptions

Exception

Thrown when the user is not found, user email is missing, or profile is not found.

GetUserProjectsAsync(Guid, CancellationToken)

Retrieves all projects associated with a user, including projects they own and projects they are members of.

public Task<List<ProjectDto>> GetUserProjectsAsync(Guid userId, CancellationToken cancellationToken = default)

Parameters

userId Guid

The unique identifier of the user whose projects should be retrieved.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<List<ProjectDto>>

A list of ProjectDto objects representing all projects for the user.

Remarks

For each project, the method determines the user's role (Owner or Member) and includes pending invitation and join request counts. Profile photo URLs for owners and members are automatically converted to presigned URLs (15-minute TTL) to prevent plain S3 URL leakage. External URLs (non-S3) are returned as-is without query parameters for consistency.

GetUsersAsync(int, int, string?, string?, string?, CancellationToken)

Retrieves a paginated list of users with optional filtering by search term, status, and role.

public Task<(List<UserListDto> Users, int TotalCount)> GetUsersAsync(int page, int pageSize, string? search = null, string? status = null, string? role = null, CancellationToken cancellationToken = default)

Parameters

page int

The page number (1-based) to retrieve.

pageSize int

The number of users per page.

search string

Optional search term to filter users by email or name.

status string

Optional status filter (e.g., "Active", "Blocked", "PendingVerification"). Must match UserStatus enum values.

role string

Optional role filter (e.g., "User", "Admin", "Moderator"). Must match UserRole enum values.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<(List<UserListDto> Users, int TotalCount)>

A tuple containing the list of users and the total count matching the filters.

Remarks

Invalid status or role values are ignored (treated as null). The search term is applied to both email addresses and profile names.

SendAdminUpdatesAsync(List<UserRole>, string, CancellationToken)

Sends bulk email updates to all active users matching the specified roles using the SendGrid AdminUpdates template.

public Task<AdminUpdateSendResult> SendAdminUpdatesAsync(List<UserRole> roles, string htmlContent, CancellationToken cancellationToken = default)

Parameters

roles List<UserRole>

List of user roles to target. At least one role must be specified.

htmlContent string

Raw HTML content for the email body. Must not be empty or whitespace.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<AdminUpdateSendResult>

An AdminUpdateSendResult containing detailed statistics about the email sending operation.

Remarks

This method performs the following operations:

  1. Queries all active users matching the specified roles
  2. Automatically excludes users with email addresses containing "demo.builvero.local"
  3. Sends emails in batches of 100 recipients with a maximum of 10 concurrent sends to avoid rate limiting
  4. Collects individual failures without aborting the entire operation

The email subject is defined in the SendGrid template and cannot be overridden. The HTML content is sent as-is to the template's content_html dynamic field. Recipient names are derived from profile full names or email local parts.

Failed email addresses are collected and reported (capped to first 20). Individual failures are logged but do not stop the operation from continuing with other recipients.

Exceptions

ArgumentException

Thrown when roles list is null/empty or htmlContent is null/empty/whitespace.

See Also

SendInvitationEmailsAsync(Guid, List<string>, string, Guid, string?, CancellationToken)

Sends invitation emails to multiple recipients for a specific invitation code.

public Task<SendInvitationResponse> SendInvitationEmailsAsync(Guid invitationId, List<string> emails, string baseUrl, Guid senderUserId, string? supportEmail = null, CancellationToken cancellationToken = default)

Parameters

invitationId Guid

The unique identifier of the invitation to send.

emails List<string>

List of email addresses to send invitations to.

baseUrl string

The base URL of the frontend application, used to construct invitation links.

senderUserId Guid

The unique identifier of the admin user sending the invitations.

supportEmail string

Optional support email address to include in invitation emails.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<SendInvitationResponse>

A SendInvitationResponse containing per-email results (sent and failed).

Remarks

This method:

  1. Validates that the invitation exists
  2. Normalizes email addresses (lowercase, trim)
  3. Deduplicates email addresses
  4. Sends a separate email to each recipient
  5. Collects per-email results (success/failure)

Each email is sent independently. If one email fails, others will still be attempted. The method returns 200 OK even if some emails fail, with detailed per-email results.

The invitation link is constructed as: {baseUrl}/auth/signup.html?invite={code}

Exceptions

Exception

Thrown when the invitation is not found.

UnblockUserAsync(Guid, CancellationToken)

Unblocks a user account, restoring their access to the platform.

public Task UnblockUserAsync(Guid userId, CancellationToken cancellationToken = default)

Parameters

userId Guid

The unique identifier of the user to unblock.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task
Provides administrative operations for user management, invitation management, and bulk email communications.

Remarks

This operation sets the user's status to Active and updates the UpdatedAt timestamp. The user can immediately log in and access platform features after being unblocked.

Exceptions

Exception

Thrown when the user is not found.

UpdateInvitationAsync(Guid, UpdateInvitationRequest, CancellationToken)

Updates an existing invitation code's properties (max uses, expiration date, label).

public Task<InvitationDto> UpdateInvitationAsync(Guid invitationId, UpdateInvitationRequest request, CancellationToken cancellationToken = default)

Parameters

invitationId Guid

The unique identifier of the invitation to update.

request UpdateInvitationRequest

The update request containing the fields to modify. Only provided fields are updated.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<InvitationDto>

An InvitationDto representing the updated invitation.

Remarks

This method performs partial updates - only fields provided in the request are modified. Max uses cannot be reduced below the current number of times the invitation has been used. The invitation code itself cannot be changed after creation.

Exceptions

Exception

Thrown when the invitation is not found or max uses is set to less than the current usage count.

UpdateUserAsync(Guid, UpdateUserRequest, CancellationToken)

Updates user account information including email, password, role, and status.

public Task<UserListDto> UpdateUserAsync(Guid userId, UpdateUserRequest request, CancellationToken cancellationToken = default)

Parameters

userId Guid

The unique identifier of the user to update.

request UpdateUserRequest

The update request containing the fields to modify. Only provided fields are updated.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

Task<UserListDto>

A UserListDto representing the updated user information.

Remarks

This method performs partial updates - only fields provided in the request are modified. Email uniqueness is enforced across all users. Passwords are hashed using BCrypt before storage. Role and status values must match enum values exactly (case-sensitive).

Exceptions

Exception

Thrown when the user is not found, email is already in use by another user, or invalid role/status values are provided.