using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; using LittleShop.DTOs; using LittleShop.Services; namespace LittleShop.Controllers; [ApiController] [Route("api/push")] public class PushNotificationController : ControllerBase { private readonly IPushNotificationService _pushNotificationService; public PushNotificationController(IPushNotificationService pushNotificationService) { _pushNotificationService = pushNotificationService; } /// /// Get the VAPID public key for client-side push subscription /// [HttpGet("vapid-key")] public IActionResult GetVapidPublicKey() { try { var publicKey = _pushNotificationService.GetVapidPublicKey(); return Ok(new { publicKey }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } /// /// Subscribe admin user to push notifications /// [HttpPost("subscribe")] [Authorize(AuthenticationSchemes = "Cookies", Roles = "Admin")] public async Task Subscribe([FromBody] PushSubscriptionDto subscriptionDto) { try { var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; var username = User.FindFirst(ClaimTypes.Name)?.Value ?? User.Identity?.Name; // Debug logging var logger = HttpContext.RequestServices.GetRequiredService>(); logger.LogInformation("Push subscription attempt - UserIdClaim: {UserIdClaim}, Username: {Username}", userIdClaim, username); if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out Guid userId)) { return Unauthorized(new { error = "Invalid user ID", userIdClaim, username }); } var userAgent = Request.Headers.UserAgent.ToString(); var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); var success = await _pushNotificationService.SubscribeUserAsync(userId, subscriptionDto, userAgent, ipAddress); if (success) { return Ok(new { message = "Successfully subscribed to push notifications" }); } else { return StatusCode(500, new { error = "Failed to subscribe to push notifications" }); } } catch (Exception ex) { var logger = HttpContext.RequestServices.GetRequiredService>(); logger.LogError(ex, "Push subscription error"); return StatusCode(500, new { error = ex.Message }); } } /// /// Subscribe customer to push notifications (for customer-facing apps) /// [HttpPost("subscribe/customer")] [AllowAnonymous] public async Task SubscribeCustomer([FromBody] PushSubscriptionDto subscriptionDto, [FromQuery] Guid customerId) { try { if (customerId == Guid.Empty) { return BadRequest("Invalid customer ID"); } var userAgent = Request.Headers.UserAgent.ToString(); var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); var success = await _pushNotificationService.SubscribeCustomerAsync(customerId, subscriptionDto, userAgent, ipAddress); if (success) { return Ok(new { message = "Successfully subscribed to push notifications" }); } else { return StatusCode(500, new { error = "Failed to subscribe to push notifications" }); } } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } /// /// Unsubscribe from push notifications /// [HttpPost("unsubscribe")] public async Task Unsubscribe([FromBody] UnsubscribeDto unsubscribeDto) { try { var success = await _pushNotificationService.UnsubscribeAsync(unsubscribeDto.Endpoint); if (success) { return Ok(new { message = "Successfully unsubscribed from push notifications" }); } else { return NotFound(new { error = "Subscription not found" }); } } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } /// /// Send test notification to current admin user /// [HttpPost("test")] [Authorize(AuthenticationSchemes = "Cookies", Roles = "Admin")] public async Task SendTestNotification([FromBody] TestPushNotificationDto testDto) { try { var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; var success = await _pushNotificationService.SendTestNotificationAsync(userIdClaim, testDto.Title, testDto.Body); if (success) { return Ok(new { message = "Test notification sent successfully" }); } else { return StatusCode(500, new { error = "Failed to send test notification. Make sure you are subscribed to push notifications." }); } } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } /// /// Send broadcast notification to all admin users /// [HttpPost("broadcast")] [Authorize(AuthenticationSchemes = "Cookies", Roles = "Admin")] public async Task SendBroadcastNotification([FromBody] PushNotificationDto notificationDto) { try { var success = await _pushNotificationService.SendNotificationToAllUsersAsync(notificationDto); if (success) { return Ok(new { message = "Broadcast notification sent successfully" }); } else { return StatusCode(500, new { error = "Failed to send broadcast notification" }); } } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } /// /// Get active push subscriptions (admin only) /// [HttpGet("subscriptions")] [Authorize(AuthenticationSchemes = "Cookies", Roles = "Admin")] public async Task GetActiveSubscriptions() { try { var subscriptions = await _pushNotificationService.GetActiveSubscriptionsAsync(); var result = subscriptions.Select(s => new { id = s.Id, userId = s.UserId, customerId = s.CustomerId, userName = s.User?.Username, customerName = s.Customer?.TelegramDisplayName ?? s.Customer?.TelegramUsername, subscribedAt = s.SubscribedAt, lastUsedAt = s.LastUsedAt, userAgent = s.UserAgent, endpoint = s.Endpoint[..Math.Min(50, s.Endpoint.Length)] + "..." // Truncate for security }).ToList(); return Ok(result); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } /// /// Cleanup expired push subscriptions /// [HttpPost("cleanup")] [Authorize(AuthenticationSchemes = "Cookies", Roles = "Admin")] public async Task CleanupExpiredSubscriptions() { try { var cleanedCount = await _pushNotificationService.CleanupExpiredSubscriptionsAsync(); return Ok(new { message = $"Cleaned up {cleanedCount} expired subscriptions" }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } } public class UnsubscribeDto { public string Endpoint { get; set; } = string.Empty; }