using Iceshrimp.Backend.Controllers.Schemas; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Federation.WebFinger; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Controllers; [ApiController] [Tags("Federation")] [Route("/.well-known")] [EnableCors("well-known")] public class WellKnownController(IOptions config, DatabaseContext db) : Controller { [HttpGet("webfinger")] [Produces("application/json")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WebFingerResponse))] [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))] public async Task WebFinger([FromQuery] string resource) { User? user; if (resource.StartsWith("acct:")) { var split = resource[5..].TrimStart('@').Split('@'); if (split.Length > 2) return NotFound(); if (split.Length == 2) { List domains = [config.Value.AccountDomain, config.Value.WebDomain]; if (!domains.Contains(split[1])) return NotFound(); } user = await db.Users.FirstOrDefaultAsync(p => p.UsernameLower == split[0].ToLowerInvariant() && p.Host == null); } else if (resource.StartsWith($"https://{config.Value.WebDomain}/users/")) { var id = resource[$"https://{config.Value.WebDomain}/users/".Length..]; user = await db.Users.FirstOrDefaultAsync(p => p.Id == id && p.Host == null); } else { user = await db.Users.FirstOrDefaultAsync(p => p.UsernameLower == resource.ToLowerInvariant() && p.Host == null); } if (user == null) return NotFound(); var response = new WebFingerResponse { Subject = $"acct:{user.Username}@{config.Value.AccountDomain}", Links = [ new WebFingerLink { Rel = "self", Type = "application/activity+json", Href = $"https://{config.Value.WebDomain}/users/{user.Id}" }, new WebFingerLink { Rel = "http://webfinger.net/rel/profile-page", Type = "text/html", Href = $"https://{config.Value.WebDomain}/@{user.Username}" }, new WebFingerLink { Rel = "http://ostatus.org/schema/1.0/subscribe", Template = $"https://{config.Value.WebDomain}/authorize-follow?acct={{uri}}" } ] }; return Ok(response); } [HttpGet("nodeinfo")] [Produces("application/json")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(NodeInfoIndexResponse))] public IActionResult NodeInfo() { var response = 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" } ] }; return Ok(response); } [HttpGet("host-meta")] [Produces("application/xrd+xml")] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult HostMeta() { //TODO: use a proper xml serializer for this return Content($$""""""); } }