[backend/api] Respect note filters (ISH-243)

This commit is contained in:
Laura Hausmann 2024-04-05 17:11:14 +02:00
parent 01e640e8e5
commit e4c4ec186c
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
6 changed files with 65 additions and 26 deletions

View file

@ -69,7 +69,8 @@ public class NoteController(
.PrecomputeNoteContextVisibilities(user) .PrecomputeNoteContextVisibilities(user)
.ToListAsync(); .ToListAsync();
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user)); return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user,
Filter.FilterContext.Threads));
} }
[HttpGet("{id}/descendants")] [HttpGet("{id}/descendants")]
@ -97,7 +98,8 @@ public class NoteController(
.PrecomputeNoteContextVisibilities(user) .PrecomputeNoteContextVisibilities(user)
.ToListAsync(); .ToListAsync();
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user)); return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user,
Filter.FilterContext.Threads));
} }
[HttpGet("{id}/reactions/{name}")] [HttpGet("{id}/reactions/{name}")]

View file

@ -1,7 +1,8 @@
using Iceshrimp.Shared.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.Helpers;
using Iceshrimp.Backend.Core.Services; using Iceshrimp.Backend.Core.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -9,13 +10,28 @@ namespace Iceshrimp.Backend.Controllers.Renderers;
public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiService emojiSvc) public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiService emojiSvc)
{ {
public async Task<NoteResponse> RenderOne(Note note, User? localUser, NoteRendererDto? data = null) public async Task<NoteResponse> RenderOne(
Note note, User? user, Filter.FilterContext? filterContext = null, NoteRendererDto? data = null
)
{ {
var res = await RenderBaseInternal(note, localUser, data); var res = await RenderBaseInternal(note, user, data);
var renote = note.Renote is { IsPureRenote: true } ? await RenderRenote(note.Renote, localUser, data) : null; var renote = note.Renote is { IsPureRenote: true } ? await RenderRenote(note.Renote, user, data) : null;
var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, localUser, data) : null; var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, user, data) : null;
var reply = note.Reply != null ? await RenderBase(note.Reply, localUser, data) : null; var reply = note.Reply != null ? await RenderBase(note.Reply, user, data) : null;
var filters = data?.Filters ?? await GetFilters(user, filterContext);
var filtered = FilterHelper.IsFiltered([note, note.Reply, note.Renote, note.Renote?.Renote], filters);
if (filtered.HasValue)
{
res.Filtered = new NoteFilteredSchema
{
Id = filtered.Value.filter.Id,
Keyword = filtered.Value.keyword,
Hide = filtered.Value.filter.Action == Filter.FilterAction.Hide
};
}
res.Renote = renote; res.Renote = renote;
res.RenoteId = note.RenoteId; res.RenoteId = note.RenoteId;
@ -27,10 +43,10 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
return res; return res;
} }
private async Task<NoteWithQuote> RenderRenote(Note note, User? localUser, NoteRendererDto? data = null) private async Task<NoteWithQuote> RenderRenote(Note note, User? user, NoteRendererDto? data = null)
{ {
var res = await RenderBaseInternal(note, localUser, data); var res = await RenderBaseInternal(note, user, data);
var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, localUser, data) : null; var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, user, data) : null;
res.Quote = quote; res.Quote = quote;
res.QuoteId = note.RenoteId; res.QuoteId = note.RenoteId;
@ -41,11 +57,11 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
private async Task<NoteBase> RenderBase(Note note, User? localUser, NoteRendererDto? data = null) private async Task<NoteBase> RenderBase(Note note, User? localUser, NoteRendererDto? data = null)
=> await RenderBaseInternal(note, localUser, data); => await RenderBaseInternal(note, localUser, data);
private async Task<NoteResponse> RenderBaseInternal(Note note, User? localUser, NoteRendererDto? data = null) private async Task<NoteResponse> RenderBaseInternal(Note note, User? user, NoteRendererDto? data = null)
{ {
var user = (data?.Users ?? await GetUsers([note])).First(p => p.Id == note.User.Id); var noteUser = (data?.Users ?? await GetUsers([note])).First(p => p.Id == note.User.Id);
var attachments = (data?.Attachments ?? await GetAttachments([note])).Where(p => note.FileIds.Contains(p.Id)); var attachments = (data?.Attachments ?? await GetAttachments([note])).Where(p => note.FileIds.Contains(p.Id));
var reactions = (data?.Reactions ?? await GetReactions([note], localUser)).Where(p => p.NoteId == note.Id); var reactions = (data?.Reactions ?? await GetReactions([note], user)).Where(p => p.NoteId == note.Id);
return new NoteResponse return new NoteResponse
{ {
@ -54,7 +70,7 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
Text = note.Text, Text = note.Text,
Cw = note.Cw, Cw = note.Cw,
Visibility = RenderVisibility(note.Visibility), Visibility = RenderVisibility(note.Visibility),
User = user, User = noteUser,
Attachments = attachments.ToList(), Attachments = attachments.ToList(),
Reactions = reactions.ToList() Reactions = reactions.ToList()
}; };
@ -130,7 +146,15 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
.ToList(); .ToList();
} }
public async Task<IEnumerable<NoteResponse>> RenderMany(IEnumerable<Note> notes, User? user) private async Task<List<Filter>> GetFilters(User? user, Filter.FilterContext? filterContext)
{
if (filterContext == null) return [];
return await db.Filters.Where(p => p.User == user && p.Contexts.Contains(filterContext.Value)).ToListAsync();
}
public async Task<IEnumerable<NoteResponse>> RenderMany(
IEnumerable<Note> notes, User? user, Filter.FilterContext? filterContext = null
)
{ {
var notesList = notes.ToList(); var notesList = notes.ToList();
if (notesList.Count == 0) return []; if (notesList.Count == 0) return [];
@ -139,10 +163,11 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
{ {
Users = await GetUsers(allNotes), Users = await GetUsers(allNotes),
Attachments = await GetAttachments(allNotes), Attachments = await GetAttachments(allNotes),
Reactions = await GetReactions(allNotes, user) Reactions = await GetReactions(allNotes, user),
Filters = await GetFilters(user, filterContext)
}; };
return await notesList.Select(p => RenderOne(p, user, data)).AwaitAllAsync(); return await notesList.Select(p => RenderOne(p, user, filterContext, data)).AwaitAllAsync();
} }
public class NoteRendererDto public class NoteRendererDto
@ -150,5 +175,6 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
public List<NoteAttachment>? Attachments; public List<NoteAttachment>? Attachments;
public List<NoteReactionSchema>? Reactions; public List<NoteReactionSchema>? Reactions;
public List<UserResponse>? Users; public List<UserResponse>? Users;
public List<Filter>? Filters;
} }
} }

