[backend] Apply formatting rules
This commit is contained in:
parent
571f2274f2
commit
df3a7bdfe5
82 changed files with 471 additions and 462 deletions
|
@ -3,7 +3,6 @@ using System.Net.Mime;
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Federation;
|
using Iceshrimp.Backend.Controllers.Federation;
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
@ -12,6 +11,7 @@ using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
using Iceshrimp.Backend.Core.Helpers;
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
@ -125,7 +125,7 @@ public class AdminController(
|
||||||
.FirstOrDefaultAsync(p => p.Id == id && p.UserHost == null);
|
.FirstOrDefaultAsync(p => p.Id == id && p.UserHost == null);
|
||||||
if (note == null) return NotFound();
|
if (note == null) return NotFound();
|
||||||
var rendered = await noteRenderer.RenderAsync(note);
|
var rendered = await noteRenderer.RenderAsync(note);
|
||||||
var compacted = LdHelpers.Compact(rendered);
|
var compacted = rendered.Compact();
|
||||||
return Ok(compacted);
|
return Ok(compacted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Helpers;
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Federation.Attributes;
|
using Iceshrimp.Backend.Controllers.Federation.Attributes;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
@ -10,6 +9,7 @@ using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Queues;
|
using Iceshrimp.Backend.Core.Queues;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
|
@ -4,11 +4,11 @@ using System.Text;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
using Iceshrimp.Backend.Controllers.Federation.Attributes;
|
using Iceshrimp.Backend.Controllers.Federation.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Federation.Schemas;
|
using Iceshrimp.Backend.Controllers.Federation.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Federation.WebFinger;
|
using Iceshrimp.Backend.Core.Federation.WebFinger;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class MediaController(DriveService driveSvc, DatabaseContext db) : Contro
|
||||||
Description = file.Comment,
|
Description = file.Comment,
|
||||||
PreviewUrl = file.PublicThumbnailUrl,
|
PreviewUrl = file.PublicThumbnailUrl,
|
||||||
RemoteUrl = file.Uri,
|
RemoteUrl = file.Uri,
|
||||||
Sensitive = file.IsSensitive,
|
Sensitive = file.IsSensitive
|
||||||
//Metadata = TODO,
|
//Metadata = TODO,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,20 @@ public class NoteRenderer(
|
||||||
EmojiService emojiSvc
|
EmojiService emojiSvc
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
private static readonly FilterResultEntity InaccessibleFilter = new()
|
||||||
|
{
|
||||||
|
Filter = new FilterEntity
|
||||||
|
{
|
||||||
|
Title = "HideInaccessible",
|
||||||
|
FilterAction = "hide",
|
||||||
|
Id = "0",
|
||||||
|
Context = ["home", "thread", "notifications", "account", "public"],
|
||||||
|
Keywords = [new FilterKeyword("RE: \ud83d\udd12", 0, 0)],
|
||||||
|
ExpiresAt = null
|
||||||
|
},
|
||||||
|
KeywordMatches = ["RE: \ud83d\udd12"] // lock emoji
|
||||||
|
};
|
||||||
|
|
||||||
public async Task<StatusEntity> RenderAsync(
|
public async Task<StatusEntity> RenderAsync(
|
||||||
Note note, User? user, Filter.FilterContext? filterContext = null, NoteRendererDto? data = null, int recurse = 2
|
Note note, User? user, Filter.FilterContext? filterContext = null, NoteRendererDto? data = null, int recurse = 2
|
||||||
)
|
)
|
||||||
|
@ -150,20 +164,6 @@ public class NoteRenderer(
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly FilterResultEntity InaccessibleFilter = new()
|
|
||||||
{
|
|
||||||
Filter = new FilterEntity
|
|
||||||
{
|
|
||||||
Title = "HideInaccessible",
|
|
||||||
FilterAction = "hide",
|
|
||||||
Id = "0",
|
|
||||||
Context = ["home", "thread", "notifications", "account", "public"],
|
|
||||||
Keywords = [new FilterKeyword("RE: \ud83d\udd12", 0, 0)],
|
|
||||||
ExpiresAt = null
|
|
||||||
},
|
|
||||||
KeywordMatches = ["RE: \ud83d\udd12"] // lock emoji
|
|
||||||
};
|
|
||||||
|
|
||||||
public async Task<List<StatusEdit>> RenderHistoryAsync(Note note)
|
public async Task<List<StatusEdit>> RenderHistoryAsync(Note note)
|
||||||
{
|
{
|
||||||
var edits = await db.NoteEdits.Where(p => p.Note == note).OrderBy(p => p.Id).ToListAsync();
|
var edits = await db.NoteEdits.Where(p => p.Note == note).OrderBy(p => p.Id).ToListAsync();
|
||||||
|
@ -430,13 +430,13 @@ public class NoteRenderer(
|
||||||
public List<AttachmentEntity>? Attachments;
|
public List<AttachmentEntity>? Attachments;
|
||||||
public List<string>? BookmarkedNotes;
|
public List<string>? BookmarkedNotes;
|
||||||
public List<EmojiEntity>? Emoji;
|
public List<EmojiEntity>? Emoji;
|
||||||
|
public List<Filter>? Filters;
|
||||||
public List<string>? LikedNotes;
|
public List<string>? LikedNotes;
|
||||||
public List<MentionEntity>? Mentions;
|
public List<MentionEntity>? Mentions;
|
||||||
public List<string>? PinnedNotes;
|
public List<string>? PinnedNotes;
|
||||||
public List<PollEntity>? Polls;
|
public List<PollEntity>? Polls;
|
||||||
public List<ReactionEntity>? Reactions;
|
public List<ReactionEntity>? Reactions;
|
||||||
public List<string>? Renotes;
|
public List<string>? Renotes;
|
||||||
public List<Filter>? Filters;
|
|
||||||
|
|
||||||
public bool Source;
|
public bool Source;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
|
||||||
public class StatusEntity : IEntity, ICloneable
|
public class StatusEntity : IEntity, ICloneable
|
||||||
{
|
{
|
||||||
[J("id")] public required string Id { get; set; }
|
[JI] public string? MastoReplyUserId;
|
||||||
[J("text")] public required string? Text { get; set; }
|
[J("text")] public required string? Text { get; set; }
|
||||||
[J("content")] public required string? Content { get; set; }
|
[J("content")] public required string? Content { get; set; }
|
||||||
[J("uri")] public required string Uri { get; set; }
|
[J("uri")] public required string Uri { get; set; }
|
||||||
|
@ -52,7 +52,8 @@ public class StatusEntity : IEntity, ICloneable
|
||||||
|
|
||||||
[J("language")] public string? Language => null; //FIXME
|
[J("language")] public string? Language => null; //FIXME
|
||||||
|
|
||||||
[JI] public string? MastoReplyUserId;
|
public object Clone() => MemberwiseClone();
|
||||||
|
[J("id")] public required string Id { get; set; }
|
||||||
|
|
||||||
public static string EncodeVisibility(Note.NoteVisibility visibility)
|
public static string EncodeVisibility(Note.NoteVisibility visibility)
|
||||||
{
|
{
|
||||||
|
@ -77,8 +78,6 @@ public class StatusEntity : IEntity, ICloneable
|
||||||
_ => throw GracefulException.BadRequest($"Unknown visibility: {visibility}")
|
_ => throw GracefulException.BadRequest($"Unknown visibility: {visibility}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Clone() => MemberwiseClone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StatusContext
|
public class StatusContext
|
||||||
|
|
|
@ -97,6 +97,8 @@ public abstract class StatusSchemas
|
||||||
|
|
||||||
public class ReblogRequest
|
public class ReblogRequest
|
||||||
{
|
{
|
||||||
[B(Name = "visibility")] [J("visibility")] public string? Visibility { get; set; }
|
[B(Name = "visibility")]
|
||||||
|
[J("visibility")]
|
||||||
|
public string? Visibility { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,6 @@ using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Helpers;
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
||||||
using static Iceshrimp.Parsing.MfmNodeTypes;
|
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
|
@ -20,6 +19,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using static Iceshrimp.Parsing.MfmNodeTypes;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Mastodon;
|
namespace Iceshrimp.Backend.Controllers.Mastodon;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ public class StatusController(
|
||||||
var note = await db.Notes
|
var note = await db.Notes
|
||||||
.Where(p => p.Id == id)
|
.Where(p => p.Id == id)
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.FilterHidden(user, db, filterOutgoingBlocks: false, filterMutes: false,
|
.FilterHidden(user, db, false, false,
|
||||||
filterMentions: false)
|
filterMentions: false)
|
||||||
.EnsureVisibleFor(user)
|
.EnsureVisibleFor(user)
|
||||||
.PrecomputeVisibilities(user)
|
.PrecomputeVisibilities(user)
|
||||||
|
@ -89,7 +89,7 @@ public class StatusController(
|
||||||
var note = await db.Notes
|
var note = await db.Notes
|
||||||
.Where(p => p.Id == id)
|
.Where(p => p.Id == id)
|
||||||
.EnsureVisibleFor(user)
|
.EnsureVisibleFor(user)
|
||||||
.FilterHidden(user, db, filterOutgoingBlocks: false, filterMutes: false)
|
.FilterHidden(user, db, false, false)
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
|
|
|
@ -63,12 +63,6 @@ public class DirectChannel(WebSocketConnection connection) : IChannel
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoteWithVisibilities(Note note)
|
|
||||||
{
|
|
||||||
public readonly Note Note = note;
|
|
||||||
public Note? Renote = note.Renote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
||||||
{
|
{
|
||||||
var renote = note.Renote == null && rendered.Renote != null;
|
var renote = note.Renote == null && rendered.Renote != null;
|
||||||
|
@ -149,4 +143,10 @@ public class DirectChannel(WebSocketConnection connection) : IChannel
|
||||||
_logger.LogError("Event handler OnNoteUpdated threw exception: {e}", e);
|
_logger.LogError("Event handler OnNoteUpdated threw exception: {e}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NoteWithVisibilities(Note note)
|
||||||
|
{
|
||||||
|
public readonly Note Note = note;
|
||||||
|
public Note? Renote = note.Renote;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -13,13 +13,13 @@ public class HashtagChannel(WebSocketConnection connection, bool local) : IChann
|
||||||
private readonly ILogger<HashtagChannel> _logger =
|
private readonly ILogger<HashtagChannel> _logger =
|
||||||
connection.Scope.ServiceProvider.GetRequiredService<ILogger<HashtagChannel>>();
|
connection.Scope.ServiceProvider.GetRequiredService<ILogger<HashtagChannel>>();
|
||||||
|
|
||||||
|
private readonly WriteLockingList<string> _tags = [];
|
||||||
|
|
||||||
public string Name => local ? "hashtag:local" : "hashtag";
|
public string Name => local ? "hashtag:local" : "hashtag";
|
||||||
public List<string> Scopes => ["read:statuses"];
|
public List<string> Scopes => ["read:statuses"];
|
||||||
public bool IsSubscribed => _tags.Count != 0;
|
public bool IsSubscribed => _tags.Count != 0;
|
||||||
public bool IsAggregate => true;
|
public bool IsAggregate => true;
|
||||||
|
|
||||||
private readonly WriteLockingList<string> _tags = [];
|
|
||||||
|
|
||||||
public async Task Subscribe(StreamingRequestMessage msg)
|
public async Task Subscribe(StreamingRequestMessage msg)
|
||||||
{
|
{
|
||||||
if (msg.Tag == null)
|
if (msg.Tag == null)
|
||||||
|
@ -79,12 +79,6 @@ public class HashtagChannel(WebSocketConnection connection, bool local) : IChann
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoteWithVisibilities(Note note)
|
|
||||||
{
|
|
||||||
public readonly Note Note = note;
|
|
||||||
public Note? Renote = note.Renote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
||||||
{
|
{
|
||||||
var renote = note.Renote == null && rendered.Renote != null;
|
var renote = note.Renote == null && rendered.Renote != null;
|
||||||
|
@ -169,4 +163,10 @@ public class HashtagChannel(WebSocketConnection connection, bool local) : IChann
|
||||||
_logger.LogError("Event handler OnNoteDeleted threw exception: {e}", e);
|
_logger.LogError("Event handler OnNoteDeleted threw exception: {e}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NoteWithVisibilities(Note note)
|
||||||
|
{
|
||||||
|
public readonly Note Note = note;
|
||||||
|
public Note? Renote = note.Renote;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,18 +12,19 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Streaming.Channels;
|
||||||
|
|
||||||
public class ListChannel(WebSocketConnection connection) : IChannel
|
public class ListChannel(WebSocketConnection connection) : IChannel
|
||||||
{
|
{
|
||||||
|
private readonly WriteLockingList<string> _lists = [];
|
||||||
|
|
||||||
private readonly ILogger<ListChannel> _logger =
|
private readonly ILogger<ListChannel> _logger =
|
||||||
connection.Scope.ServiceProvider.GetRequiredService<ILogger<ListChannel>>();
|
connection.Scope.ServiceProvider.GetRequiredService<ILogger<ListChannel>>();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, WriteLockingList<string>> _members = [];
|
||||||
|
private IEnumerable<string> _applicableUserIds = [];
|
||||||
|
|
||||||
public string Name => "list";
|
public string Name => "list";
|
||||||
public List<string> Scopes => ["read:statuses"];
|
public List<string> Scopes => ["read:statuses"];
|
||||||
public bool IsSubscribed => _lists.Count != 0;
|
public bool IsSubscribed => _lists.Count != 0;
|
||||||
public bool IsAggregate => true;
|
public bool IsAggregate => true;
|
||||||
|
|
||||||
private readonly WriteLockingList<string> _lists = [];
|
|
||||||
private readonly ConcurrentDictionary<string, WriteLockingList<string>> _members = [];
|
|
||||||
private IEnumerable<string> _applicableUserIds = [];
|
|
||||||
|
|
||||||
public async Task Subscribe(StreamingRequestMessage msg)
|
public async Task Subscribe(StreamingRequestMessage msg)
|
||||||
{
|
{
|
||||||
if (msg.List == null)
|
if (msg.List == null)
|
||||||
|
@ -101,12 +102,6 @@ public class ListChannel(WebSocketConnection connection) : IChannel
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoteWithVisibilities(Note note)
|
|
||||||
{
|
|
||||||
public readonly Note Note = note;
|
|
||||||
public Note? Renote = note.Renote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
||||||
{
|
{
|
||||||
var renote = note.Renote == null && rendered.Renote != null;
|
var renote = note.Renote == null && rendered.Renote != null;
|
||||||
|
@ -218,4 +213,10 @@ public class ListChannel(WebSocketConnection connection) : IChannel
|
||||||
_logger.LogError("Event handler OnListUpdated threw exception: {e}", e);
|
_logger.LogError("Event handler OnListUpdated threw exception: {e}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NoteWithVisibilities(Note note)
|
||||||
|
{
|
||||||
|
public readonly Note Note = note;
|
||||||
|
public Note? Renote = note.Renote;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -71,12 +71,6 @@ public class PublicChannel(
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoteWithVisibilities(Note note)
|
|
||||||
{
|
|
||||||
public readonly Note Note = note;
|
|
||||||
public Note? Renote = note.Renote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
||||||
{
|
{
|
||||||
var renote = note.Renote == null && rendered.Renote != null;
|
var renote = note.Renote == null && rendered.Renote != null;
|
||||||
|
@ -160,4 +154,10 @@ public class PublicChannel(
|
||||||
_logger.LogError("Event handler OnNoteDeleted threw exception: {e}", e);
|
_logger.LogError("Event handler OnNoteDeleted threw exception: {e}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NoteWithVisibilities(Note note)
|
||||||
|
{
|
||||||
|
public readonly Note Note = note;
|
||||||
|
public Note? Renote = note.Renote;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -83,12 +83,6 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoteWithVisibilities(Note note)
|
|
||||||
{
|
|
||||||
public readonly Note Note = note;
|
|
||||||
public Note? Renote = note.Renote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
private static StatusEntity EnforceRenoteReplyVisibility(StatusEntity rendered, NoteWithVisibilities note)
|
||||||
{
|
{
|
||||||
var renote = note.Renote == null && rendered.Renote != null;
|
var renote = note.Renote == null && rendered.Renote != null;
|
||||||
|
@ -206,4 +200,10 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
_logger.LogError("Event handler OnNotification threw exception: {e}", e);
|
_logger.LogError("Event handler OnNotification threw exception: {e}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NoteWithVisibilities(Note note)
|
||||||
|
{
|
||||||
|
public readonly Note Note = note;
|
||||||
|
public Note? Renote = note.Renote;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -23,18 +23,18 @@ public sealed class WebSocketConnection(
|
||||||
CancellationToken ct
|
CancellationToken ct
|
||||||
) : IDisposable
|
) : IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly WriteLockingList<string> _blockedBy = [];
|
||||||
|
private readonly WriteLockingList<string> _blocking = [];
|
||||||
private readonly SemaphoreSlim _lock = new(1);
|
private readonly SemaphoreSlim _lock = new(1);
|
||||||
|
private readonly WriteLockingList<string> _muting = [];
|
||||||
public readonly List<IChannel> Channels = [];
|
public readonly List<IChannel> Channels = [];
|
||||||
public readonly EventService EventService = eventSvc;
|
public readonly EventService EventService = eventSvc;
|
||||||
|
public readonly WriteLockingList<Filter> Filters = [];
|
||||||
|
public readonly WriteLockingList<string> Following = [];
|
||||||
public readonly IServiceScope Scope = scopeFactory.CreateScope();
|
public readonly IServiceScope Scope = scopeFactory.CreateScope();
|
||||||
public readonly IServiceScopeFactory ScopeFactory = scopeFactory;
|
public readonly IServiceScopeFactory ScopeFactory = scopeFactory;
|
||||||
public readonly OauthToken Token = token;
|
public readonly OauthToken Token = token;
|
||||||
public List<string> HiddenFromHome = [];
|
public List<string> HiddenFromHome = [];
|
||||||
public readonly WriteLockingList<Filter> Filters = [];
|
|
||||||
public readonly WriteLockingList<string> Following = [];
|
|
||||||
private readonly WriteLockingList<string> _blocking = [];
|
|
||||||
private readonly WriteLockingList<string> _blockedBy = [];
|
|
||||||
private readonly WriteLockingList<string> _muting = [];
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -362,9 +362,9 @@ public sealed class WebSocketConnection(
|
||||||
(note.Renote?.User != null &&
|
(note.Renote?.User != null &&
|
||||||
(IsFiltered(note.Renote.User) ||
|
(IsFiltered(note.Renote.User) ||
|
||||||
IsFilteredMentions(note.Renote.Mentions))) ||
|
IsFilteredMentions(note.Renote.Mentions))) ||
|
||||||
note.Renote?.Renote?.User != null &&
|
(note.Renote?.Renote?.User != null &&
|
||||||
(IsFiltered(note.Renote.Renote.User) ||
|
(IsFiltered(note.Renote.Renote.User) ||
|
||||||
IsFilteredMentions(note.Renote.Renote.Mentions));
|
IsFilteredMentions(note.Renote.Renote.Mentions)));
|
||||||
|
|
||||||
public async Task CloseAsync(WebSocketCloseStatus status)
|
public async Task CloseAsync(WebSocketCloseStatus status)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,13 +4,13 @@ using System.Net.Mime;
|
||||||
using AsyncKeyedLock;
|
using AsyncKeyedLock;
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Helpers;
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -45,7 +45,7 @@ public class NoteController(
|
||||||
var note = await db.Notes.Where(p => p.Id == id)
|
var note = await db.Notes.Where(p => p.Id == id)
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.EnsureVisibleFor(user)
|
.EnsureVisibleFor(user)
|
||||||
.FilterHidden(user, db, filterOutgoingBlocks: false, filterMutes: false)
|
.FilterHidden(user, db, false, false)
|
||||||
.PrecomputeVisibilities(user)
|
.PrecomputeVisibilities(user)
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.NotFound("Note not found");
|
throw GracefulException.NotFound("Note not found");
|
||||||
|
@ -65,7 +65,7 @@ public class NoteController(
|
||||||
|
|
||||||
var note = await db.Notes.Where(p => p.Id == id)
|
var note = await db.Notes.Where(p => p.Id == id)
|
||||||
.EnsureVisibleFor(user)
|
.EnsureVisibleFor(user)
|
||||||
.FilterHidden(user, db, filterOutgoingBlocks: false, filterMutes: false)
|
.FilterHidden(user, db, false, false)
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.NotFound("Note not found");
|
throw GracefulException.NotFound("Note not found");
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public class NoteController(
|
||||||
|
|
||||||
var note = await db.Notes.Where(p => p.Id == id)
|
var note = await db.Notes.Where(p => p.Id == id)
|
||||||
.EnsureVisibleFor(user)
|
.EnsureVisibleFor(user)
|
||||||
.FilterHidden(user, db, filterOutgoingBlocks: false, filterMutes: false)
|
.FilterHidden(user, db, false, false)
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.NotFound("Note not found");
|
throw GracefulException.NotFound("Note not found");
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ using System.Net.Mime;
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Helpers;
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
@ -219,10 +219,10 @@ public class NoteRenderer(
|
||||||
public class NoteRendererDto
|
public class NoteRendererDto
|
||||||
{
|
{
|
||||||
public List<NoteAttachment>? Attachments;
|
public List<NoteAttachment>? Attachments;
|
||||||
public List<NoteReactionSchema>? Reactions;
|
public List<EmojiResponse>? Emoji;
|
||||||
public List<UserResponse>? Users;
|
|
||||||
public List<Filter>? Filters;
|
public List<Filter>? Filters;
|
||||||
public List<string>? LikedNotes;
|
public List<string>? LikedNotes;
|
||||||
public List<EmojiResponse>? Emoji;
|
public List<NoteReactionSchema>? Reactions;
|
||||||
|
public List<UserResponse>? Users;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Renderers;
|
namespace Iceshrimp.Backend.Controllers.Renderers;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Renderers;
|
namespace Iceshrimp.Backend.Controllers.Renderers;
|
||||||
|
@ -91,14 +91,14 @@ public class UserProfileRenderer(DatabaseContext db)
|
||||||
|
|
||||||
public class RelationData
|
public class RelationData
|
||||||
{
|
{
|
||||||
public required string UserId;
|
public required bool IsBlocking;
|
||||||
public required bool IsSelf;
|
|
||||||
public required bool IsFollowing;
|
|
||||||
public required bool IsFollowedBy;
|
public required bool IsFollowedBy;
|
||||||
|
public required bool IsFollowing;
|
||||||
|
public required bool IsMuting;
|
||||||
public required bool IsRequested;
|
public required bool IsRequested;
|
||||||
public required bool IsRequestedBy;
|
public required bool IsRequestedBy;
|
||||||
public required bool IsBlocking;
|
public required bool IsSelf;
|
||||||
public required bool IsMuting;
|
public required string UserId;
|
||||||
|
|
||||||
public static implicit operator Relations(RelationData data)
|
public static implicit operator Relations(RelationData data)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ using System.Net.Mime;
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
|
@ -2,12 +2,12 @@ using System.Net.Mime;
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
|
@ -17,10 +17,10 @@ public sealed class Config
|
||||||
|
|
||||||
public sealed class InstanceSection
|
public sealed class InstanceSection
|
||||||
{
|
{
|
||||||
public readonly string Version;
|
|
||||||
public readonly string Codename;
|
public readonly string Codename;
|
||||||
public readonly string? CommitHash;
|
public readonly string? CommitHash;
|
||||||
public readonly string RawVersion;
|
public readonly string RawVersion;
|
||||||
|
public readonly string Version;
|
||||||
|
|
||||||
public InstanceSection()
|
public InstanceSection()
|
||||||
{
|
{
|
||||||
|
@ -100,9 +100,9 @@ public sealed class Config
|
||||||
|
|
||||||
public sealed class StorageSection
|
public sealed class StorageSection
|
||||||
{
|
{
|
||||||
public readonly TimeSpan? MediaRetentionTimeSpan;
|
|
||||||
public readonly int? MaxCacheSizeBytes;
|
public readonly int? MaxCacheSizeBytes;
|
||||||
public readonly int? MaxUploadSizeBytes;
|
public readonly int? MaxUploadSizeBytes;
|
||||||
|
public readonly TimeSpan? MediaRetentionTimeSpan;
|
||||||
|
|
||||||
public bool CleanAvatars = false;
|
public bool CleanAvatars = false;
|
||||||
public bool CleanBanners = false;
|
public bool CleanBanners = false;
|
||||||
|
|
|
@ -14,6 +14,13 @@ public static class Enums
|
||||||
ObjectStorage = 1
|
ObjectStorage = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ImageProcessor
|
||||||
|
{
|
||||||
|
ImageSharp,
|
||||||
|
LibVips,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
public enum ItemVisibility
|
public enum ItemVisibility
|
||||||
{
|
{
|
||||||
Hide = 0,
|
Hide = 0,
|
||||||
|
@ -21,13 +28,6 @@ public static class Enums
|
||||||
Public = 2
|
Public = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Registrations
|
|
||||||
{
|
|
||||||
Closed = 0,
|
|
||||||
Invite = 1,
|
|
||||||
Open = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PublicPreview
|
public enum PublicPreview
|
||||||
{
|
{
|
||||||
Lockdown = 0,
|
Lockdown = 0,
|
||||||
|
@ -36,10 +36,10 @@ public static class Enums
|
||||||
Public = 3
|
Public = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ImageProcessor
|
public enum Registrations
|
||||||
{
|
{
|
||||||
ImageSharp,
|
Closed = 0,
|
||||||
LibVips,
|
Invite = 1,
|
||||||
None
|
Open = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,6 @@ public interface IEntity
|
||||||
|
|
||||||
public class EntityWrapper<T> : IEntity
|
public class EntityWrapper<T> : IEntity
|
||||||
{
|
{
|
||||||
public required string Id { get; init; }
|
|
||||||
public required T Entity { get; init; }
|
public required T Entity { get; init; }
|
||||||
|
public required string Id { get; init; }
|
||||||
}
|
}
|
|
@ -10,6 +10,13 @@ namespace Iceshrimp.Backend.Core.Database.Tables;
|
||||||
[Index("user_id")]
|
[Index("user_id")]
|
||||||
public class Filter
|
public class Filter
|
||||||
{
|
{
|
||||||
|
[PgName("filter_action_enum")]
|
||||||
|
public enum FilterAction
|
||||||
|
{
|
||||||
|
[PgName("warn")] Warn,
|
||||||
|
[PgName("hide")] Hide
|
||||||
|
}
|
||||||
|
|
||||||
[PgName("filter_context_enum")]
|
[PgName("filter_context_enum")]
|
||||||
public enum FilterContext
|
public enum FilterContext
|
||||||
{
|
{
|
||||||
|
@ -18,17 +25,11 @@ public class Filter
|
||||||
[PgName("threads")] Threads,
|
[PgName("threads")] Threads,
|
||||||
[PgName("notifications")] Notifications,
|
[PgName("notifications")] Notifications,
|
||||||
[PgName("accounts")] Accounts,
|
[PgName("accounts")] Accounts,
|
||||||
[PgName("public")] Public,
|
[PgName("public")] Public
|
||||||
}
|
}
|
||||||
|
|
||||||
[PgName("filter_action_enum")]
|
[Key]
|
||||||
public enum FilterAction
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
{
|
|
||||||
[PgName("warn")] Warn,
|
|
||||||
[PgName("hide")] Hide,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,7 @@ public class FollowRequest : IEntity
|
||||||
[StringLength(128)]
|
[StringLength(128)]
|
||||||
public string? RequestId { get; set; }
|
public string? RequestId { get; set; }
|
||||||
|
|
||||||
[Column("relationshipId")]
|
[Column("relationshipId")] public Guid? RelationshipId { get; set; }
|
||||||
public Guid? RelationshipId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// [Denormalized]
|
/// [Denormalized]
|
||||||
|
|
|
@ -80,8 +80,7 @@ public class Following
|
||||||
[StringLength(512)]
|
[StringLength(512)]
|
||||||
public string? FolloweeSharedInbox { get; set; }
|
public string? FolloweeSharedInbox { get; set; }
|
||||||
|
|
||||||
[Column("relationshipId")]
|
[Column("relationshipId")] public Guid? RelationshipId { get; set; }
|
||||||
public Guid? RelationshipId { get; set; }
|
|
||||||
|
|
||||||
[ForeignKey(nameof(FolloweeId))]
|
[ForeignKey(nameof(FolloweeId))]
|
||||||
[InverseProperty(nameof(User.IncomingFollowRelationships))]
|
[InverseProperty(nameof(User.IncomingFollowRelationships))]
|
||||||
|
|
|
@ -155,7 +155,8 @@ public class Note : IEntity
|
||||||
public string? ReplyUserId { get; set; }
|
public string? ReplyUserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mastodon requires a slightly differently computed replyUserId field. To save processing time, we do this ahead of time.
|
/// Mastodon requires a slightly differently computed replyUserId field. To save processing time, we do this ahead of
|
||||||
|
/// time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Column("mastoReplyUserId")]
|
[Column("mastoReplyUserId")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
|
|
|
@ -266,8 +266,7 @@ public class User : IEntity
|
||||||
[StringLength(128)]
|
[StringLength(128)]
|
||||||
public string? BannerBlurhash { get; set; }
|
public string? BannerBlurhash { get; set; }
|
||||||
|
|
||||||
[Column("splitDomainResolved")]
|
[Column("splitDomainResolved")] public bool SplitDomainResolved { get; set; }
|
||||||
public bool SplitDomainResolved { get; set; }
|
|
||||||
|
|
||||||
[InverseProperty(nameof(AbuseUserReport.Assignee))]
|
[InverseProperty(nameof(AbuseUserReport.Assignee))]
|
||||||
public virtual ICollection<AbuseUserReport> AbuseUserReportAssignees { get; set; } = new List<AbuseUserReport>();
|
public virtual ICollection<AbuseUserReport> AbuseUserReportAssignees { get; set; } = new List<AbuseUserReport>();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Text.Json;
|
|
||||||
using Iceshrimp.Backend.Controllers.Attributes;
|
using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -9,6 +8,8 @@ using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Extensions;
|
namespace Iceshrimp.Backend.Core.Extensions;
|
||||||
|
|
||||||
|
@ -42,9 +43,9 @@ public static class MvcBuilderExtensions
|
||||||
|
|
||||||
public static IMvcBuilder ConfigureNewtonsoftJson(this IMvcBuilder builder)
|
public static IMvcBuilder ConfigureNewtonsoftJson(this IMvcBuilder builder)
|
||||||
{
|
{
|
||||||
Newtonsoft.Json.JsonConvert.DefaultSettings = () => new Newtonsoft.Json.JsonSerializerSettings
|
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc
|
||||||
};
|
};
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
|
|
@ -4,7 +4,6 @@ using System.Xml.Linq;
|
||||||
using Iceshrimp.Backend.Controllers.Federation;
|
using Iceshrimp.Backend.Controllers.Federation;
|
||||||
using Iceshrimp.Backend.Controllers.Mastodon.Renderers;
|
using Iceshrimp.Backend.Controllers.Mastodon.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Federation.WebFinger;
|
using Iceshrimp.Backend.Core.Federation.WebFinger;
|
||||||
|
@ -13,6 +12,7 @@ using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
using Iceshrimp.Backend.Hubs.Authentication;
|
using Iceshrimp.Backend.Hubs.Authentication;
|
||||||
using Iceshrimp.Shared.Configuration;
|
using Iceshrimp.Shared.Configuration;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||||
|
@ -20,6 +20,7 @@ using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationM
|
||||||
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
|
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
|
||||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||||
|
using Microsoft.AspNetCore.Http.Json;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
@ -120,7 +121,7 @@ public static class ServiceExtensions
|
||||||
.ConfigureWithValidation<Config.LocalStorageSection>(configuration, "Storage:Local")
|
.ConfigureWithValidation<Config.LocalStorageSection>(configuration, "Storage:Local")
|
||||||
.ConfigureWithValidation<Config.ObjectStorageSection>(configuration, "Storage:ObjectStorage");
|
.ConfigureWithValidation<Config.ObjectStorageSection>(configuration, "Storage:ObjectStorage");
|
||||||
|
|
||||||
services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
|
services.Configure<JsonOptions>(options =>
|
||||||
{
|
{
|
||||||
options.SerializerOptions.PropertyNamingPolicy = JsonSerialization.Options.PropertyNamingPolicy;
|
options.SerializerOptions.PropertyNamingPolicy = JsonSerialization.Options.PropertyNamingPolicy;
|
||||||
foreach (var converter in JsonSerialization.Options.Converters)
|
foreach (var converter in JsonSerialization.Options.Converters)
|
||||||
|
@ -324,7 +325,8 @@ public static class HttpContextExtensions
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Async equivalent of EntityFrameworkCoreDataProtectionExtensions.PersistKeysToDbContext.
|
/// Async equivalent of EntityFrameworkCoreDataProtectionExtensions.PersistKeysToDbContext.
|
||||||
/// Required because Npgsql doesn't support the non-async APIs when using connection multiplexing, and the stock version EFCore API calls their blocking equivalents.
|
/// Required because Npgsql doesn't support the non-async APIs when using connection multiplexing, and the stock
|
||||||
|
/// version EFCore API calls their blocking equivalents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
file static class DataProtectionExtensions
|
file static class DataProtectionExtensions
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,22 +23,6 @@ public static class SwaggerGenOptionsExtensions
|
||||||
options.DocInclusionPredicate(DocInclusionPredicate);
|
options.DocInclusionPredicate(DocInclusionPredicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local",
|
|
||||||
Justification = "SwaggerGenOptions.SchemaFilter<T> instantiates this class at runtime")]
|
|
||||||
private class RequireNonNullablePropertiesSchemaFilter : ISchemaFilter
|
|
||||||
{
|
|
||||||
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
|
||||||
{
|
|
||||||
var additionalRequiredProps = model.Properties
|
|
||||||
.Where(x => !x.Value.Nullable && !model.Required.Contains(x.Key))
|
|
||||||
.Select(x => x.Key);
|
|
||||||
foreach (var propKey in additionalRequiredProps)
|
|
||||||
{
|
|
||||||
model.Required.Add(propKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool DocInclusionPredicate(string docName, ApiDescription apiDesc)
|
private static bool DocInclusionPredicate(string docName, ApiDescription apiDesc)
|
||||||
{
|
{
|
||||||
if (!apiDesc.TryGetMethodInfo(out var methodInfo)) return false;
|
if (!apiDesc.TryGetMethodInfo(out var methodInfo)) return false;
|
||||||
|
@ -61,6 +45,22 @@ public static class SwaggerGenOptionsExtensions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local",
|
||||||
|
Justification = "SwaggerGenOptions.SchemaFilter<T> instantiates this class at runtime")]
|
||||||
|
private class RequireNonNullablePropertiesSchemaFilter : ISchemaFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||||
|
{
|
||||||
|
var additionalRequiredProps = model.Properties
|
||||||
|
.Where(x => !x.Value.Nullable && !model.Required.Contains(x.Key))
|
||||||
|
.Select(x => x.Key);
|
||||||
|
foreach (var propKey in additionalRequiredProps)
|
||||||
|
{
|
||||||
|
model.Required.Add(propKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local",
|
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local",
|
||||||
Justification = "SwaggerGenOptions.OperationFilter<T> instantiates this class at runtime")]
|
Justification = "SwaggerGenOptions.OperationFilter<T> instantiates this class at runtime")]
|
||||||
private class AuthorizeCheckOperationFilter : IOperationFilter
|
private class AuthorizeCheckOperationFilter : IOperationFilter
|
||||||
|
|
|
@ -4,11 +4,11 @@ using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Migrations;
|
using Iceshrimp.Backend.Core.Database.Migrations;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Iceshrimp.WebPush;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.Extensions.Configuration.Ini;
|
using Microsoft.Extensions.Configuration.Ini;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Iceshrimp.WebPush;
|
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Extensions;
|
namespace Iceshrimp.Backend.Core.Extensions;
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
||||||
using static Iceshrimp.Parsing.MfmNodeTypes;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.FSharp.Collections;
|
using Microsoft.FSharp.Collections;
|
||||||
|
using static Iceshrimp.Parsing.MfmNodeTypes;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Federation.ActivityPub;
|
namespace Iceshrimp.Backend.Core.Federation.ActivityPub;
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,6 @@ public static class LdHelpers
|
||||||
|
|
||||||
private static readonly string Prefix = Path.Combine(AssemblyLocation, "contexts");
|
private static readonly string Prefix = Path.Combine(AssemblyLocation, "contexts");
|
||||||
|
|
||||||
private static JToken GetPreloadedDocument(string filename) =>
|
|
||||||
JToken.Parse(File.ReadAllText(Path.Combine(Prefix, filename)));
|
|
||||||
|
|
||||||
private static RemoteDocument GetPreloadedContext(string filename) => new()
|
|
||||||
{
|
|
||||||
Document = GetPreloadedDocument(filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, RemoteDocument> PreloadedContexts = new()
|
private static readonly Dictionary<string, RemoteDocument> PreloadedContexts = new()
|
||||||
{
|
{
|
||||||
{ "https://www.w3.org/ns/activitystreams", GetPreloadedContext("as.json") },
|
{ "https://www.w3.org/ns/activitystreams", GetPreloadedContext("as.json") },
|
||||||
|
@ -66,6 +58,14 @@ public static class LdHelpers
|
||||||
|
|
||||||
private static IEnumerable<string> ASForceArray => ["tag", "attachment", "to", "cc", "bcc", "bto"];
|
private static IEnumerable<string> ASForceArray => ["tag", "attachment", "to", "cc", "bcc", "bto"];
|
||||||
|
|
||||||
|
private static JToken GetPreloadedDocument(string filename) =>
|
||||||
|
JToken.Parse(File.ReadAllText(Path.Combine(Prefix, filename)));
|
||||||
|
|
||||||
|
private static RemoteDocument GetPreloadedContext(string filename) => new()
|
||||||
|
{
|
||||||
|
Document = GetPreloadedDocument(filename)
|
||||||
|
};
|
||||||
|
|
||||||
private static RemoteDocument CustomLoader(Uri uri, JsonLdLoaderOptions jsonLdLoaderOptions)
|
private static RemoteDocument CustomLoader(Uri uri, JsonLdLoaderOptions jsonLdLoaderOptions)
|
||||||
{
|
{
|
||||||
var key = uri.AbsolutePath == "/schemas/litepub-0.1.jsonld" ? "litepub-0.1" : uri.ToString();
|
var key = uri.AbsolutePath == "/schemas/litepub-0.1.jsonld" ? "litepub-0.1" : uri.ToString();
|
||||||
|
|
|
@ -9,11 +9,10 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASNote : ASObject
|
public class ASNote : ASObject
|
||||||
{
|
{
|
||||||
public ASNote(bool withType = true) => Type = withType ? Types.Note : null;
|
private string? _mkContent;
|
||||||
|
|
||||||
[JI] public bool VerifiedFetch = false;
|
[JI] public bool VerifiedFetch = false;
|
||||||
|
public ASNote(bool withType = true) => Type = withType ? Types.Note : null;
|
||||||
private string? _mkContent;
|
|
||||||
|
|
||||||
[J("https://misskey-hub.net/ns#_misskey_content")]
|
[J("https://misskey-hub.net/ns#_misskey_content")]
|
||||||
[JC(typeof(VC))]
|
[JC(typeof(VC))]
|
||||||
|
|
|
@ -184,11 +184,11 @@ public static class HttpSignature
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
public readonly string Algo = algo;
|
public readonly string Algo = algo;
|
||||||
public readonly IEnumerable<string> Headers = headers;
|
|
||||||
public readonly string KeyId = keyId;
|
|
||||||
public readonly byte[] Signature = signature;
|
|
||||||
public readonly string? Created = created;
|
public readonly string? Created = created;
|
||||||
public readonly string? Expires = expires;
|
public readonly string? Expires = expires;
|
||||||
|
public readonly IEnumerable<string> Headers = headers;
|
||||||
|
public readonly string KeyId = keyId;
|
||||||
public readonly string? Opaque = opaque;
|
public readonly string? Opaque = opaque;
|
||||||
|
public readonly byte[] Signature = signature;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,12 +4,6 @@ namespace Iceshrimp.Backend.Core.Helpers;
|
||||||
|
|
||||||
public static class CryptographyHelpers
|
public static class CryptographyHelpers
|
||||||
{
|
{
|
||||||
public static string GenerateRandomHexString(int length)
|
|
||||||
=> RandomNumberGenerator.GetHexString(length, true);
|
|
||||||
|
|
||||||
public static string GenerateRandomString(int length, Charset charset = Charset.AlphaNum)
|
|
||||||
=> RandomNumberGenerator.GetString(GetCharset(charset), length);
|
|
||||||
|
|
||||||
public enum Charset
|
public enum Charset
|
||||||
{
|
{
|
||||||
AlphaNum,
|
AlphaNum,
|
||||||
|
@ -18,6 +12,17 @@ public static class CryptographyHelpers
|
||||||
CrockfordBase32Lower
|
CrockfordBase32Lower
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string AlphaNumCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
private const string AlphaNumLowerCharset = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
private const string CrockfordBase32Charset = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
||||||
|
private const string CrockfordBase32LowerCharset = "0123456789abcdefghjkmnpqrstvwxyz";
|
||||||
|
|
||||||
|
public static string GenerateRandomHexString(int length)
|
||||||
|
=> RandomNumberGenerator.GetHexString(length, true);
|
||||||
|
|
||||||
|
public static string GenerateRandomString(int length, Charset charset = Charset.AlphaNum)
|
||||||
|
=> RandomNumberGenerator.GetString(GetCharset(charset), length);
|
||||||
|
|
||||||
private static string GetCharset(Charset charset) => charset switch
|
private static string GetCharset(Charset charset) => charset switch
|
||||||
{
|
{
|
||||||
Charset.AlphaNum => AlphaNumCharset,
|
Charset.AlphaNum => AlphaNumCharset,
|
||||||
|
@ -26,9 +31,4 @@ public static class CryptographyHelpers
|
||||||
Charset.CrockfordBase32Lower => CrockfordBase32LowerCharset,
|
Charset.CrockfordBase32Lower => CrockfordBase32LowerCharset,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(charset), charset, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(charset), charset, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
private const string AlphaNumCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
private const string AlphaNumLowerCharset = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
private const string CrockfordBase32Charset = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
||||||
private const string CrockfordBase32LowerCharset = "0123456789abcdefghjkmnpqrstvwxyz";
|
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@ namespace Iceshrimp.Backend.Core.Helpers;
|
||||||
|
|
||||||
public sealed class AsyncAutoResetEvent(bool signaled = false)
|
public sealed class AsyncAutoResetEvent(bool signaled = false)
|
||||||
{
|
{
|
||||||
private readonly List<TaskCompletionSource<bool>> _taskCompletionSources = [];
|
|
||||||
private readonly List<TaskCompletionSource<bool>> _noResetTaskCompletionSources = [];
|
private readonly List<TaskCompletionSource<bool>> _noResetTaskCompletionSources = [];
|
||||||
|
private readonly List<TaskCompletionSource<bool>> _taskCompletionSources = [];
|
||||||
public bool Signaled => signaled;
|
public bool Signaled => signaled;
|
||||||
|
|
||||||
public Task<bool> WaitAsync(CancellationToken cancellationToken = default)
|
public Task<bool> WaitAsync(CancellationToken cancellationToken = default)
|
||||||
|
|
|
@ -77,7 +77,7 @@ public static class FilterHelper
|
||||||
return keyword;
|
return keyword;
|
||||||
}
|
}
|
||||||
else if ((note.Text != null && note.Text.Contains(keyword, StringComparison.InvariantCultureIgnoreCase)) ||
|
else if ((note.Text != null && note.Text.Contains(keyword, StringComparison.InvariantCultureIgnoreCase)) ||
|
||||||
note.Cw != null && note.Cw.Contains(keyword, StringComparison.InvariantCultureIgnoreCase))
|
(note.Cw != null && note.Cw.Contains(keyword, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
{
|
{
|
||||||
return keyword;
|
return keyword;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
||||||
using static Iceshrimp.Parsing.MfmNodeTypes;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.FSharp.Collections;
|
using Microsoft.FSharp.Collections;
|
||||||
|
using static Iceshrimp.Parsing.MfmNodeTypes;
|
||||||
using MfmHtmlParser = Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing.HtmlParser;
|
using MfmHtmlParser = Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing.HtmlParser;
|
||||||
using HtmlParser = AngleSharp.Html.Parser.HtmlParser;
|
using HtmlParser = AngleSharp.Html.Parser.HtmlParser;
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,6 @@ namespace Iceshrimp.Backend.Core.Helpers;
|
||||||
|
|
||||||
public static class NoteThreadHelpers
|
public static class NoteThreadHelpers
|
||||||
{
|
{
|
||||||
public class TreeNode<T>(T self)
|
|
||||||
{
|
|
||||||
public readonly T Self = self;
|
|
||||||
public List<TreeNode<T>> Descendants = [];
|
|
||||||
public TreeNode<T>? Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<NoteResponse> OrderAncestors(this List<NoteResponse> notes)
|
public static List<NoteResponse> OrderAncestors(this List<NoteResponse> notes)
|
||||||
{
|
{
|
||||||
var final = new List<NoteResponse>();
|
var final = new List<NoteResponse>();
|
||||||
|
@ -139,4 +132,11 @@ public static class NoteThreadHelpers
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TreeNode<T>(T self)
|
||||||
|
{
|
||||||
|
public readonly T Self = self;
|
||||||
|
public List<TreeNode<T>> Descendants = [];
|
||||||
|
public TreeNode<T>? Parent;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,23 @@ public class WriteLockingList<T>(IEnumerable<T>? sourceCollection = null) : ICol
|
||||||
lock (_list) _list.Add(item);
|
lock (_list) _list.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (_list) _list.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item) => _list.Contains(item);
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
lock (_list) return _list.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _list.Count;
|
||||||
|
public bool IsReadOnly => ((ICollection<T>)_list).IsReadOnly;
|
||||||
|
|
||||||
public bool AddIfMissing(T item)
|
public bool AddIfMissing(T item)
|
||||||
{
|
{
|
||||||
lock (_list)
|
lock (_list)
|
||||||
|
@ -32,25 +49,8 @@ public class WriteLockingList<T>(IEnumerable<T>? sourceCollection = null) : ICol
|
||||||
lock (_list) _list.AddRange(item);
|
lock (_list) _list.AddRange(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
lock (_list) _list.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(T item) => _list.Contains(item);
|
|
||||||
|
|
||||||
public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
|
|
||||||
|
|
||||||
public bool Remove(T item)
|
|
||||||
{
|
|
||||||
lock (_list) return _list.Remove(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int RemoveAll(Predicate<T> predicate)
|
public int RemoveAll(Predicate<T> predicate)
|
||||||
{
|
{
|
||||||
lock (_list) return _list.RemoveAll(predicate);
|
lock (_list) return _list.RemoveAll(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count => _list.Count;
|
|
||||||
public bool IsReadOnly => ((ICollection<T>)_list).IsReadOnly;
|
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
|
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Middleware;
|
namespace Iceshrimp.Backend.Core.Middleware;
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class BackgroundTaskQueue(int parallelism)
|
||||||
await db.DriveFiles.AnyAsync(p => p.Id != file.Id &&
|
await db.DriveFiles.AnyAsync(p => p.Id != file.Id &&
|
||||||
p.AccessKey == file.AccessKey &&
|
p.AccessKey == file.AccessKey &&
|
||||||
!p.IsLink,
|
!p.IsLink,
|
||||||
cancellationToken: token);
|
token);
|
||||||
|
|
||||||
if (!deduplicated)
|
if (!deduplicated)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +128,7 @@ public class BackgroundTaskQueue(int parallelism)
|
||||||
if (file.AccessKey == null) return;
|
if (file.AccessKey == null) return;
|
||||||
var deduplicated =
|
var deduplicated =
|
||||||
await db.DriveFiles.AnyAsync(p => p.Id != file.Id && p.AccessKey == file.AccessKey && !p.IsLink,
|
await db.DriveFiles.AnyAsync(p => p.Id != file.Id && p.AccessKey == file.AccessKey && !p.IsLink,
|
||||||
cancellationToken: token);
|
token);
|
||||||
|
|
||||||
if (deduplicated)
|
if (deduplicated)
|
||||||
return;
|
return;
|
||||||
|
@ -267,7 +267,7 @@ public class BackgroundTaskQueue(int parallelism)
|
||||||
var dbInstance = await bgInstanceSvc.GetUpdatedInstanceMetadataAsync(user);
|
var dbInstance = await bgInstanceSvc.GetUpdatedInstanceMetadataAsync(user);
|
||||||
await bgDb.Instances.Where(p => p.Id == dbInstance.Id)
|
await bgDb.Instances.Where(p => p.Id == dbInstance.Id)
|
||||||
.ExecuteUpdateAsync(p => p.SetProperty(i => i.UsersCount, i => i.UsersCount - 1),
|
.ExecuteUpdateAsync(p => p.SetProperty(i => i.UsersCount, i => i.UsersCount - 1),
|
||||||
cancellationToken: token);
|
token);
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.LogDebug("User {id} deleted successfully", jobData.UserId);
|
logger.LogDebug("User {id} deleted successfully", jobData.UserId);
|
||||||
|
|
|
@ -208,15 +208,15 @@ public class CustomHttpClient : HttpClient
|
||||||
|
|
||||||
private class RedirectHandler : DelegatingHandler
|
private class RedirectHandler : DelegatingHandler
|
||||||
{
|
{
|
||||||
private int MaxAutomaticRedirections { get; set; }
|
|
||||||
private bool InitialAutoRedirect { get; set; }
|
|
||||||
|
|
||||||
public RedirectHandler(HttpMessageHandler innerHandler) : base(innerHandler)
|
public RedirectHandler(HttpMessageHandler innerHandler) : base(innerHandler)
|
||||||
{
|
{
|
||||||
var mostInnerHandler = innerHandler.GetMostInnerHandler();
|
var mostInnerHandler = innerHandler.GetMostInnerHandler();
|
||||||
SetupCustomAutoRedirect(mostInnerHandler);
|
SetupCustomAutoRedirect(mostInnerHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int MaxAutomaticRedirections { get; set; }
|
||||||
|
private bool InitialAutoRedirect { get; set; }
|
||||||
|
|
||||||
private void SetupCustomAutoRedirect(HttpMessageHandler? mostInnerHandler)
|
private void SetupCustomAutoRedirect(HttpMessageHandler? mostInnerHandler)
|
||||||
{
|
{
|
||||||
//Store the initial auto-redirect & max-auto-redirect values.
|
//Store the initial auto-redirect & max-auto-redirect values.
|
||||||
|
|
|
@ -74,10 +74,10 @@ public class ImageProcessor
|
||||||
public class Result
|
public class Result
|
||||||
{
|
{
|
||||||
public string? Blurhash;
|
public string? Blurhash;
|
||||||
public Func<Stream, Task>? RenderThumbnail;
|
|
||||||
public Func<Stream, Task>? RenderWebpublic;
|
|
||||||
|
|
||||||
public required DriveFile.FileProperties Properties;
|
public required DriveFile.FileProperties Properties;
|
||||||
|
public Func<Stream, Task>? RenderThumbnail;
|
||||||
|
public Func<Stream, Task>? RenderWebpublic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result?> ProcessImage(Stream data, DriveFileCreationRequest request, bool genThumb, bool genWebp)
|
public async Task<Result?> ProcessImage(Stream data, DriveFileCreationRequest request, bool genThumb, bool genWebp)
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class MetaService([FromKeyedServices("cache")] DatabaseContext db)
|
||||||
{
|
{
|
||||||
await db.MetaStore.Upsert(new MetaStoreEntry { Key = key, Value = value })
|
await db.MetaStore.Upsert(new MetaStoreEntry { Key = key, Value = value })
|
||||||
.On(p => p.Key)
|
.On(p => p.Key)
|
||||||
.WhenMatched((_, orig) => new MetaStoreEntry { Value = orig.Value, })
|
.WhenMatched((_, orig) => new MetaStoreEntry { Value = orig.Value })
|
||||||
.RunAsync();
|
.RunAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,11 @@ using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
||||||
using static Iceshrimp.Parsing.MfmNodeTypes;
|
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Queues;
|
using Iceshrimp.Backend.Core.Queues;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using static Iceshrimp.Parsing.MfmNodeTypes;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Services;
|
namespace Iceshrimp.Backend.Core.Services;
|
||||||
|
|
||||||
|
@ -49,8 +49,6 @@ public class NoteService(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
private const int DefaultRecursionLimit = 100;
|
private const int DefaultRecursionLimit = 100;
|
||||||
private readonly List<string> _resolverHistory = [];
|
|
||||||
private int _recursionLimit = DefaultRecursionLimit;
|
|
||||||
|
|
||||||
private static readonly AsyncKeyedLocker<string> KeyedLocker = new(o =>
|
private static readonly AsyncKeyedLocker<string> KeyedLocker = new(o =>
|
||||||
{
|
{
|
||||||
|
@ -58,6 +56,9 @@ public class NoteService(
|
||||||
o.PoolInitialFill = 5;
|
o.PoolInitialFill = 5;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private readonly List<string> _resolverHistory = [];
|
||||||
|
private int _recursionLimit = DefaultRecursionLimit;
|
||||||
|
|
||||||
public async Task<Note> CreateNoteAsync(
|
public async Task<Note> CreateNoteAsync(
|
||||||
User user, Note.NoteVisibility visibility, string? text = null, string? cw = null, Note? reply = null,
|
User user, Note.NoteVisibility visibility, string? text = null, string? cw = null, Note? reply = null,
|
||||||
Note? renote = null, IReadOnlyCollection<DriveFile>? attachments = null, Poll? poll = null,
|
Note? renote = null, IReadOnlyCollection<DriveFile>? attachments = null, Poll? poll = null,
|
||||||
|
@ -632,7 +633,8 @@ public class NoteService(
|
||||||
note.Visibility,
|
note.Visibility,
|
||||||
note.User.GetPublicUri(config.Value) +
|
note.User.GetPublicUri(config.Value) +
|
||||||
"/followers"))
|
"/followers"))
|
||||||
: ActivityPub.ActivityRenderer.RenderDelete(actor, new ASTombstone { Id = note.GetPublicUri(config.Value) });
|
: ActivityPub.ActivityRenderer.RenderDelete(actor,
|
||||||
|
new ASTombstone { Id = note.GetPublicUri(config.Value) });
|
||||||
|
|
||||||
if (note.Visibility == Note.NoteVisibility.Specified)
|
if (note.Visibility == Note.NoteVisibility.Specified)
|
||||||
await deliverSvc.DeliverToAsync(activity, note.User, recipients.ToArray());
|
await deliverSvc.DeliverToAsync(activity, note.User, recipients.ToArray());
|
||||||
|
|
|
@ -14,14 +14,14 @@ public class ObjectStorageService(IOptions<Config.StorageSection> config, HttpCl
|
||||||
{
|
{
|
||||||
private readonly string? _accessUrl = config.Value.ObjectStorage?.AccessUrl;
|
private readonly string? _accessUrl = config.Value.ObjectStorage?.AccessUrl;
|
||||||
|
|
||||||
private readonly S3Bucket? _bucket = GetBucketSafely(config);
|
|
||||||
|
|
||||||
private readonly string? _prefix = config.Value.ObjectStorage?.Prefix?.Trim('/');
|
|
||||||
|
|
||||||
private readonly IReadOnlyDictionary<string, string>? _acl = config.Value.ObjectStorage?.SetAcl != null
|
private readonly IReadOnlyDictionary<string, string>? _acl = config.Value.ObjectStorage?.SetAcl != null
|
||||||
? new Dictionary<string, string> { { "x-amz-acl", config.Value.ObjectStorage.SetAcl } }.AsReadOnly()
|
? new Dictionary<string, string> { { "x-amz-acl", config.Value.ObjectStorage.SetAcl } }.AsReadOnly()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
private readonly S3Bucket? _bucket = GetBucketSafely(config);
|
||||||
|
|
||||||
|
private readonly string? _prefix = config.Value.ObjectStorage?.Prefix?.Trim('/');
|
||||||
|
|
||||||
private static S3Bucket? GetBucketSafely(IOptions<Config.StorageSection> config)
|
private static S3Bucket? GetBucketSafely(IOptions<Config.StorageSection> config)
|
||||||
{
|
{
|
||||||
if (config.Value.Provider != Enums.FileStorage.Local) return GetBucket(config);
|
if (config.Value.Provider != Enums.FileStorage.Local) return GetBucket(config);
|
||||||
|
|
|
@ -8,9 +8,9 @@ using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
|
using Iceshrimp.WebPush;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Iceshrimp.WebPush;
|
|
||||||
using PushSubscription = Iceshrimp.Backend.Core.Database.Tables.PushSubscription;
|
using PushSubscription = Iceshrimp.Backend.Core.Database.Tables.PushSubscription;
|
||||||
using WebPushSubscription = Iceshrimp.WebPush.PushSubscription;
|
using WebPushSubscription = Iceshrimp.WebPush.PushSubscription;
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,13 @@ public class QueueService(
|
||||||
) : BackgroundService
|
) : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly List<IPostgresJobQueue> _queues = [];
|
private readonly List<IPostgresJobQueue> _queues = [];
|
||||||
|
public readonly BackgroundTaskQueue BackgroundTaskQueue = new(queueConcurrency.Value.BackgroundTask);
|
||||||
public IEnumerable<string> QueueNames => _queues.Select(p => p.Name);
|
public readonly DeliverQueue DeliverQueue = new(queueConcurrency.Value.Deliver);
|
||||||
|
|
||||||
public readonly InboxQueue InboxQueue = new(queueConcurrency.Value.Inbox);
|
public readonly InboxQueue InboxQueue = new(queueConcurrency.Value.Inbox);
|
||||||
public readonly DeliverQueue DeliverQueue = new(queueConcurrency.Value.Deliver);
|
|
||||||
public readonly PreDeliverQueue PreDeliverQueue = new(queueConcurrency.Value.PreDeliver);
|
public readonly PreDeliverQueue PreDeliverQueue = new(queueConcurrency.Value.PreDeliver);
|
||||||
public readonly BackgroundTaskQueue BackgroundTaskQueue = new(queueConcurrency.Value.BackgroundTask);
|
|
||||||
|
public IEnumerable<string> QueueNames => _queues.Select(p => p.Name);
|
||||||
|
|
||||||
private static async Task<NpgsqlConnection> GetNpgsqlConnection(IServiceScope scope)
|
private static async Task<NpgsqlConnection> GetNpgsqlConnection(IServiceScope scope)
|
||||||
{
|
{
|
||||||
|
@ -263,13 +263,13 @@ public interface IPostgresJobQueue
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
|
public TimeSpan Timeout { get; }
|
||||||
|
|
||||||
public Task ExecuteAsync(IServiceScopeFactory scopeFactory, CancellationToken token, CancellationToken queueToken);
|
public Task ExecuteAsync(IServiceScopeFactory scopeFactory, CancellationToken token, CancellationToken queueToken);
|
||||||
public Task RecoverOrPrepareForExitAsync();
|
public Task RecoverOrPrepareForExitAsync();
|
||||||
|
|
||||||
public void RaiseJobQueuedEvent();
|
public void RaiseJobQueuedEvent();
|
||||||
public void RaiseJobDelayedEvent();
|
public void RaiseJobDelayedEvent();
|
||||||
|
|
||||||
public TimeSpan Timeout { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PostgresJobQueue<T>(
|
public class PostgresJobQueue<T>(
|
||||||
|
@ -279,14 +279,13 @@ public class PostgresJobQueue<T>(
|
||||||
TimeSpan timeout
|
TimeSpan timeout
|
||||||
) : IPostgresJobQueue where T : class
|
) : IPostgresJobQueue where T : class
|
||||||
{
|
{
|
||||||
public string Name => name;
|
|
||||||
public TimeSpan Timeout => timeout;
|
|
||||||
|
|
||||||
private readonly AsyncAutoResetEvent _delayedChannel = new();
|
private readonly AsyncAutoResetEvent _delayedChannel = new();
|
||||||
private readonly AsyncAutoResetEvent _queuedChannel = new();
|
private readonly AsyncAutoResetEvent _queuedChannel = new();
|
||||||
private IServiceScopeFactory _scopeFactory = null!;
|
|
||||||
private ILogger<QueueService> _logger = null!;
|
private ILogger<QueueService> _logger = null!;
|
||||||
|
private IServiceScopeFactory _scopeFactory = null!;
|
||||||
private string? _workerId;
|
private string? _workerId;
|
||||||
|
public string Name => name;
|
||||||
|
public TimeSpan Timeout => timeout;
|
||||||
|
|
||||||
public void RaiseJobQueuedEvent() => QueuedChannelEvent?.Invoke(null, EventArgs.Empty);
|
public void RaiseJobQueuedEvent() => QueuedChannelEvent?.Invoke(null, EventArgs.Empty);
|
||||||
public void RaiseJobDelayedEvent() => DelayedChannelEvent?.Invoke(null, EventArgs.Empty);
|
public void RaiseJobDelayedEvent() => DelayedChannelEvent?.Invoke(null, EventArgs.Empty);
|
||||||
|
|
|
@ -12,20 +12,17 @@ namespace Iceshrimp.Backend.Core.Services;
|
||||||
|
|
||||||
public sealed class StreamingService
|
public sealed class StreamingService
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, StreamingConnectionAggregate> _connections = [];
|
|
||||||
private readonly IHubContext<StreamingHub, IStreamingHubClient> _hub;
|
|
||||||
private readonly EventService _eventSvc;
|
|
||||||
private readonly IServiceScopeFactory _scopeFactory;
|
|
||||||
private readonly ILogger<StreamingService> _logger;
|
|
||||||
|
|
||||||
private static readonly AsyncKeyedLocker<string> Locker = new(o =>
|
private static readonly AsyncKeyedLocker<string> Locker = new(o =>
|
||||||
{
|
{
|
||||||
o.PoolSize = 100;
|
o.PoolSize = 100;
|
||||||
o.PoolInitialFill = 5;
|
o.PoolInitialFill = 5;
|
||||||
});
|
});
|
||||||
|
|
||||||
public event EventHandler<(Note note, Func<Task<NoteResponse>> rendered)>? NotePublished;
|
private readonly ConcurrentDictionary<string, StreamingConnectionAggregate> _connections = [];
|
||||||
public event EventHandler<(Note note, Func<Task<NoteResponse>> rendered)>? NoteUpdated;
|
private readonly EventService _eventSvc;
|
||||||
|
private readonly IHubContext<StreamingHub, IStreamingHubClient> _hub;
|
||||||
|
private readonly ILogger<StreamingService> _logger;
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
public StreamingService(
|
public StreamingService(
|
||||||
IHubContext<StreamingHub, IStreamingHubClient> hub,
|
IHubContext<StreamingHub, IStreamingHubClient> hub,
|
||||||
|
@ -43,6 +40,9 @@ public sealed class StreamingService
|
||||||
eventSvc.NoteUpdated += OnNoteUpdated;
|
eventSvc.NoteUpdated += OnNoteUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event EventHandler<(Note note, Func<Task<NoteResponse>> rendered)>? NotePublished;
|
||||||
|
public event EventHandler<(Note note, Func<Task<NoteResponse>> rendered)>? NoteUpdated;
|
||||||
|
|
||||||
public void Connect(string userId, User user, string connectionId)
|
public void Connect(string userId, User user, string connectionId)
|
||||||
{
|
{
|
||||||
_connections
|
_connections
|
||||||
|
|
|
@ -5,8 +5,8 @@ using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
||||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
using Iceshrimp.Backend.Core.Helpers.LibMfm.Parsing;
|
||||||
using static Iceshrimp.Parsing.MfmNodeTypes;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using static Iceshrimp.Parsing.MfmNodeTypes;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Services;
|
namespace Iceshrimp.Backend.Core.Services;
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ public class UserService(
|
||||||
//TODO: FollowersCount
|
//TODO: FollowersCount
|
||||||
//TODO: FollowingCount
|
//TODO: FollowingCount
|
||||||
Emojis = emoji.Select(p => p.Id).ToList(),
|
Emojis = emoji.Select(p => p.Id).ToList(),
|
||||||
Tags = tags,
|
Tags = tags
|
||||||
};
|
};
|
||||||
|
|
||||||
var profile = new UserProfile
|
var profile = new UserProfile
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Iceshrimp.Shared.HubSchemas;
|
using Iceshrimp.Shared.HubSchemas;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Hubs;
|
namespace Iceshrimp.Backend.Hubs;
|
||||||
|
|
||||||
public class ExampleHub : Hub<IExampleHubClient>, IExampleHubServer
|
public class ExampleHub : Hub<IExampleHubClient>, IExampleHubServer
|
||||||
|
|
|
@ -18,114 +18,49 @@ namespace Iceshrimp.Backend.Hubs.Helpers;
|
||||||
[MustDisposeResource]
|
[MustDisposeResource]
|
||||||
public sealed class StreamingConnectionAggregate : IDisposable
|
public sealed class StreamingConnectionAggregate : IDisposable
|
||||||
{
|
{
|
||||||
private readonly User _user;
|
private readonly WriteLockingList<string> _blockedBy = [];
|
||||||
private readonly string _userId;
|
private readonly WriteLockingList<string> _blocking = [];
|
||||||
private readonly WriteLockingList<string> _connectionIds = [];
|
private readonly WriteLockingList<string> _connectionIds = [];
|
||||||
|
|
||||||
private readonly IHubContext<StreamingHub, IStreamingHubClient> _hub;
|
|
||||||
|
|
||||||
private readonly EventService _eventService;
|
private readonly EventService _eventService;
|
||||||
|
|
||||||
|
private readonly WriteLockingList<string> _following = [];
|
||||||
|
|
||||||
|
private readonly IHubContext<StreamingHub, IStreamingHubClient> _hub;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly WriteLockingList<string> _muting = [];
|
||||||
private readonly IServiceScope _scope;
|
private readonly IServiceScope _scope;
|
||||||
private readonly IServiceScopeFactory _scopeFactory;
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
private readonly StreamingService _streamingService;
|
private readonly StreamingService _streamingService;
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private readonly WriteLockingList<string> _following = [];
|
|
||||||
private readonly WriteLockingList<string> _muting = [];
|
|
||||||
private readonly WriteLockingList<string> _blocking = [];
|
|
||||||
private readonly WriteLockingList<string> _blockedBy = [];
|
|
||||||
private List<string> _hiddenFromHome = [];
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, WriteLockingList<StreamingTimeline>> _subscriptions = [];
|
private readonly ConcurrentDictionary<string, WriteLockingList<StreamingTimeline>> _subscriptions = [];
|
||||||
|
private readonly User _user;
|
||||||
|
private readonly string _userId;
|
||||||
|
private List<string> _hiddenFromHome = [];
|
||||||
|
|
||||||
public bool HasSubscribers => _connectionIds.Count != 0;
|
public bool HasSubscribers => _connectionIds.Count != 0;
|
||||||
|
|
||||||
|
#region Destruction
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DisconnectAll();
|
||||||
|
_streamingService.NotePublished -= OnNotePublished;
|
||||||
|
_streamingService.NoteUpdated -= OnNoteUpdated;
|
||||||
|
_eventService.Notification -= OnNotification;
|
||||||
|
_eventService.UserBlocked -= OnUserBlock;
|
||||||
|
_eventService.UserUnblocked -= OnUserUnblock;
|
||||||
|
_eventService.UserMuted -= OnUserMute;
|
||||||
|
_eventService.UserUnmuted -= OnUserUnmute;
|
||||||
|
_eventService.UserFollowed -= OnUserFollow;
|
||||||
|
_eventService.UserUnfollowed -= OnUserUnfollow;
|
||||||
|
_scope.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private AsyncServiceScope GetTempScope() => _scopeFactory.CreateAsyncScope();
|
private AsyncServiceScope GetTempScope() => _scopeFactory.CreateAsyncScope();
|
||||||
|
|
||||||
#region Initialization
|
|
||||||
|
|
||||||
public StreamingConnectionAggregate(
|
|
||||||
string userId,
|
|
||||||
User user,
|
|
||||||
IHubContext<StreamingHub, IStreamingHubClient> hub,
|
|
||||||
EventService eventSvc,
|
|
||||||
IServiceScopeFactory scopeFactory, StreamingService streamingService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (userId != user.Id)
|
|
||||||
throw new Exception("userId doesn't match user.Id");
|
|
||||||
|
|
||||||
_userId = userId;
|
|
||||||
_user = user;
|
|
||||||
_hub = hub;
|
|
||||||
_eventService = eventSvc;
|
|
||||||
_scope = scopeFactory.CreateScope();
|
|
||||||
_scopeFactory = scopeFactory;
|
|
||||||
_streamingService = streamingService;
|
|
||||||
_logger = _scope.ServiceProvider.GetRequiredService<ILogger<StreamingConnectionAggregate>>();
|
|
||||||
|
|
||||||
_ = InitializeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
_eventService.UserBlocked += OnUserBlock;
|
|
||||||
_eventService.UserUnblocked += OnUserUnblock;
|
|
||||||
_eventService.UserMuted += OnUserMute;
|
|
||||||
_eventService.UserUnmuted += OnUserUnmute;
|
|
||||||
_eventService.UserFollowed += OnUserFollow;
|
|
||||||
_eventService.UserUnfollowed += OnUserUnfollow;
|
|
||||||
|
|
||||||
await InitializeRelationships();
|
|
||||||
|
|
||||||
_eventService.Notification += OnNotification;
|
|
||||||
_streamingService.NotePublished += OnNotePublished;
|
|
||||||
_streamingService.NoteUpdated += OnNoteUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitializeRelationships()
|
|
||||||
{
|
|
||||||
await using var scope = GetTempScope();
|
|
||||||
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
|
||||||
_following.AddRange(await db.Followings.Where(p => p.Follower == _user)
|
|
||||||
.Select(p => p.FolloweeId)
|
|
||||||
.ToListAsync());
|
|
||||||
_blocking.AddRange(await db.Blockings.Where(p => p.Blocker == _user)
|
|
||||||
.Select(p => p.BlockeeId)
|
|
||||||
.ToListAsync());
|
|
||||||
_blockedBy.AddRange(await db.Blockings.Where(p => p.Blockee == _user)
|
|
||||||
.Select(p => p.BlockerId)
|
|
||||||
.ToListAsync());
|
|
||||||
_muting.AddRange(await db.Mutings.Where(p => p.Muter == _user)
|
|
||||||
.Select(p => p.MuteeId)
|
|
||||||
.ToListAsync());
|
|
||||||
|
|
||||||
_hiddenFromHome = await db.UserListMembers
|
|
||||||
.Where(p => p.UserList.User == _user && p.UserList.HideFromHomeTl)
|
|
||||||
.Select(p => p.UserId)
|
|
||||||
.Distinct()
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Channel subscription handlers
|
|
||||||
|
|
||||||
public void Subscribe(string connectionId, StreamingTimeline timeline)
|
|
||||||
{
|
|
||||||
if (!_connectionIds.Contains(connectionId)) return;
|
|
||||||
_subscriptions.GetOrAdd(connectionId, []).Add(timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unsubscribe(string connectionId, StreamingTimeline timeline)
|
|
||||||
{
|
|
||||||
if (!_connectionIds.Contains(connectionId)) return;
|
|
||||||
_subscriptions.TryGetValue(connectionId, out var collection);
|
|
||||||
collection?.Remove(timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private async void OnNotification(object? _, Notification notification)
|
private async void OnNotification(object? _, Notification notification)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -228,13 +163,6 @@ public sealed class StreamingConnectionAggregate : IDisposable
|
||||||
return wrapped;
|
return wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoteWithVisibilities(Note note)
|
|
||||||
{
|
|
||||||
public readonly Note Note = note;
|
|
||||||
public Note? Reply = note.Reply;
|
|
||||||
public Note? Renote = note.Renote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private (List<string> connectionIds, List<StreamingTimeline> timelines) FindRecipients(Note note)
|
private (List<string> connectionIds, List<StreamingTimeline> timelines) FindRecipients(Note note)
|
||||||
{
|
{
|
||||||
List<StreamingTimeline> timelines = [];
|
List<StreamingTimeline> timelines = [];
|
||||||
|
@ -260,6 +188,97 @@ public sealed class StreamingConnectionAggregate : IDisposable
|
||||||
return (connectionIds, timelines);
|
return (connectionIds, timelines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NoteWithVisibilities(Note note)
|
||||||
|
{
|
||||||
|
public readonly Note Note = note;
|
||||||
|
public Note? Renote = note.Renote;
|
||||||
|
public Note? Reply = note.Reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
|
||||||
|
public StreamingConnectionAggregate(
|
||||||
|
string userId,
|
||||||
|
User user,
|
||||||
|
IHubContext<StreamingHub, IStreamingHubClient> hub,
|
||||||
|
EventService eventSvc,
|
||||||
|
IServiceScopeFactory scopeFactory, StreamingService streamingService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (userId != user.Id)
|
||||||
|
throw new Exception("userId doesn't match user.Id");
|
||||||
|
|
||||||
|
_userId = userId;
|
||||||
|
_user = user;
|
||||||
|
_hub = hub;
|
||||||
|
_eventService = eventSvc;
|
||||||
|
_scope = scopeFactory.CreateScope();
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
_streamingService = streamingService;
|
||||||
|
_logger = _scope.ServiceProvider.GetRequiredService<ILogger<StreamingConnectionAggregate>>();
|
||||||
|
|
||||||
|
_ = InitializeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
_eventService.UserBlocked += OnUserBlock;
|
||||||
|
_eventService.UserUnblocked += OnUserUnblock;
|
||||||
|
_eventService.UserMuted += OnUserMute;
|
||||||
|
_eventService.UserUnmuted += OnUserUnmute;
|
||||||
|
_eventService.UserFollowed += OnUserFollow;
|
||||||
|
_eventService.UserUnfollowed += OnUserUnfollow;
|
||||||
|
|
||||||
|
await InitializeRelationships();
|
||||||
|
|
||||||
|
_eventService.Notification += OnNotification;
|
||||||
|
_streamingService.NotePublished += OnNotePublished;
|
||||||
|
_streamingService.NoteUpdated += OnNoteUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeRelationships()
|
||||||
|
{
|
||||||
|
await using var scope = GetTempScope();
|
||||||
|
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||||
|
_following.AddRange(await db.Followings.Where(p => p.Follower == _user)
|
||||||
|
.Select(p => p.FolloweeId)
|
||||||
|
.ToListAsync());
|
||||||
|
_blocking.AddRange(await db.Blockings.Where(p => p.Blocker == _user)
|
||||||
|
.Select(p => p.BlockeeId)
|
||||||
|
.ToListAsync());
|
||||||
|
_blockedBy.AddRange(await db.Blockings.Where(p => p.Blockee == _user)
|
||||||
|
.Select(p => p.BlockerId)
|
||||||
|
.ToListAsync());
|
||||||
|
_muting.AddRange(await db.Mutings.Where(p => p.Muter == _user)
|
||||||
|
.Select(p => p.MuteeId)
|
||||||
|
.ToListAsync());
|
||||||
|
|
||||||
|
_hiddenFromHome = await db.UserListMembers
|
||||||
|
.Where(p => p.UserList.User == _user && p.UserList.HideFromHomeTl)
|
||||||
|
.Select(p => p.UserId)
|
||||||
|
.Distinct()
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Channel subscription handlers
|
||||||
|
|
||||||
|
public void Subscribe(string connectionId, StreamingTimeline timeline)
|
||||||
|
{
|
||||||
|
if (!_connectionIds.Contains(connectionId)) return;
|
||||||
|
_subscriptions.GetOrAdd(connectionId, []).Add(timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unsubscribe(string connectionId, StreamingTimeline timeline)
|
||||||
|
{
|
||||||
|
if (!_connectionIds.Contains(connectionId)) return;
|
||||||
|
_subscriptions.TryGetValue(connectionId, out var collection);
|
||||||
|
collection?.Remove(timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Relationship change event handlers
|
#region Relationship change event handlers
|
||||||
|
|
||||||
private void OnUserBlock(object? _, UserInteraction interaction)
|
private void OnUserBlock(object? _, UserInteraction interaction)
|
||||||
|
@ -373,23 +392,4 @@ public sealed class StreamingConnectionAggregate : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Destruction
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
DisconnectAll();
|
|
||||||
_streamingService.NotePublished -= OnNotePublished;
|
|
||||||
_streamingService.NoteUpdated -= OnNoteUpdated;
|
|
||||||
_eventService.Notification -= OnNotification;
|
|
||||||
_eventService.UserBlocked -= OnUserBlock;
|
|
||||||
_eventService.UserUnblocked -= OnUserUnblock;
|
|
||||||
_eventService.UserMuted -= OnUserMute;
|
|
||||||
_eventService.UserUnmuted -= OnUserUnmute;
|
|
||||||
_eventService.UserFollowed -= OnUserFollow;
|
|
||||||
_eventService.UserUnfollowed -= OnUserUnfollow;
|
|
||||||
_scope.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
@page "/"
|
@page "/"
|
||||||
@model Iceshrimp.Backend.Pages.IndexModel
|
@model IndexModel
|
||||||
@{
|
@{
|
||||||
ViewData["title"] = $"Home - {Model.InstanceName}";
|
ViewData["title"] = $"Home - {Model.InstanceName}";
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ namespace Iceshrimp.Backend.Pages;
|
||||||
|
|
||||||
public class IndexModel(MetaService meta) : PageModel
|
public class IndexModel(MetaService meta) : PageModel
|
||||||
{
|
{
|
||||||
public string InstanceName = null!;
|
|
||||||
public string InstanceDescription = null!;
|
|
||||||
public string? ContactEmail;
|
public string? ContactEmail;
|
||||||
|
public string InstanceDescription = null!;
|
||||||
|
public string InstanceName = null!;
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet()
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@page "/notes/{id}"
|
@page "/notes/{id}"
|
||||||
@using Iceshrimp.Backend.Core.Database.Tables
|
@using Iceshrimp.Backend.Core.Database.Tables
|
||||||
@using Iceshrimp.Backend.Core.Extensions
|
@using Iceshrimp.Backend.Core.Extensions
|
||||||
@model Iceshrimp.Backend.Pages.NoteModel
|
@model NoteModel
|
||||||
|
|
||||||
@if (Model.Note == null)
|
@if (Model.Note == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,13 +19,13 @@ public class NoteModel(
|
||||||
MfmConverter mfmConverter
|
MfmConverter mfmConverter
|
||||||
) : PageModel
|
) : PageModel
|
||||||
{
|
{
|
||||||
|
public Dictionary<string, List<DriveFile>> MediaAttachments = new();
|
||||||
public Note? Note;
|
public Note? Note;
|
||||||
public string? QuoteUrl;
|
public string? QuoteUrl;
|
||||||
public Dictionary<string, string> TextContent = new();
|
|
||||||
public Dictionary<string, List<DriveFile>> MediaAttachments = new();
|
|
||||||
|
|
||||||
public bool ShowMedia = security.Value.PublicPreview > Enums.PublicPreview.RestrictedNoMedia;
|
public bool ShowMedia = security.Value.PublicPreview > Enums.PublicPreview.RestrictedNoMedia;
|
||||||
public bool ShowRemoteReplies = security.Value.PublicPreview > Enums.PublicPreview.Restricted;
|
public bool ShowRemoteReplies = security.Value.PublicPreview > Enums.PublicPreview.Restricted;
|
||||||
|
public Dictionary<string, string> TextContent = new();
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "EntityFramework.NPlusOne.IncompleteDataQuery",
|
[SuppressMessage("ReSharper", "EntityFramework.NPlusOne.IncompleteDataQuery",
|
||||||
Justification = "IncludeCommonProperties")]
|
Justification = "IncludeCommonProperties")]
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
width: 3em;
|
width: 3em;
|
||||||
max-height: 3em;
|
max-height: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
grid-column: 2/-1;
|
grid-column: 2/-1;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.acct {
|
.acct {
|
||||||
grid-column: 2/-1;
|
grid-column: 2/-1;
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
@page "/queue/{queue?}/{pagination:int?}/{status?}"
|
@page "/queue/{queue?}/{pagination:int?}/{status?}"
|
||||||
|
@inject QueueService QueueSvc
|
||||||
@using Iceshrimp.Backend.Core.Database.Tables
|
@using Iceshrimp.Backend.Core.Database.Tables
|
||||||
@using Iceshrimp.Backend.Core.Extensions
|
@using Iceshrimp.Backend.Core.Extensions
|
||||||
@using Iceshrimp.Backend.Core.Services
|
@using Iceshrimp.Backend.Core.Services
|
||||||
@inject QueueService QueueSvc
|
|
||||||
@model QueueModel
|
@model QueueModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
|
|
|
@ -10,15 +10,15 @@ namespace Iceshrimp.Backend.Pages;
|
||||||
|
|
||||||
public class QueueModel(DatabaseContext db, QueueService queueSvc) : PageModel
|
public class QueueModel(DatabaseContext db, QueueService queueSvc) : PageModel
|
||||||
{
|
{
|
||||||
public List<Job> Jobs = [];
|
public int? DelayedCount;
|
||||||
public string? Queue;
|
|
||||||
public Job.JobStatus? Filter;
|
public Job.JobStatus? Filter;
|
||||||
public int? TotalCount;
|
public List<Job> Jobs = [];
|
||||||
|
public int? NextPage;
|
||||||
|
public int? PrevPage;
|
||||||
|
public string? Queue;
|
||||||
public int? QueuedCount;
|
public int? QueuedCount;
|
||||||
public int? RunningCount;
|
public int? RunningCount;
|
||||||
public int? DelayedCount;
|
public int? TotalCount;
|
||||||
public int? PrevPage;
|
|
||||||
public int? NextPage;
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet(
|
public async Task<IActionResult> OnGet(
|
||||||
[FromRoute] string? queue, [FromRoute(Name = "pagination")] int? page, [FromRoute] string? status
|
[FromRoute] string? queue, [FromRoute(Name = "pagination")] int? page, [FromRoute] string? status
|
||||||
|
|
|
@ -42,7 +42,9 @@
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>Actions</td>
|
<td>Actions</td>
|
||||||
<td><a class="fake-link" onclick="retry('@Model.Job.Id.ToStringLower()')">Retry</a></td>
|
<td>
|
||||||
|
<a class="fake-link" onclick="retry('@Model.Job.Id.ToStringLower()')">Retry</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -9,8 +9,17 @@ namespace Iceshrimp.Backend.Pages;
|
||||||
|
|
||||||
public class QueueJobModel(DatabaseContext db) : PageModel
|
public class QueueJobModel(DatabaseContext db) : PageModel
|
||||||
{
|
{
|
||||||
|
private static Dictionary<string, string> _lookup = new()
|
||||||
|
{
|
||||||
|
{ "inbox", "body" },
|
||||||
|
{ "deliver", "payload" },
|
||||||
|
{ "pre-deliver", "serializedActivity" }
|
||||||
|
};
|
||||||
|
|
||||||
public Job Job = null!;
|
public Job Job = null!;
|
||||||
|
|
||||||
|
public Dictionary<string, string> Lookup => _lookup;
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] Guid id)
|
public async Task<IActionResult> OnGet([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
if (!Request.Cookies.TryGetValue("admin_session", out var cookie))
|
if (!Request.Cookies.TryGetValue("admin_session", out var cookie))
|
||||||
|
@ -22,13 +31,4 @@ public class QueueJobModel(DatabaseContext db) : PageModel
|
||||||
throw GracefulException.NotFound($"Job {id} not found");
|
throw GracefulException.NotFound($"Job {id} not found");
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, string> _lookup = new()
|
|
||||||
{
|
|
||||||
{ "inbox", "body" },
|
|
||||||
{ "deliver", "payload" },
|
|
||||||
{ "pre-deliver", "serializedActivity" }
|
|
||||||
};
|
|
||||||
|
|
||||||
public Dictionary<string, string> Lookup => _lookup;
|
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue