Iceshrimp.NET/Iceshrimp.Backend/Controllers/Federation/WellKnownController.cs

129 lines
No EOL
4 KiB
C#

using System.Net;
using System.Net.Mime;
using Iceshrimp.Backend.Controllers.Federation.Attributes;
using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Federation.WebFinger;
using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace Iceshrimp.Backend.Controllers.Federation;
[FederationApiController]
[Route("/.well-known")]
[EnableCors("well-known")]
[OutputCache(PolicyName = "federation")]
public class WellKnownController(IOptions<Config.InstanceSection> config, DatabaseContext db) : ControllerBase
{
[HttpGet("webfinger")]
[Produces("application/jrd+json", "application/xrd+xml")]
[ProducesResults(HttpStatusCode.OK)]
[ProducesErrors(HttpStatusCode.NotFound)]
public async Task<WebFingerResponse> WebFinger([FromQuery] string resource)
{
User? user;
if (Uri.TryCreate(resource, UriKind.Absolute, out var uri) && uri.Scheme is "https")
{
if (uri.Host != config.Value.WebDomain)
throw GracefulException.NotFound("User not found");
if (!uri.AbsolutePath.StartsWith("/users/"))
throw GracefulException.NotFound("User not found");
var id = uri.AbsolutePath["/users/".Length..];
user = await db.Users.FirstOrDefaultAsync(p => p.Id == id && p.IsLocalUser);
}
else
{
if (resource.StartsWith("acct:"))
resource = resource[5..];
var split = resource.TrimStart('@').Split('@');
if (split.Length > 2) throw GracefulException.NotFound("User not found");
if (split.Length == 2)
{
List<string> domains = [config.Value.AccountDomain, config.Value.WebDomain];
if (!domains.Contains(split[1])) throw GracefulException.NotFound("User not found");
}
user = await db.Users.FirstOrDefaultAsync(p => p.UsernameLower == split[0].ToLowerInvariant() &&
p.IsLocalUser);
}
if (user == null) throw GracefulException.NotFound("User not found");
return new WebFingerResponse
{
Subject = $"acct:{user.Username}@{config.Value.AccountDomain}",
Links =
[
new WebFingerLink
{
Rel = "self",
Type = "application/activity+json",
Href = user.GetPublicUri(config.Value)
},
new WebFingerLink
{
Rel = "self",
Type = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
Href = user.GetPublicUri(config.Value)
},
new WebFingerLink
{
Rel = "http://webfinger.net/rel/profile-page",
Type = "text/html",
Href = user.GetPublicUri(config.Value)
},
new WebFingerLink
{
Rel = "http://ostatus.org/schema/1.0/subscribe",
Template = $"https://{config.Value.WebDomain}/authorize-follow?acct={{uri}}"
}
],
Aliases = [user.GetPublicUrl(config.Value), user.GetPublicUri(config.Value)]
};
}
[HttpGet("nodeinfo")]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResults(HttpStatusCode.OK)]
public NodeInfoIndexResponse NodeInfo()
{
return new NodeInfoIndexResponse
{
Links =
[
new WebFingerLink
{
Rel = "http://nodeinfo.diaspora.software/ns/schema/2.1",
Href = $"https://{config.Value.WebDomain}/nodeinfo/2.1"
},
new WebFingerLink
{
Rel = "http://nodeinfo.diaspora.software/ns/schema/2.0",
Href = $"https://{config.Value.WebDomain}/nodeinfo/2.0"
}
]
};
}
[HttpGet("host-meta")]
[Produces("application/xrd+xml", "application/jrd+json")]
[ProducesResults(HttpStatusCode.OK)]
public HostMetaResponse HostMeta()
{
if (Request.Headers.Accept is []) Request.Headers.Accept = "application/xrd+xml";
return new HostMetaResponse(config.Value.WebDomain);
}
[HttpGet("host-meta.json")]
[Produces("application/jrd+json")]
[ProducesResults(HttpStatusCode.OK)]
public HostMetaResponse HostMetaJson() => new(config.Value.WebDomain);
}