using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Application.Helpers; using Domain.Entities.Developers; using Microsoft.EntityFrameworkCore; using SharedKernel.Results; namespace Application.Features.Api.Developers.Apps.RotateSecret; internal sealed class Handler(IAppDbContext db) : ICommandHandler> { public async Task> Handle(Command request, CancellationToken ct) { var app = await db.ApiApplication .Where(c => c.ID == request.AppID && c.OwnerMemberID == request.OwnerMemberID) .FirstOrDefaultAsync(ct); if (app is null) { return Result.Failure(Error.NotFound("App.NotFound", "App not found.")); } var oldCreds = await db.ApiCredential .AsTracking() .Where(c => c.ApplicationID == app.ID && c.RevokedAt == null) .ToListAsync(ct); foreach (var c in oldCreds) { c.Revoke(); } var newClientID = ApiTokenGenerator.GenerateClientID(); var newSecret = ApiTokenGenerator.GenerateClientSecret(); var credential = ApiCredential.Create(app.ID, newClientID, ApiTokenGenerator.Hash(newSecret), ApiTokenGenerator.Hint(newSecret)); await db.ApiCredential.AddAsync(credential, ct); await db.SaveChangesAsync(ct); return Result.Success(new Response(newClientID, newSecret)); } }