From 9496d81abe4d5d3d3cd7fe44b8190096a8d40205 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Fri, 16 Feb 2024 02:56:42 +0100 Subject: [PATCH] [backend/database] Add entity methods for getting the public url/uri for users & notes (ISH-47) --- .../Mastodon/Renderers/NoteRenderer.cs | 2 +- .../Mastodon/Renderers/UserRenderer.cs | 6 +++--- .../Mastodon/Schemas/Entities/Mention.cs | 6 +++--- .../Controllers/WellKnownController.cs | 4 ++-- Iceshrimp.Backend/Core/Database/Tables/Note.cs | 5 +++++ Iceshrimp.Backend/Core/Database/Tables/User.cs | 17 ++++++++++++++++- .../Core/Federation/ActivityPub/NoteRenderer.cs | 10 +++++----- .../Core/Federation/ActivityPub/UserRenderer.cs | 6 +++--- Iceshrimp.Backend/Core/Services/NoteService.cs | 2 +- 9 files changed, 39 insertions(+), 19 deletions(-) diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs index f9d42092..f74f185a 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs @@ -20,7 +20,7 @@ public class NoteRenderer( List? attachments = null, Dictionary? likeCounts = null, List? likedNotes = null, int recurse = 2 ) { - var uri = note.Uri ?? $"https://{config.Value.WebDomain}/notes/{note.Id}"; + var uri = note.Uri ?? note.GetPublicUri(config.Value); var renote = note.Renote != null && recurse > 0 ? await RenderAsync(note.Renote, user, accounts, mentions, attachments, likeCounts, likedNotes, --recurse) : null; diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs index 102582f6..d0708859 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs @@ -18,7 +18,7 @@ public class UserRenderer(IOptions config, MfmConverter var res = new Account { Id = user.Id, DisplayName = user.DisplayName ?? user.Username, - AvatarUrl = user.AvatarUrl ?? $"https://{config.Value.WebDomain}/identicon/{user.Id}", + AvatarUrl = user.AvatarUrl ?? user.GetIdenticonUrl(config.Value), Username = user.Username, Acct = acct, FullyQualifiedName = $"{user.Username}@{user.Host ?? config.Value.AccountDomain}", @@ -28,8 +28,8 @@ public class UserRenderer(IOptions config, MfmConverter FollowingCount = user.FollowingCount, StatusesCount = user.NotesCount, Note = await mfmConverter.ToHtmlAsync(profile?.Description ?? "", [], user.Host), - Url = profile?.Url ?? user.Uri ?? $"https://{user.Host ?? config.Value.WebDomain}/@{user.Username}", - AvatarStaticUrl = user.AvatarUrl ?? $"https://{config.Value.WebDomain}/identicon/{user.Id}", //TODO + Url = profile?.Url ?? user.Uri ?? user.GetPublicUrl(config.Value), + AvatarStaticUrl = user.AvatarUrl ?? user.GetIdenticonUrl(config.Value), //TODO HeaderUrl = user.BannerUrl ?? _transparent, HeaderStaticUrl = user.BannerUrl ?? _transparent, //TODO MovedToAccount = null, //TODO diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/Mention.cs b/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/Mention.cs index a8fc301d..475ca7e4 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/Mention.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/Mention.cs @@ -21,9 +21,9 @@ public class Mention() { Username = u.Username; Host = u.Host; Acct = u.Acct; - Uri = u.Uri ?? $"https://{webDomain}/users/{u.Id}"; + Uri = u.Uri ?? u.GetPublicUri(webDomain); Url = u.UserProfile != null - ? u.UserProfile.Url ?? u.Uri ?? $"https://{webDomain}/@{u.Username}" - : u.Uri ?? $"https://{webDomain}/@{u.Username}"; + ? u.UserProfile.Url ?? u.Uri ?? u.GetPublicUrl(webDomain) + : u.Uri ?? u.GetPublicUri(webDomain); } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Controllers/WellKnownController.cs b/Iceshrimp.Backend/Controllers/WellKnownController.cs index 312b2e71..bfd1dca4 100644 --- a/Iceshrimp.Backend/Controllers/WellKnownController.cs +++ b/Iceshrimp.Backend/Controllers/WellKnownController.cs @@ -49,12 +49,12 @@ public class WellKnownController(IOptions config, Databa new WebFingerLink { Rel = "self", Type = "application/activity+json", - Href = $"https://{config.Value.WebDomain}/users/{user.Id}" + Href = user.GetPublicUri(config.Value) }, new WebFingerLink { Rel = "http://webfinger.net/rel/profile-page", Type = "text/html", - Href = $"https://{config.Value.WebDomain}/@{user.Username}" + Href = user.GetPublicUri(config.Value) }, new WebFingerLink { Rel = "http://ostatus.org/schema/1.0/subscribe", diff --git a/Iceshrimp.Backend/Core/Database/Tables/Note.cs b/Iceshrimp.Backend/Core/Database/Tables/Note.cs index 5de0e772..046f6ba7 100644 --- a/Iceshrimp.Backend/Core/Database/Tables/Note.cs +++ b/Iceshrimp.Backend/Core/Database/Tables/Note.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; using EntityFrameworkCore.Projectables; +using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Helpers; using Microsoft.EntityFrameworkCore; using NpgsqlTypes; @@ -276,6 +277,10 @@ public class Note : IEntity { return this; } + + public string GetPublicUri(Config.InstanceSection config) => UserHost == null + ? $"https://{config.WebDomain}/notes/{Id}" + : throw new Exception("Cannot access PublicUri for remote note"); public class MentionedUser { [J("uri")] public required string Uri { get; set; } diff --git a/Iceshrimp.Backend/Core/Database/Tables/User.cs b/Iceshrimp.Backend/Core/Database/Tables/User.cs index a701929d..2311da66 100644 --- a/Iceshrimp.Backend/Core/Database/Tables/User.cs +++ b/Iceshrimp.Backend/Core/Database/Tables/User.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using EntityFrameworkCore.Projectables; +using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Helpers; using Microsoft.EntityFrameworkCore; @@ -381,7 +382,7 @@ public class User : IEntity { [InverseProperty(nameof(NoteBookmark.User))] public virtual ICollection NoteBookmarks { get; set; } = new List(); - + [InverseProperty(nameof(NoteReaction.User))] public virtual ICollection NoteLikes { get; set; } = new List(); @@ -550,4 +551,18 @@ public class User : IEntity { return this; } + + public string GetPublicUrl(Config.InstanceSection config) => GetPublicUrl(config.WebDomain); + public string GetPublicUri(Config.InstanceSection config) => GetPublicUri(config.WebDomain); + public string GetIdenticonUrl(Config.InstanceSection config) => GetIdenticonUrl(config.WebDomain); + + public string GetPublicUri(string webDomain) => Host == null + ? $"https://{webDomain}/users/{Id}" + : throw new Exception("Cannot access PublicUri for remote user"); + + public string GetPublicUrl(string webDomain) => Host == null + ? $"https://{webDomain}/@{Username}" + : throw new Exception("Cannot access PublicUrl for remote user"); + + public string GetIdenticonUrl(string webDomain) => $"https://{webDomain}/identicon/{Id}"; } \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs index fe88dfba..26e9b1cf 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs @@ -17,15 +17,15 @@ public class NoteRenderer(IOptions config, MfmConverter /// ASNote with only the Id field populated public ASNote RenderLite(Note note) { return new ASNote { - Id = note.Uri ?? $"https://{config.Value.WebDomain}/notes/{note.Id}" + Id = note.Uri ?? note.GetPublicUri(config.Value) }; } public async Task RenderAsync(Note note, List? mentions = null) { - var id = $"https://{config.Value.WebDomain}/notes/{note.Id}"; - var userId = $"https://{config.Value.WebDomain}/users/{note.User.Id}"; + var id = note.GetPublicUri(config.Value); + var userId = note.User.GetPublicUri(config.Value); var replyId = note.Reply != null - ? new ASObjectBase(note.Reply.Uri ?? $"https://{config.Value.WebDomain}/notes/{note.ReplyId}") + ? new ASObjectBase(note.Reply.Uri ?? note.Reply.GetPublicUri(config.Value)) : null; mentions ??= await db.Users @@ -35,7 +35,7 @@ public class NoteRenderer(IOptions config, MfmConverter Host = p.Host ?? config.Value.AccountDomain, Username = p.Username, Url = p.UserProfile != null ? p.UserProfile.Url : null, - Uri = p.Uri ?? $"https://{config.Value.WebDomain}/users/{p.Id}" + Uri = p.Uri ?? p.GetPublicUri(config.Value) }) .ToListAsync(); diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs index 9adea62b..225683da 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs @@ -24,7 +24,7 @@ public class UserRenderer(IOptions config, DatabaseConte } return new ASActor { - Id = $"https://{config.Value.WebDomain}/users/{user.Id}" + Id = user.GetPublicUri(config.Value) }; } @@ -40,7 +40,7 @@ public class UserRenderer(IOptions config, DatabaseConte if (keypair == null) throw new GracefulException("User has no keypair"); - var id = $"https://{config.Value.WebDomain}/users/{user.Id}"; + var id = user.GetPublicUri(config.Value); var type = Constants.SystemUsers.Contains(user.UsernameLower) ? "Application" : user.IsBot @@ -55,7 +55,7 @@ public class UserRenderer(IOptions config, DatabaseConte Followers = new ASCollection($"{id}/followers"), Following = new ASCollection($"{id}/following"), SharedInbox = new ASLink($"https://{config.Value.WebDomain}/inbox"), - Url = new ASLink($"https://{config.Value.WebDomain}/@{user.Username}"), + Url = new ASLink(user.GetPublicUrl(config.Value)), Username = user.Username, DisplayName = user.DisplayName ?? user.Username, Summary = profile?.Description != null ? await mfmConverter.FromHtmlAsync(profile.Description) : null, diff --git a/Iceshrimp.Backend/Core/Services/NoteService.cs b/Iceshrimp.Backend/Core/Services/NoteService.cs index 3ee78528..0d2d0487 100644 --- a/Iceshrimp.Backend/Core/Services/NoteService.cs +++ b/Iceshrimp.Backend/Core/Services/NoteService.cs @@ -277,7 +277,7 @@ public class NoteService( var localMentions = localUsers.Select(p => new Note.MentionedUser { Host = config.Value.AccountDomain, Username = p.Username, - Uri = $"https://{config.Value.WebDomain}/users/{p.Id}", + Uri = p.GetPublicUri(config.Value), Url = $"https://{config.Value.WebDomain}/@{p.Username}" });