110 lines
3.3 KiB
C#
110 lines
3.3 KiB
C#
using DysonNetwork.Sphere.Permission;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
|
|
namespace DysonNetwork.Sphere.Connection.WebReader;
|
|
|
|
/// <summary>
|
|
/// Controller for web scraping and link preview services
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("/scrap")]
|
|
[EnableRateLimiting("fixed")]
|
|
public class WebReaderController(WebReaderService reader, ILogger<WebReaderController> logger)
|
|
: ControllerBase
|
|
{
|
|
/// <summary>
|
|
/// Retrieves a preview for the provided URL
|
|
/// </summary>
|
|
/// <param name="url">URL-encoded link to generate preview for</param>
|
|
/// <returns>Link preview data including title, description, and image</returns>
|
|
[HttpGet("link")]
|
|
public async Task<ActionResult<LinkEmbed>> 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" });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force invalidates the cache for a specific URL
|
|
/// </summary>
|
|
[HttpDelete("link/cache")]
|
|
[Authorize]
|
|
[RequiredPermission("maintenance", "cache.scrap")]
|
|
public async Task<IActionResult> 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" });
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force invalidates all cached link previews
|
|
/// </summary>
|
|
[HttpDelete("cache/all")]
|
|
[Authorize]
|
|
[RequiredPermission("maintenance", "cache.scrap")]
|
|
public async Task<IActionResult> InvalidateAllCache()
|
|
{
|
|
await reader.InvalidateAllCachedPreviewsAsync();
|
|
return Ok(new { message = "All link preview caches invalidated" });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class for URL decoding
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
} |