using Microsoft.AspNetCore.Mvc; using QRRapidoApp.Services; using System.Diagnostics; namespace QRRapidoApp.Controllers { /// /// Controller for tracking QR code scans (Premium feature) /// Handles redirect URLs and analytics /// [Route("t")] public class TrackingController : ControllerBase { private readonly IUserService _userService; private readonly ILogger _logger; public TrackingController(IUserService userService, ILogger logger) { _userService = userService; _logger = logger; } /// /// Track QR code scan and redirect to original URL /// URL format: https://qrrapido.site/t/{trackingId} /// [HttpGet("{trackingId}")] public async Task TrackAndRedirect(string trackingId) { var stopwatch = Stopwatch.StartNew(); using (_logger.BeginScope(new Dictionary { ["TrackingId"] = trackingId, ["QRTracking"] = true })) { _logger.LogInformation("QR tracking request - TrackingId: {TrackingId}", trackingId); try { // Get QR code from database var qr = await _userService.GetQRByTrackingIdAsync(trackingId); if (qr == null) { _logger.LogWarning("QR code not found for tracking - TrackingId: {TrackingId}", trackingId); return NotFound("QR code not found"); } // Increment scan count and update last access (fire and forget for performance) _ = Task.Run(async () => { try { await _userService.IncrementQRScanCountAsync(trackingId); _logger.LogDebug("QR scan count incremented - TrackingId: {TrackingId}, NewCount: {NewCount}", trackingId, qr.ScanCount + 1); } catch (Exception ex) { _logger.LogError(ex, "Error incrementing scan count - TrackingId: {TrackingId}", trackingId); } }); stopwatch.Stop(); _logger.LogInformation("QR redirect - TrackingId: {TrackingId}, Destination: {Destination}, ProcessingTime: {ProcessingTimeMs}ms", trackingId, qr.Content, stopwatch.ElapsedMilliseconds); // Redirect to original URL (HTTP 302 temporary redirect) return Redirect(qr.Content); } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "QR tracking failed - TrackingId: {TrackingId}, ProcessingTime: {ProcessingTimeMs}ms", trackingId, stopwatch.ElapsedMilliseconds); // Return error page instead of exposing exception details return StatusCode(500, "Error processing QR code"); } } } /// /// Get analytics for a specific QR code (requires authentication) /// [HttpGet("analytics/{trackingId}")] public async Task GetAnalytics(string trackingId) { var userId = User?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { _logger.LogWarning("Analytics request without authentication - TrackingId: {TrackingId}", trackingId); return Unauthorized(); } try { var qr = await _userService.GetQRByTrackingIdAsync(trackingId); if (qr == null) { _logger.LogWarning("Analytics request for non-existent QR - TrackingId: {TrackingId}", trackingId); return NotFound(); } // Verify ownership if (qr.UserId != userId) { _logger.LogWarning("Analytics request for QR owned by different user - TrackingId: {TrackingId}, RequestingUser: {UserId}, Owner: {OwnerId}", trackingId, userId, qr.UserId); return Forbid(); } _logger.LogInformation("Analytics retrieved - TrackingId: {TrackingId}, UserId: {UserId}, ScanCount: {ScanCount}", trackingId, userId, qr.ScanCount); return Ok(new { trackingId = qr.TrackingId, scanCount = qr.ScanCount, createdAt = qr.CreatedAt, lastAccessedAt = qr.LastAccessedAt, content = qr.Content, type = qr.Type }); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving analytics - TrackingId: {TrackingId}, UserId: {UserId}", trackingId, userId); return StatusCode(500); } } } }