using DysonNetwork.Sphere.Permission;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
namespace DysonNetwork.Sphere.Connection.WebReader;
///
/// Controller for web scraping and link preview services
///
[ApiController]
[Route("/scrap")]
[EnableRateLimiting("fixed")]
public class WebReaderController(WebReaderService reader, ILogger logger)
: ControllerBase
{
///
/// Retrieves a preview for the provided URL
///
/// URL-encoded link to generate preview for
/// Link preview data including title, description, and image
[HttpGet("link")]
public async Task> ScrapLink([FromQuery] string url)
{
if (string.IsNullOrEmpty(url))
{
return BadRequest(new { error = "URL parameter is required" });
}
try
{
// Ensure URL is properly decoded
var decodedUrl = UrlDecoder.Decode(url);
// Validate URL format
if (!Uri.TryCreate(decodedUrl, UriKind.Absolute, out _))
{
return BadRequest(new { error = "Invalid URL format" });
}
var linkEmbed = await reader.GetLinkPreviewAsync(decodedUrl);
return Ok(linkEmbed);
}
catch (WebReaderException ex)
{
logger.LogWarning(ex, "Error scraping link: {Url}", url);
return BadRequest(new { error = ex.Message });
}
catch (Exception ex)
{
logger.LogError(ex, "Unexpected error scraping link: {Url}", url);
return StatusCode(StatusCodes.Status500InternalServerError,
new { error = "An unexpected error occurred while processing the link" });
}
}
///
/// Force invalidates the cache for a specific URL
///
[HttpDelete("link/cache")]
[Authorize]
[RequiredPermission("maintenance", "cache.scrap")]
public async Task InvalidateCache([FromQuery] string url)
{
if (string.IsNullOrEmpty(url))
{
return BadRequest(new { error = "URL parameter is required" });
}
await reader.InvalidateCacheForUrlAsync(url);
return Ok(new { message = "Cache invalidated for URL" });
}
///
/// Force invalidates all cached link previews
///
[HttpDelete("cache/all")]
[Authorize]
[RequiredPermission("maintenance", "cache.scrap")]
public async Task InvalidateAllCache()
{
await reader.InvalidateAllCachedPreviewsAsync();
return Ok(new { message = "All link preview caches invalidated" });
}
}
///
/// Helper class for URL decoding
///
public static class UrlDecoder
{
public static string Decode(string url)
{
// First check if URL is already decoded
if (!url.Contains('%') && !url.Contains('+'))
{
return url;
}
try
{
return System.Net.WebUtility.UrlDecode(url);
}
catch
{
// If decoding fails, return the original string
return url;
}
}
}