QrRapido/Controllers/TrackingController.cs

139 lines
5.4 KiB
C#

using Microsoft.AspNetCore.Mvc;
using QRRapidoApp.Services;
using System.Diagnostics;
namespace QRRapidoApp.Controllers
{
/// <summary>
/// Controller for tracking QR code scans (Premium feature)
/// Handles redirect URLs and analytics
/// </summary>
[Route("t")]
public class TrackingController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<TrackingController> _logger;
public TrackingController(IUserService userService, ILogger<TrackingController> logger)
{
_userService = userService;
_logger = logger;
}
/// <summary>
/// Track QR code scan and redirect to original URL
/// URL format: https://qrrapido.site/t/{trackingId}
/// </summary>
[HttpGet("{trackingId}")]
public async Task<IActionResult> TrackAndRedirect(string trackingId)
{
var stopwatch = Stopwatch.StartNew();
using (_logger.BeginScope(new Dictionary<string, object>
{
["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");
}
}
}
/// <summary>
/// Get analytics for a specific QR code (requires authentication)
/// </summary>
[HttpGet("analytics/{trackingId}")]
public async Task<IActionResult> 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);
}
}
}
}