View file

@ -62,7 +62,7 @@ public class NotificationRenderer(
private async Task<List<NoteResponse>> GetNotes(IEnumerable<Notification> notifications, User user) private async Task<List<NoteResponse>> GetNotes(IEnumerable<Notification> notifications, User user)
{ {
var notes = notifications.Select(p => p.Note).OfType<Note>().DistinctBy(p => p.Id); var notes = notifications.Select(p => p.Note).OfType<Note>().DistinctBy(p => p.Id);
return await noteRenderer.RenderMany(notes, user).ToListAsync(); return await noteRenderer.RenderMany(notes, user, Filter.FilterContext.Notifications).ToListAsync();
} }
public async Task<IEnumerable<NotificationResponse>> RenderMany(IEnumerable<Notification> notifications, User user) public async Task<IEnumerable<NotificationResponse>> RenderMany(IEnumerable<Notification> notifications, User user)

View file

@ -4,6 +4,7 @@ using Iceshrimp.Backend.Controllers.Renderers;
using Iceshrimp.Backend.Controllers.Schemas; using Iceshrimp.Backend.Controllers.Schemas;
using Iceshrimp.Shared.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.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;
@ -39,6 +40,6 @@ public class TimelineController(DatabaseContext db, CacheService cache, NoteRend
.PrecomputeVisibilities(user) .PrecomputeVisibilities(user)
.ToListAsync(); .ToListAsync();
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user)); return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user, Filter.FilterContext.Home));
} }
} }

View file

@ -4,6 +4,7 @@ using Iceshrimp.Backend.Controllers.Renderers;
using Iceshrimp.Backend.Controllers.Schemas; using Iceshrimp.Backend.Controllers.Schemas;
using Iceshrimp.Shared.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.Extensions; using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -69,6 +70,7 @@ public class UserController(
.Paginate(pq, ControllerContext) .Paginate(pq, ControllerContext)
.ToListAsync(); .ToListAsync();
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), localUser)); return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), localUser,
Filter.FilterContext.Accounts));
} }
} }

View file

@ -9,6 +9,7 @@ public class NoteResponse : NoteWithQuote
[J("replyId")] public string? ReplyId { get; set; } [J("replyId")] public string? ReplyId { get; set; }
[J("renote")] public NoteWithQuote? Renote { get; set; } [J("renote")] public NoteWithQuote? Renote { get; set; }
[J("renoteId")] public string? RenoteId { get; set; } [J("renoteId")] public string? RenoteId { get; set; }
[J("filtered")] public NoteFilteredSchema? Filtered { get; set; }
} }
public class NoteWithQuote : NoteBase public class NoteWithQuote : NoteBase
@ -46,3 +47,10 @@ public class NoteReactionSchema
[J("reacted")] public required bool Reacted { get; set; } [J("reacted")] public required bool Reacted { get; set; }
[J("url")] public required string? Url { get; set; } [J("url")] public required string? Url { get; set; }
} }
public class NoteFilteredSchema
{
[J("filterId")] public required long Id { get; set; }
[J("keyword")] public required string Keyword { get; set; }
[J("drop")] public required bool Hide { get; set; }
}