Foxnouns.NET/Foxnouns.Backend/Controllers/InternalController.cs

75 lines
No EOL
3.1 KiB
C#

using System.Text.RegularExpressions;
using Foxnouns.Backend.Database;
using Foxnouns.Backend.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing.Template;
namespace Foxnouns.Backend.Controllers;
[ApiController]
[Route("/api/internal")]
public partial class InternalController(DatabaseContext db) : ControllerBase
{
[GeneratedRegex(@"(\{\w+\})")]
private static partial Regex PathVarRegex();
private static string GetCleanedTemplate(string template)
{
if (template.StartsWith("api/v2")) template = template["api/v2".Length..];
template = PathVarRegex()
.Replace(template, "{id}") // Replace all path variables (almost always IDs) with `{id}`
.Replace("@me", "{id}"); // Also replace hardcoded `@me` with `{id}`
if (template.Contains("{id}")) return template.Split("{id}")[0] + "{id}";
return template;
}
[HttpPost("request-data")]
public async Task<IActionResult> GetRequestDataAsync([FromBody] RequestDataRequest req)
{
var endpoint = GetEndpoint(HttpContext, req.Path, req.Method);
if (endpoint == null) throw new ApiError.BadRequest("Path/method combination is invalid");
var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
var template = actionDescriptor?.AttributeRouteInfo?.Template;
if (template == null) throw new FoxnounsError("Template value was null on valid endpoint");
template = GetCleanedTemplate(template);
// If no token was supplied, or it isn't valid base 64, return a null user ID (limiting by IP)
if (!AuthUtils.TryParseToken(req.Token, out var rawToken))
return Ok(new RequestDataResponse(null, template));
var userId = await db.GetTokenUserId(rawToken);
return Ok(new RequestDataResponse(userId, template));
}
public record RequestDataRequest(string? Token, string Method, string Path);
public record RequestDataResponse(
Snowflake? UserId,
string Template);
private static RouteEndpoint? GetEndpoint(HttpContext httpContext, string url, string requestMethod)
{
var endpointDataSource = httpContext.RequestServices.GetService<EndpointDataSource>();
if (endpointDataSource == null) return null;
var endpoints = endpointDataSource.Endpoints.OfType<RouteEndpoint>();
foreach (var endpoint in endpoints)
{
if (endpoint.RoutePattern.RawText == null) continue;
var templateMatcher = new TemplateMatcher(TemplateParser.Parse(endpoint.RoutePattern.RawText),
new RouteValueDictionary());
if (!templateMatcher.TryMatch(url, new())) continue;
var httpMethodAttribute = endpoint.Metadata.GetMetadata<HttpMethodAttribute>();
if (httpMethodAttribute != null &&
!httpMethodAttribute.HttpMethods.Any(x => x.Equals(requestMethod, StringComparison.OrdinalIgnoreCase)))
continue;
return endpoint;
}
return null;
}
}