using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using QMax.Api.Data; using QMax.Api.Data.Entities; using QMax.Api.Infrastructure.Auth; namespace QMax.Api.Controllers; [ApiController] [Authorize] [Route("api/push")] public sealed class PushController(QMaxDbContext db, ILogger logger) : ControllerBase { public sealed record RegisterPushDeviceRequest(string FirebaseToken, string Platform); [HttpPost("devices")] public async Task Register(RegisterPushDeviceRequest request, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(request.FirebaseToken)) { return BadRequest("firebaseToken is required."); } var userId = await ResolvePushUserIdAsync(User.GetUserId(), cancellationToken); if (userId is null) { return Unauthorized(); } var device = await db.PushDevices.FirstOrDefaultAsync(x => x.FirebaseToken == request.FirebaseToken, cancellationToken); if (device is null) { device = new PushDevice { FirebaseToken = request.FirebaseToken }; db.PushDevices.Add(device); } device.UserId = userId.Value; device.Platform = string.IsNullOrWhiteSpace(request.Platform) ? "android" : request.Platform; device.UpdatedAt = DateTimeOffset.UtcNow; await db.SaveChangesAsync(cancellationToken); return NoContent(); } [HttpDelete("devices/{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { var userId = User.GetUserId(); var device = await db.PushDevices.FirstOrDefaultAsync(x => x.Id == id && x.UserId == userId, cancellationToken); if (device is null) { return NotFound(); } db.PushDevices.Remove(device); await db.SaveChangesAsync(cancellationToken); return NoContent(); } private async Task ResolvePushUserIdAsync(Guid authenticatedUserId, CancellationToken cancellationToken) { if (await db.Users.AnyAsync(x => x.Id == authenticatedUserId, cancellationToken)) { return authenticatedUserId; } var existingUserIds = await db.Users .Select(x => x.Id) .Take(2) .ToArrayAsync(cancellationToken); if (existingUserIds.Length == 1) { logger.LogWarning( "Push registration used stale user {AuthenticatedUserId}; attaching device to single existing user {UserId}.", authenticatedUserId, existingUserIds[0]); return existingUserIds[0]; } if (existingUserIds.Length > 1) { logger.LogWarning( "Push registration used missing user {AuthenticatedUserId}, but database has multiple users.", authenticatedUserId); return null; } db.Users.Add(new User { Id = authenticatedUserId, DisplayName = User.Identity?.Name?.Trim() is { Length: > 0 } displayName ? displayName : "QMAX Owner" }); return authenticatedUserId; } }