Files
2026-07-05 16:07:44 +03:00

99 lines
3.2 KiB
C#

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<PushController> logger) : ControllerBase
{
public sealed record RegisterPushDeviceRequest(string FirebaseToken, string Platform);
[HttpPost("devices")]
public async Task<IActionResult> 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<IActionResult> 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<Guid?> 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;
}
}