using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; using Meezi.API.Models.Public; using Meezi.API.Services; using Meezi.Core.Interfaces; using Meezi.Shared; namespace Meezi.API.Controllers; /// /// Push-notification endpoints for the native (Capacitor + Pushe) app. /// /// POST /api/public/push/register — anonymous device registration /// POST /api/public/push/unregister — anonymous device removal /// POST /api/push/broadcast — authorized topic broadcast (marketing / /// saved-café alerts) /// [ApiController] public class PushController : ControllerBase { private readonly IPushDeviceService _devices; private readonly IPushSender _sender; public PushController(IPushDeviceService devices, IPushSender sender) { _devices = devices; _sender = sender; } [HttpPost("api/public/push/register")] [AllowAnonymous] [EnableRateLimiting("public-read")] public async Task Register( [FromBody] RegisterPushDeviceRequest request, CancellationToken ct) { if (string.IsNullOrWhiteSpace(request.Token)) return BadRequest(new ApiResponse(false, null, new ApiError("INVALID_TOKEN", "Device token is required."))); await _devices.RegisterAsync(request, ct); return Ok(new ApiResponse(true, new { registered = true })); } [HttpPost("api/public/push/unregister")] [AllowAnonymous] [EnableRateLimiting("public-read")] public async Task Unregister( [FromBody] UnregisterPushDeviceRequest request, CancellationToken ct) { await _devices.UnregisterAsync(request.Token, ct); return Ok(new ApiResponse(true, new { unregistered = true })); } [HttpPost("api/push/broadcast")] [Authorize] public async Task Broadcast( [FromBody] BroadcastPushRequest request, CancellationToken ct) { if (string.IsNullOrWhiteSpace(request.Topic)) return BadRequest(new ApiResponse(false, null, new ApiError("INVALID_TOPIC", "Topic is required."))); await _sender.SendToTopicAsync(request.Topic, request.Title, request.Body, request.DeepLink, ct); return Ok(new ApiResponse(true, new { sent = true })); } }