diff --git a/Iceshrimp.Backend/Components/PublicPreview/Attributes/PublicPreviewRouteFilter.cs b/Iceshrimp.Backend/Components/PublicPreview/Attributes/PublicPreviewRouteFilter.cs index 2549b59e..a86e974e 100644 --- a/Iceshrimp.Backend/Components/PublicPreview/Attributes/PublicPreviewRouteFilter.cs +++ b/Iceshrimp.Backend/Components/PublicPreview/Attributes/PublicPreviewRouteFilter.cs @@ -1,9 +1,11 @@ using AngleSharp.Io; +using Iceshrimp.Backend.Core.Extensions; using Microsoft.AspNetCore.Routing.Matching; namespace Iceshrimp.Backend.Components.PublicPreview.Attributes; -public class PublicPreviewRouteMatcher : MatcherPolicy, IEndpointSelectorPolicy +public class PublicPreviewRouteMatcher : MatcherPolicy, IEndpointSelectorPolicy, ISingletonService, + IService { public override int Order => 99999; // That's ActionConstraintMatcherPolicy - 1 diff --git a/Iceshrimp.Backend/Components/PublicPreview/Renderers/MfmRenderer.cs b/Iceshrimp.Backend/Components/PublicPreview/Renderers/MfmRenderer.cs index 83138ba5..27885332 100644 --- a/Iceshrimp.Backend/Components/PublicPreview/Renderers/MfmRenderer.cs +++ b/Iceshrimp.Backend/Components/PublicPreview/Renderers/MfmRenderer.cs @@ -1,5 +1,6 @@ using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion; using Iceshrimp.Parsing; using Microsoft.AspNetCore.Components; @@ -7,7 +8,7 @@ using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Components.PublicPreview.Renderers; -public class MfmRenderer(IOptions config) +public class MfmRenderer(IOptions config) : ISingletonService { private readonly MfmConverter _converter = new(config); diff --git a/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs index f3832183..8547e5c4 100644 --- a/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs @@ -14,7 +14,7 @@ public class NoteRenderer( MfmRenderer mfm, IOptions instance, IOptionsSnapshot security -) +) : IScopedService { public async Task RenderOne(Note? note) { diff --git a/Iceshrimp.Backend/Components/PublicPreview/Renderers/UserRenderer.cs b/Iceshrimp.Backend/Components/PublicPreview/Renderers/UserRenderer.cs index cc74483a..692df4fa 100644 --- a/Iceshrimp.Backend/Components/PublicPreview/Renderers/UserRenderer.cs +++ b/Iceshrimp.Backend/Components/PublicPreview/Renderers/UserRenderer.cs @@ -13,7 +13,7 @@ public class UserRenderer( MfmRenderer mfm, IOptions instance, IOptionsSnapshot security -) +) : IScopedService { public async Task RenderOne(User? user) { diff --git a/Iceshrimp.Backend/Controllers/Federation/ActivityPubController.cs b/Iceshrimp.Backend/Controllers/Federation/ActivityPubController.cs index 9d4246d1..5d91b59a 100644 --- a/Iceshrimp.Backend/Controllers/Federation/ActivityPubController.cs +++ b/Iceshrimp.Backend/Controllers/Federation/ActivityPubController.cs @@ -29,7 +29,7 @@ public class ActivityPubController( ActivityPub.NoteRenderer noteRenderer, ActivityPub.UserRenderer userRenderer, IOptions config -) : ControllerBase +) : ControllerBase, IScopedService { [HttpGet("/notes/{id}")] [AuthorizedFetch] diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs index 102dbc03..fe64f73e 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs @@ -20,7 +20,7 @@ public class NoteRenderer( MfmConverter mfmConverter, DatabaseContext db, EmojiService emojiSvc -) +) : IScopedService { private static readonly FilterResultEntity InaccessibleFilter = new() { diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NotificationRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NotificationRenderer.cs index 1194353b..494b3fdc 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NotificationRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NotificationRenderer.cs @@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Controllers.Mastodon.Renderers; -public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer, UserRenderer userRenderer) +public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer, UserRenderer userRenderer) : IScopedService { public async Task RenderAsync( Notification notification, User user, bool isPleroma, List? accounts = null, diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/PollRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/PollRenderer.cs index e37b652b..2a2fce89 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/PollRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/PollRenderer.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Controllers.Mastodon.Renderers; -public class PollRenderer(DatabaseContext db) +public class PollRenderer(DatabaseContext db) : IScopedService { public async Task RenderAsync(Poll poll, User? user, PollRendererDto? data = null) { diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs index 16217e1e..ee9129b2 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/UserRenderer.cs @@ -14,7 +14,7 @@ public class UserRenderer( IOptionsSnapshot security, MfmConverter mfmConverter, DatabaseContext db -) +) : IScopedService { private readonly string _transparent = $"https://{config.Value.WebDomain}/assets/transparent.png"; diff --git a/Iceshrimp.Backend/Controllers/Web/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Controllers/Web/Renderers/NoteRenderer.cs index a3f31d0e..a4007d5c 100644 --- a/Iceshrimp.Backend/Controllers/Web/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Web/Renderers/NoteRenderer.cs @@ -15,7 +15,7 @@ public class NoteRenderer( DatabaseContext db, EmojiService emojiSvc, IOptions config -) +) : IScopedService { public async Task RenderOne( Note note, User? user, Filter.FilterContext? filterContext = null, NoteRendererDto? data = null diff --git a/Iceshrimp.Backend/Controllers/Web/Renderers/NotificationRenderer.cs b/Iceshrimp.Backend/Controllers/Web/Renderers/NotificationRenderer.cs index 33eaeb6a..c24dde26 100644 --- a/Iceshrimp.Backend/Controllers/Web/Renderers/NotificationRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Web/Renderers/NotificationRenderer.cs @@ -5,7 +5,7 @@ using static Iceshrimp.Shared.Schemas.Web.NotificationResponse; namespace Iceshrimp.Backend.Controllers.Web.Renderers; -public class NotificationRenderer(UserRenderer userRenderer, NoteRenderer noteRenderer) +public class NotificationRenderer(UserRenderer userRenderer, NoteRenderer noteRenderer) : IScopedService { private static NotificationResponse Render(Notification notification, NotificationRendererDto data) { diff --git a/Iceshrimp.Backend/Controllers/Web/Renderers/UserProfileRenderer.cs b/Iceshrimp.Backend/Controllers/Web/Renderers/UserProfileRenderer.cs index d9442d15..1f9b3b21 100644 --- a/Iceshrimp.Backend/Controllers/Web/Renderers/UserProfileRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Web/Renderers/UserProfileRenderer.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Controllers.Web.Renderers; -public class UserProfileRenderer(DatabaseContext db) +public class UserProfileRenderer(DatabaseContext db) : IScopedService { public async Task RenderOne(User user, User? localUser, UserRendererDto? data = null) { diff --git a/Iceshrimp.Backend/Controllers/Web/Renderers/UserRenderer.cs b/Iceshrimp.Backend/Controllers/Web/Renderers/UserRenderer.cs index 977c815d..b74a99f2 100644 --- a/Iceshrimp.Backend/Controllers/Web/Renderers/UserRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Web/Renderers/UserRenderer.cs @@ -1,13 +1,14 @@ using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Shared.Schemas.Web; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Controllers.Web.Renderers; -public class UserRenderer(IOptions config, DatabaseContext db) +public class UserRenderer(IOptions config, DatabaseContext db) : IScopedService { private UserResponse Render(User user, UserRendererDto data) { diff --git a/Iceshrimp.Backend/Core/Extensions/ServiceExtensions.cs b/Iceshrimp.Backend/Core/Extensions/ServiceExtensions.cs index a94595f1..98929b6d 100644 --- a/Iceshrimp.Backend/Core/Extensions/ServiceExtensions.cs +++ b/Iceshrimp.Backend/Core/Extensions/ServiceExtensions.cs @@ -3,19 +3,10 @@ using System.Reflection; using System.Threading.RateLimiting; using System.Xml.Linq; using Iceshrimp.AssemblyUtils; -using Iceshrimp.Backend.Components.PublicPreview.Attributes; -using Iceshrimp.Backend.Components.PublicPreview.Renderers; -using Iceshrimp.Backend.Controllers.Federation; -using Iceshrimp.Backend.Controllers.Mastodon.Renderers; -using Iceshrimp.Backend.Controllers.Web.Renderers; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; -using Iceshrimp.Backend.Core.Federation.WebFinger; using Iceshrimp.Backend.Core.Helpers; -using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion; using Iceshrimp.Backend.Core.Middleware; -using Iceshrimp.Backend.Core.Services; -using Iceshrimp.Backend.Core.Services.ImageProcessing; using Iceshrimp.Backend.SignalR.Authentication; using Iceshrimp.Shared.Configuration; using Iceshrimp.Shared.Schemas.Web; @@ -33,9 +24,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; -using NoteRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.NoteRenderer; -using NotificationRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.NotificationRenderer; -using UserRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.UserRenderer; namespace Iceshrimp.Backend.Core.Extensions; @@ -43,91 +31,40 @@ public static class ServiceExtensions { public static void AddServices(this IServiceCollection services, IConfiguration configuration) { - // Transient = instantiated per request and class + var config = configuration.Get() ?? throw new Exception("Failed to read storage config section"); - // Scoped = instantiated per request - services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); + var serviceTypes = PluginLoader + .Assemblies.Prepend(Assembly.GetExecutingAssembly()) + .SelectMany(AssemblyLoader.GetImplementationsOfInterface) + .OrderBy(type => type.GetInterfaceProperty(nameof(IService.Priority)) ?? 0) + .ToArray(); - // Singleton = instantiated once across application lifetime - services - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - var config = configuration.GetSection("Storage").Get() ?? - throw new Exception("Failed to read storage config section"); - - switch (config.MediaProcessing.ImageProcessor) + foreach (var type in serviceTypes) { - case Enums.ImageProcessor.LibVips: - services.AddSingleton(); - services.AddSingleton(); - break; - case Enums.ImageProcessor.ImageSharp: - services.AddSingleton(); - break; - case Enums.ImageProcessor.None: - break; - default: - throw new ArgumentOutOfRangeException(); + if (type.GetInterfaceProperty(nameof(IService.Lifetime)) is not { } lifetime) + continue; + + if (type.GetInterface(nameof(IConditionalService)) != null) + if (type.CallInterfaceMethod(nameof(IConditionalService.Predicate), config) is not true) + continue; + + var serviceType = type.GetInterfaceProperty(nameof(IService.ServiceType)) ?? type; + services.Add(new ServiceDescriptor(serviceType, type, lifetime)); } - // Hosted services = long running background tasks - // Note: These need to be added as a singleton as well to ensure data consistency - services.AddHostedService(provider => provider.GetRequiredService()); - services.AddHostedService(provider => provider.GetRequiredService()); - services.AddHostedService(provider => provider.GetRequiredService()); + var hostedServiceTypes = PluginLoader + .Assemblies.Prepend(Assembly.GetExecutingAssembly()) + .SelectMany(AssemblyLoader.GetImplementationsOfInterface) + .ToArray(); + + foreach (var type in hostedServiceTypes) + { + if (type.GetInterface(nameof(IService)) == null) + services.Add(new ServiceDescriptor(type, type, ServiceLifetime.Singleton)); + + services.Add(new ServiceDescriptor(typeof(IHostedService), provider => provider.GetRequiredService(type), + ServiceLifetime.Singleton)); + } } public static void AddMiddleware(this IServiceCollection services) @@ -413,6 +350,51 @@ public static partial class HttpContextExtensions ctx.Items.TryGetValue(CacheKey, out var s) && s is true; } +public interface IService +{ + // This should be abstract instead of virtual but the runtime team said https://github.com/dotnet/runtime/issues/79331 + public static virtual ServiceLifetime Lifetime => throw new Exception("Missing IService.Lifetime override"); + + public static virtual Type? ServiceType => null; + public static virtual int Priority => 0; +} + +/// +/// Instantiated per request and class +/// +public interface ITransientService : IService +{ + static ServiceLifetime IService.Lifetime => ServiceLifetime.Transient; +} + +/// +/// Instantiated per request +/// +public interface IScopedService : IService +{ + static ServiceLifetime IService.Lifetime => ServiceLifetime.Scoped; +} + +/// +/// Instantiated once across application lifetime +/// +public interface ISingletonService : IService +{ + static ServiceLifetime IService.Lifetime => ServiceLifetime.Singleton; +} + +public interface IService : IService +{ + static Type IService.ServiceType => typeof(TService); +} + +public interface IConditionalService : IService +{ + // This should be abstract instead of virtual but the runtime team said https://github.com/dotnet/runtime/issues/79331 + public static virtual bool Predicate(Config ctx) => + throw new Exception("Missing IConditionalService.Predicate override"); +} + #region AsyncDataProtection handlers /// diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityDeliverService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityDeliverService.cs index d76dace4..db46a182 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityDeliverService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityDeliverService.cs @@ -1,5 +1,6 @@ using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.ActivityStreams; using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; using Iceshrimp.Backend.Core.Queues; @@ -13,7 +14,7 @@ public class ActivityDeliverService( ILogger logger, QueueService queueService, DatabaseContext db -) +) : IScopedService { public async Task DeliverToFollowersAsync(ASActivity activity, User actor, IEnumerable recipients) { diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs index 93cec270..44b0ef1f 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs @@ -23,7 +23,7 @@ public class ActivityFetcherService( DatabaseContext db, ILogger logger, FederationControlService fedCtrlSvc -) +) : IScopedService { private static readonly IReadOnlyCollection AcceptableActivityTypes = [ diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs index 1439d263..ad61b8b5 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs @@ -29,7 +29,7 @@ public class ActivityHandlerService( EmojiService emojiSvc, EventService eventSvc, RelayService relaySvc -) +) : IScopedService { public async Task PerformActivityAsync(ASActivity activity, string? inboxUserId, string? authenticatedUserId) { diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityRenderer.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityRenderer.cs index bb6509be..8c49bf58 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityRenderer.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityRenderer.cs @@ -12,7 +12,7 @@ public class ActivityRenderer( IOptions config, UserRenderer userRenderer, NoteRenderer noteRenderer -) +) : IScopedService { private string GenerateActivityId() => $"https://{config.Value.WebDomain}/activities/ephemeral/{Guid.NewGuid().ToStringLower()}"; diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/FederationControlService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/FederationControlService.cs index 7b9d40a0..0a0aa2cf 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/FederationControlService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/FederationControlService.cs @@ -13,7 +13,7 @@ public class FederationControlService( IOptionsSnapshot options, IOptions instance, DatabaseContext db -) +) : IScopedService { //TODO: we need some level of caching here public async Task ShouldBlockAsync(params string?[] hosts) diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/MentionsResolver.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/MentionsResolver.cs index 79229006..6b954b0f 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/MentionsResolver.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/MentionsResolver.cs @@ -16,9 +16,7 @@ using SplitDomainMapping = IReadOnlyDictionary<(string usernameLower, string web /// Resolves mentions into their canonical form. This is required for handling split domain mentions correctly, as it /// cannot be guaranteed that remote instances handle split domain users correctly. /// -public class MentionsResolver( - IOptions config -) +public class MentionsResolver(IOptions config) : ISingletonService { public string ResolveMentions( string mfm, string? host, diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs index 82b19f19..cba926ee 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/NoteRenderer.cs @@ -10,7 +10,11 @@ using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Federation.ActivityPub; -public class NoteRenderer(IOptions config, MfmConverter mfmConverter, DatabaseContext db) +public class NoteRenderer( + IOptions config, + MfmConverter mfmConverter, + DatabaseContext db +) : IScopedService { /// /// This function is meant for compacting a note into the @id form as specified in ActivityStreams diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ObjectResolver.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ObjectResolver.cs index 563d2529..65871333 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ObjectResolver.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ObjectResolver.cs @@ -1,6 +1,7 @@ using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; using Iceshrimp.Backend.Core.Middleware; using Microsoft.EntityFrameworkCore; @@ -14,7 +15,7 @@ public class ObjectResolver( DatabaseContext db, FederationControlService federationCtrl, IOptions config -) +) : IScopedService { public async Task ResolveObject( ASObjectBase baseObj, string? actorUri = null, int recurse = 5, bool force = false, User? user = null diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs index fe586b7d..d560b5dc 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserRenderer.cs @@ -2,6 +2,7 @@ using AngleSharp.Text; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion; using Microsoft.EntityFrameworkCore; @@ -9,7 +10,11 @@ using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Federation.ActivityPub; -public class UserRenderer(IOptions config, DatabaseContext db, MfmConverter mfmConverter) +public class UserRenderer( + IOptions config, + DatabaseContext db, + MfmConverter mfmConverter +) : IScopedService { /// /// This function is meant for compacting an actor into the @id form as specified in ActivityStreams diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs index b92f974d..774c9072 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs @@ -3,6 +3,7 @@ using AsyncKeyedLock; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.WebFinger; using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Backend.Core.Middleware; @@ -20,7 +21,7 @@ public class UserResolver( ActivityFetcherService fetchSvc, IOptions config, DatabaseContext db -) +) : IScopedService { private static readonly AsyncKeyedLocker KeyedLocker = new(o => { diff --git a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs index 0e872caf..a200e994 100644 --- a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs +++ b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs @@ -4,6 +4,7 @@ using System.Text.Encodings.Web; using System.Xml; using System.Xml.Serialization; using Iceshrimp.Backend.Core.Configuration; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Services; using Microsoft.Extensions.Options; @@ -27,7 +28,7 @@ public class WebFingerService( HttpRequestService httpRqSvc, IHostApplicationLifetime appLifetime, IOptions config -) +) : ISingletonService { private static readonly ImmutableArray Accept = [ diff --git a/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs b/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs index 0556acc4..6e2ba61f 100644 --- a/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs +++ b/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs @@ -16,7 +16,9 @@ using HtmlParser = AngleSharp.Html.Parser.HtmlParser; namespace Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion; -public class MfmConverter(IOptions config) +public class MfmConverter( + IOptions config +) : IScopedService // <- this is intentional, see property below { public bool SupportsHtmlFormatting { private get; set; } = true; diff --git a/Iceshrimp.Backend/Core/Services/BiteService.cs b/Iceshrimp.Backend/Core/Services/BiteService.cs index 39555064..0060ca85 100644 --- a/Iceshrimp.Backend/Core/Services/BiteService.cs +++ b/Iceshrimp.Backend/Core/Services/BiteService.cs @@ -1,79 +1,88 @@ using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Services; -public class BiteService(DatabaseContext db, ActivityPub.ActivityRenderer activityRenderer, ActivityPub.ActivityDeliverService deliverSvc, NotificationService notificationSvc, IOptions config) +public class BiteService( + DatabaseContext db, + ActivityPub.ActivityRenderer activityRenderer, + ActivityPub.ActivityDeliverService deliverSvc, + NotificationService notificationSvc, + IOptions config +) : IScopedService { - public async Task BiteAsync(User user, Bite target) - { - var bite = new Bite - { - Id = IdHelpers.GenerateSnowflakeId(), - CreatedAt = DateTime.UtcNow, - User = user, - TargetBite = target - }; - bite.Uri = bite.GetPublicUri(config.Value); + public async Task BiteAsync(User user, Bite target) + { + var bite = new Bite + { + Id = IdHelpers.GenerateSnowflakeId(), + CreatedAt = DateTime.UtcNow, + User = user, + TargetBite = target + }; + bite.Uri = bite.GetPublicUri(config.Value); - await db.Bites.AddAsync(bite); - await db.SaveChangesAsync(); + await db.Bites.AddAsync(bite); + await db.SaveChangesAsync(); - if (target.UserHost != null) - { - var activity = activityRenderer.RenderBite(bite, target.Uri ?? target.GetPublicUri(config.Value), target.User); - await deliverSvc.DeliverToAsync(activity, user, target.User); - } + if (target.UserHost != null) + { + var activity = + activityRenderer.RenderBite(bite, target.Uri ?? target.GetPublicUri(config.Value), target.User); + await deliverSvc.DeliverToAsync(activity, user, target.User); + } - await notificationSvc.GenerateBiteNotification(bite); - } - - public async Task BiteAsync(User user, Note target) - { - var bite = new Bite - { - Id = IdHelpers.GenerateSnowflakeId(), - CreatedAt = DateTime.UtcNow, - User = user, - TargetNote = target - }; - bite.Uri = bite.GetPublicUri(config.Value); + await notificationSvc.GenerateBiteNotification(bite); + } - await db.Bites.AddAsync(bite); - await db.SaveChangesAsync(); + public async Task BiteAsync(User user, Note target) + { + var bite = new Bite + { + Id = IdHelpers.GenerateSnowflakeId(), + CreatedAt = DateTime.UtcNow, + User = user, + TargetNote = target + }; + bite.Uri = bite.GetPublicUri(config.Value); - if (target.UserHost != null) - { - var activity = activityRenderer.RenderBite(bite, target.Uri ?? target.GetPublicUri(config.Value), target.User); - await deliverSvc.DeliverToAsync(activity, user, target.User); - } + await db.Bites.AddAsync(bite); + await db.SaveChangesAsync(); - await notificationSvc.GenerateBiteNotification(bite); - } - - public async Task BiteAsync(User user, User target) - { - var bite = new Bite - { - Id = IdHelpers.GenerateSnowflakeId(), - CreatedAt = DateTime.UtcNow, - User = user, - TargetUser = target - }; - bite.Uri = bite.GetPublicUri(config.Value); + if (target.UserHost != null) + { + var activity = + activityRenderer.RenderBite(bite, target.Uri ?? target.GetPublicUri(config.Value), target.User); + await deliverSvc.DeliverToAsync(activity, user, target.User); + } - await db.Bites.AddAsync(bite); - await db.SaveChangesAsync(); + await notificationSvc.GenerateBiteNotification(bite); + } - if (target.Host != null) - { - var activity = activityRenderer.RenderBite(bite, target.Uri ?? target.GetPublicUri(config.Value), target); - await deliverSvc.DeliverToAsync(activity, user, target); - } + public async Task BiteAsync(User user, User target) + { + var bite = new Bite + { + Id = IdHelpers.GenerateSnowflakeId(), + CreatedAt = DateTime.UtcNow, + User = user, + TargetUser = target + }; + bite.Uri = bite.GetPublicUri(config.Value); - await notificationSvc.GenerateBiteNotification(bite); - } + await db.Bites.AddAsync(bite); + await db.SaveChangesAsync(); + + if (target.Host != null) + { + var activity = activityRenderer.RenderBite(bite, target.Uri ?? target.GetPublicUri(config.Value), target); + await deliverSvc.DeliverToAsync(activity, user, target); + } + + await notificationSvc.GenerateBiteNotification(bite); + } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Services/CacheService.cs b/Iceshrimp.Backend/Core/Services/CacheService.cs index 334e529a..b93c8901 100644 --- a/Iceshrimp.Backend/Core/Services/CacheService.cs +++ b/Iceshrimp.Backend/Core/Services/CacheService.cs @@ -3,12 +3,12 @@ using System.Text.Json.Serialization; using AsyncKeyedLock; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; -//TODO: named caches (with prefix) -public class CacheService([FromKeyedServices("cache")] DatabaseContext db) +public class CacheService([FromKeyedServices("cache")] DatabaseContext db) : IScopedService { private static readonly AsyncKeyedLocker KeyedLocker = new(o => { diff --git a/Iceshrimp.Backend/Core/Services/CustomHttpClient.cs b/Iceshrimp.Backend/Core/Services/CustomHttpClient.cs index 0f8b5a8c..2cde99d6 100644 --- a/Iceshrimp.Backend/Core/Services/CustomHttpClient.cs +++ b/Iceshrimp.Backend/Core/Services/CustomHttpClient.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Services; -public class CustomHttpClient : HttpClient +public class CustomHttpClient : HttpClient, IService, ISingletonService { private static readonly FastFallback FastFallbackHandler = new(); diff --git a/Iceshrimp.Backend/Core/Services/DatabaseMaintenanceService.cs b/Iceshrimp.Backend/Core/Services/DatabaseMaintenanceService.cs index 5814eaba..8b1f7cdd 100644 --- a/Iceshrimp.Backend/Core/Services/DatabaseMaintenanceService.cs +++ b/Iceshrimp.Backend/Core/Services/DatabaseMaintenanceService.cs @@ -1,9 +1,10 @@ using Iceshrimp.Backend.Core.Database; +using Iceshrimp.Backend.Core.Extensions; using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; -public class DatabaseMaintenanceService(DatabaseContext db) +public class DatabaseMaintenanceService(DatabaseContext db) : IScopedService { public async Task RecomputeNoteCountersAsync() { diff --git a/Iceshrimp.Backend/Core/Services/DriveService.cs b/Iceshrimp.Backend/Core/Services/DriveService.cs index 4e7b8ae3..b8171bf1 100644 --- a/Iceshrimp.Backend/Core/Services/DriveService.cs +++ b/Iceshrimp.Backend/Core/Services/DriveService.cs @@ -27,7 +27,7 @@ public class DriveService( QueueService queueSvc, ILogger logger, ImageProcessor imageProcessor -) +) : IScopedService { public async Task StoreFile( string? uri, User user, bool sensitive, string? description = null, string? mimeType = null, diff --git a/Iceshrimp.Backend/Core/Services/EmojiImportService.cs b/Iceshrimp.Backend/Core/Services/EmojiImportService.cs index 07ae51c1..82906182 100644 --- a/Iceshrimp.Backend/Core/Services/EmojiImportService.cs +++ b/Iceshrimp.Backend/Core/Services/EmojiImportService.cs @@ -1,6 +1,7 @@ using System.IO.Compression; using System.Net; using System.Text.Json; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Middleware; using Microsoft.AspNetCore.StaticFiles; @@ -30,7 +31,7 @@ public record EmojiZip(EmojiZipMeta Metadata, ZipArchive Archive); public class EmojiImportService( EmojiService emojiSvc, ILogger logger -) +) : IScopedService { public static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web); diff --git a/Iceshrimp.Backend/Core/Services/EmojiService.cs b/Iceshrimp.Backend/Core/Services/EmojiService.cs index 7cdec265..9c77b1c9 100644 --- a/Iceshrimp.Backend/Core/Services/EmojiService.cs +++ b/Iceshrimp.Backend/Core/Services/EmojiService.cs @@ -18,7 +18,7 @@ public partial class EmojiService( DriveService driveSvc, SystemUserService sysUserSvc, IOptions config -) +) : IScopedService { private static readonly AsyncKeyedLocker KeyedLocker = new(o => { diff --git a/Iceshrimp.Backend/Core/Services/EventService.cs b/Iceshrimp.Backend/Core/Services/EventService.cs index 0d9341a9..21df00db 100644 --- a/Iceshrimp.Backend/Core/Services/EventService.cs +++ b/Iceshrimp.Backend/Core/Services/EventService.cs @@ -1,9 +1,10 @@ using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Events; +using Iceshrimp.Backend.Core.Extensions; namespace Iceshrimp.Backend.Core.Services; -public class EventService +public class EventService : ISingletonService { public event EventHandler? NotePublished; public event EventHandler? NoteUpdated; diff --git a/Iceshrimp.Backend/Core/Services/FollowupTaskService.cs b/Iceshrimp.Backend/Core/Services/FollowupTaskService.cs index 5c4522ab..caa7167e 100644 --- a/Iceshrimp.Backend/Core/Services/FollowupTaskService.cs +++ b/Iceshrimp.Backend/Core/Services/FollowupTaskService.cs @@ -1,6 +1,11 @@ +using Iceshrimp.Backend.Core.Extensions; + namespace Iceshrimp.Backend.Core.Services; -public class FollowupTaskService(IServiceScopeFactory serviceScopeFactory, ILogger logger) +public class FollowupTaskService( + IServiceScopeFactory serviceScopeFactory, + ILogger logger +) : IScopedService { public bool IsBackgroundWorker { get; private set; } diff --git a/Iceshrimp.Backend/Core/Services/HttpRequestService.cs b/Iceshrimp.Backend/Core/Services/HttpRequestService.cs index d76552c0..1e99a584 100644 --- a/Iceshrimp.Backend/Core/Services/HttpRequestService.cs +++ b/Iceshrimp.Backend/Core/Services/HttpRequestService.cs @@ -3,12 +3,13 @@ using System.Net.Http.Headers; using System.Security.Cryptography; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.Cryptography; using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Services; -public class HttpRequestService(IOptions options) +public class HttpRequestService(IOptions options) : ISingletonService { private static HttpRequestMessage GenerateRequest( string url, HttpMethod method, diff --git a/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageProcessor.cs b/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageProcessor.cs index 6b351f0b..fe33fe08 100644 --- a/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageProcessor.cs +++ b/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageProcessor.cs @@ -2,13 +2,14 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using Microsoft.Extensions.Options; using static Iceshrimp.Backend.Core.Services.ImageProcessing.ImageVersion; namespace Iceshrimp.Backend.Core.Services.ImageProcessing; -public class ImageProcessor +public class ImageProcessor : ISingletonService { private readonly IOptionsMonitor _config; //TODO: support stripping of exif/icc metadata (without re-encoding) diff --git a/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageSharpProcessor.cs b/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageSharpProcessor.cs index 582d60de..448d5b6f 100644 --- a/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageSharpProcessor.cs +++ b/Iceshrimp.Backend/Core/Services/ImageProcessing/ImageSharpProcessor.cs @@ -1,5 +1,6 @@ using CommunityToolkit.HighPerformance; using Iceshrimp.Backend.Core.Configuration; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using Microsoft.Extensions.Options; using SixLabors.ImageSharp; @@ -13,7 +14,8 @@ using ImageSharpConfig = SixLabors.ImageSharp.Configuration; namespace Iceshrimp.Backend.Core.Services.ImageProcessing; -public class ImageSharpProcessor : ImageProcessorBase, IImageProcessor +public class ImageSharpProcessor : ImageProcessorBase, IImageProcessor, + ISingletonService, IConditionalService, IService { private readonly ILogger _logger; private readonly ImageSharpConfig _sharpConfig; @@ -22,6 +24,9 @@ public class ImageSharpProcessor : ImageProcessorBase, IImageProcessor public bool CanIdentify => true; public bool CanGenerateBlurhash => true; + public static bool Predicate(Config ctx) => + ctx.Storage.MediaProcessing.ImageProcessor is Enums.ImageProcessor.ImageSharp or Enums.ImageProcessor.LibVips; + public ImageSharpProcessor( ILogger logger, IOptions config ) : base("ImageSharp", 1) diff --git a/Iceshrimp.Backend/Core/Services/ImageProcessing/VipsProcessor.cs b/Iceshrimp.Backend/Core/Services/ImageProcessing/VipsProcessor.cs index e166b745..51444d10 100644 --- a/Iceshrimp.Backend/Core/Services/ImageProcessing/VipsProcessor.cs +++ b/Iceshrimp.Backend/Core/Services/ImageProcessing/VipsProcessor.cs @@ -1,19 +1,28 @@ using System.Runtime.InteropServices; using CommunityToolkit.HighPerformance; +using Iceshrimp.Backend.Core.Configuration; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using NetVips; using Iceshrimp.MimeTypes; using SixLabors.ImageSharp.PixelFormats; +using Enums = NetVips.Enums; namespace Iceshrimp.Backend.Core.Services.ImageProcessing; -public class VipsProcessor : ImageProcessorBase, IImageProcessor +public class VipsProcessor : ImageProcessorBase, IImageProcessor, + ISingletonService, IConditionalService, IService { private readonly ILogger _logger; public bool CanIdentify => true; public bool CanGenerateBlurhash => true; + static int IService.Priority => -1; + + public static bool Predicate(Config ctx) => + ctx.Storage.MediaProcessing.ImageProcessor == Configuration.Enums.ImageProcessor.LibVips; + public VipsProcessor(ILogger logger) : base("LibVips", 0) { _logger = logger; diff --git a/Iceshrimp.Backend/Core/Services/ImportExportService.cs b/Iceshrimp.Backend/Core/Services/ImportExportService.cs index fd802264..b197df04 100644 --- a/Iceshrimp.Backend/Core/Services/ImportExportService.cs +++ b/Iceshrimp.Backend/Core/Services/ImportExportService.cs @@ -15,7 +15,7 @@ public class ImportExportService( CacheService cacheSvc, UserService userSvc, ActivityPub.UserResolver userResolver -) +) : IScopedService { public async Task ExportFollowingAsync(User user) { diff --git a/Iceshrimp.Backend/Core/Services/InstanceService.cs b/Iceshrimp.Backend/Core/Services/InstanceService.cs index 3e5aa2cf..ef123627 100644 --- a/Iceshrimp.Backend/Core/Services/InstanceService.cs +++ b/Iceshrimp.Backend/Core/Services/InstanceService.cs @@ -8,7 +8,11 @@ using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; -public class InstanceService(DatabaseContext db, HttpClient httpClient, ILogger logger) +public class InstanceService( + DatabaseContext db, + HttpClient httpClient, + ILogger logger +) : IScopedService { private static readonly AsyncKeyedLocker KeyedLocker = new(o => { diff --git a/Iceshrimp.Backend/Core/Services/MetaService.cs b/Iceshrimp.Backend/Core/Services/MetaService.cs index 080db7fc..e480b640 100644 --- a/Iceshrimp.Backend/Core/Services/MetaService.cs +++ b/Iceshrimp.Backend/Core/Services/MetaService.cs @@ -1,10 +1,11 @@ using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; -public class MetaService([FromKeyedServices("cache")] DatabaseContext db) +public class MetaService([FromKeyedServices("cache")] DatabaseContext db) : IScopedService { public async Task Get(Meta meta) => meta.ConvertGet(await Fetch(meta.Key)); diff --git a/Iceshrimp.Backend/Core/Services/NoteService.cs b/Iceshrimp.Backend/Core/Services/NoteService.cs index db00a9b2..df13161f 100644 --- a/Iceshrimp.Backend/Core/Services/NoteService.cs +++ b/Iceshrimp.Backend/Core/Services/NoteService.cs @@ -19,8 +19,6 @@ using static Iceshrimp.Parsing.MfmNodeTypes; namespace Iceshrimp.Backend.Core.Services; -[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor", - Justification = "We need IOptionsSnapshot for config hot reload")] public class NoteService( ILogger logger, DatabaseContext db, @@ -43,7 +41,7 @@ public class NoteService( PollService pollSvc, ActivityPub.FederationControlService fedCtrlSvc, PolicyService policySvc -) +) : IScopedService { private const int DefaultRecursionLimit = 100; diff --git a/Iceshrimp.Backend/Core/Services/NotificationService.cs b/Iceshrimp.Backend/Core/Services/NotificationService.cs index dca65d20..ae39b164 100644 --- a/Iceshrimp.Backend/Core/Services/NotificationService.cs +++ b/Iceshrimp.Backend/Core/Services/NotificationService.cs @@ -8,10 +8,9 @@ using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; public class NotificationService( - [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] DatabaseContext db, EventService eventSvc -) +) : IScopedService { public async Task GenerateMentionNotifications(Note note, IReadOnlyCollection mentionedLocalUserIds) { diff --git a/Iceshrimp.Backend/Core/Services/ObjectStorageService.cs b/Iceshrimp.Backend/Core/Services/ObjectStorageService.cs index 88d83cd5..3b9504ef 100644 --- a/Iceshrimp.Backend/Core/Services/ObjectStorageService.cs +++ b/Iceshrimp.Backend/Core/Services/ObjectStorageService.cs @@ -3,6 +3,7 @@ using System.Net.Http.Headers; using System.Text; using Carbon.Storage; using Iceshrimp.Backend.Core.Configuration; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.ObjectStorage.Core.Models; using Iceshrimp.ObjectStorage.Core.Security; @@ -11,7 +12,7 @@ using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Services; -public class ObjectStorageService(IOptions config, HttpClient httpClient) +public class ObjectStorageService(IOptions config, HttpClient httpClient) : ISingletonService { private readonly string? _accessUrl = config.Value.ObjectStorage?.AccessUrl; diff --git a/Iceshrimp.Backend/Core/Services/PolicyService.cs b/Iceshrimp.Backend/Core/Services/PolicyService.cs index e9199153..d2b77fe8 100644 --- a/Iceshrimp.Backend/Core/Services/PolicyService.cs +++ b/Iceshrimp.Backend/Core/Services/PolicyService.cs @@ -4,6 +4,7 @@ using System.Text.Json; using Iceshrimp.AssemblyUtils; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Shared.Configuration; @@ -11,7 +12,7 @@ using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; -public class PolicyService(IServiceScopeFactory scopeFactory) +public class PolicyService(IServiceScopeFactory scopeFactory) : ISingletonService { private bool _initialized; private IRejectPolicy[] _rejectPolicies = []; diff --git a/Iceshrimp.Backend/Core/Services/PollService.cs b/Iceshrimp.Backend/Core/Services/PollService.cs index c998a4d5..2127b5e1 100644 --- a/Iceshrimp.Backend/Core/Services/PollService.cs +++ b/Iceshrimp.Backend/Core/Services/PollService.cs @@ -1,5 +1,6 @@ using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; @@ -9,7 +10,7 @@ public class PollService( ActivityPub.ActivityRenderer activityRenderer, ActivityPub.UserRenderer userRenderer, ActivityPub.ActivityDeliverService deliverSvc -) +) : IScopedService { public async Task RegisterPollVote(PollVote pollVote, Poll poll, Note note, bool updateVotersCount = true) { diff --git a/Iceshrimp.Backend/Core/Services/RazorViewRenderService.cs b/Iceshrimp.Backend/Core/Services/RazorViewRenderService.cs index 440fbe2d..3959adc1 100644 --- a/Iceshrimp.Backend/Core/Services/RazorViewRenderService.cs +++ b/Iceshrimp.Backend/Core/Services/RazorViewRenderService.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Text.Encodings.Web; +using Iceshrimp.Backend.Core.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding; @@ -15,7 +16,7 @@ public class RazorViewRenderService( ITempDataProvider tempDataProvider, IHttpContextAccessor httpContextAccessor, IRazorPageActivator activator -) +) : ISingletonService { private async Task RenderAsync(string path, T model, TextWriter writer) where T : PageModel { diff --git a/Iceshrimp.Backend/Core/Services/RelayService.cs b/Iceshrimp.Backend/Core/Services/RelayService.cs index 1d2b4a1e..3609dcd0 100644 --- a/Iceshrimp.Backend/Core/Services/RelayService.cs +++ b/Iceshrimp.Backend/Core/Services/RelayService.cs @@ -1,5 +1,6 @@ using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Backend.Core.Middleware; using Microsoft.EntityFrameworkCore; @@ -12,7 +13,7 @@ public class RelayService( ActivityPub.ActivityRenderer activityRenderer, ActivityPub.ActivityDeliverService deliverSvc, ActivityPub.UserRenderer userRenderer -) +) : IScopedService { public async Task SubscribeToRelay(string uri) { diff --git a/Iceshrimp.Backend/Core/Services/StorageMaintenanceService.cs b/Iceshrimp.Backend/Core/Services/StorageMaintenanceService.cs index 69b14176..fa900da9 100644 --- a/Iceshrimp.Backend/Core/Services/StorageMaintenanceService.cs +++ b/Iceshrimp.Backend/Core/Services/StorageMaintenanceService.cs @@ -16,7 +16,7 @@ public class StorageMaintenanceService( [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] IOptionsSnapshot options, ILogger logger -) +) : IScopedService { public async Task MigrateLocalFiles(bool purge) { diff --git a/Iceshrimp.Backend/Core/Services/StreamingService.cs b/Iceshrimp.Backend/Core/Services/StreamingService.cs index 52128b39..358f1915 100644 --- a/Iceshrimp.Backend/Core/Services/StreamingService.cs +++ b/Iceshrimp.Backend/Core/Services/StreamingService.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using Iceshrimp.Backend.Controllers.Web.Renderers; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.SignalR; using Iceshrimp.Backend.SignalR.Helpers; using Iceshrimp.Shared.Schemas.SignalR; @@ -9,7 +10,7 @@ using Microsoft.AspNetCore.SignalR; namespace Iceshrimp.Backend.Core.Services; -public sealed class StreamingService +public sealed class StreamingService : ISingletonService { private readonly ConcurrentDictionary _connections = []; private readonly EventService _eventSvc; diff --git a/Iceshrimp.Backend/Core/Services/SystemUserService.cs b/Iceshrimp.Backend/Core/Services/SystemUserService.cs index 3c01d6f8..8e3b2fe9 100644 --- a/Iceshrimp.Backend/Core/Services/SystemUserService.cs +++ b/Iceshrimp.Backend/Core/Services/SystemUserService.cs @@ -2,12 +2,13 @@ using System.Security.Cryptography; using AsyncKeyedLock; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Helpers; using Microsoft.EntityFrameworkCore; namespace Iceshrimp.Backend.Core.Services; -public class SystemUserService(ILogger logger, DatabaseContext db) +public class SystemUserService(ILogger logger, DatabaseContext db) : IScopedService { private static readonly AsyncKeyedLocker KeyedLocker = new(o => { diff --git a/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs b/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs index 1a0d51da..85b61513 100644 --- a/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs +++ b/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs @@ -14,7 +14,10 @@ namespace Iceshrimp.Backend.Core.Services; using MentionTuple = (List mentions, Dictionary<(string usernameLower, string webDomain), string> splitDomainMapping); -public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver, IOptions config) +public class UserProfileMentionsResolver( + ActivityPub.UserResolver userResolver, + IOptions config +) : IScopedService { public async Task ResolveMentions(ASActor actor, string? host) { diff --git a/Iceshrimp.Backend/Core/Services/UserService.cs b/Iceshrimp.Backend/Core/Services/UserService.cs index 36ba22d8..7eec72aa 100644 --- a/Iceshrimp.Backend/Core/Services/UserService.cs +++ b/Iceshrimp.Backend/Core/Services/UserService.cs @@ -22,7 +22,6 @@ using static Iceshrimp.Parsing.MfmNodeTypes; namespace Iceshrimp.Backend.Core.Services; public class UserService( - [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] IOptionsSnapshot security, IOptions instance, ILogger logger, @@ -40,7 +39,7 @@ public class UserService( EventService eventSvc, WebFingerService webFingerSvc, ActivityPub.FederationControlService fedCtrlSvc -) +) : IScopedService { private static readonly AsyncKeyedLocker KeyedLocker = new(o => { diff --git a/Iceshrimp.Backend/Iceshrimp.Backend.csproj b/Iceshrimp.Backend/Iceshrimp.Backend.csproj index 18ef7611..ce30aa30 100644 --- a/Iceshrimp.Backend/Iceshrimp.Backend.csproj +++ b/Iceshrimp.Backend/Iceshrimp.Backend.csproj @@ -47,7 +47,7 @@ - + diff --git a/Iceshrimp.Backend/Startup.cs b/Iceshrimp.Backend/Startup.cs index ce9da7c3..623541f1 100644 --- a/Iceshrimp.Backend/Startup.cs +++ b/Iceshrimp.Backend/Startup.cs @@ -38,7 +38,6 @@ builder.Services.AddRazorPages(); builder.Services.AddRazorComponents(); builder.Services.AddAntiforgery(o => o.Cookie.Name = "CSRF-Token"); builder.Services.AddMiddleware(); - builder.Services.AddServices(builder.Configuration); builder.Services.ConfigureServices(builder.Configuration);