🛂 Stricter post visibility check

This commit is contained in:
2025-10-24 00:02:27 +08:00
parent 266b9e36e2
commit 4ba6206c9d
3 changed files with 77 additions and 64 deletions

View File

@@ -25,9 +25,9 @@ public partial class PostService(
ILogger<PostService> logger,
FileService.FileServiceClient files,
FileReferenceService.FileReferenceServiceClient fileRefs,
PollService polls,
Publisher.PublisherService ps,
WebReaderService reader
WebReaderService reader,
AccountService.AccountServiceClient accounts
)
{
private const string PostFileUsageIdentifier = "post";
@@ -702,6 +702,16 @@ public partial class PostService(
: new Dictionary<Guid, Dictionary<string, bool>>();
var repliesCountMap = await GetPostRepliesCountBatch(postsId);
// Load user friends if the current user exists
List<SnPublisher> publishers = [];
List<Guid> userFriends = [];
if (currentUser is not null)
{
var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest { AccountId = currentUser.Id });
userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList();
publishers = await ps.GetUserPublishers(Guid.Parse(currentUser.Id));
}
foreach (var post in posts)
{
// Set reaction count
@@ -719,6 +729,26 @@ public partial class PostService(
? repliesCount
: 0;
// Check visibility for replied post
if (post.RepliedPost != null)
{
if (!CanViewPost(post.RepliedPost, currentUser, publishers, userFriends))
{
post.RepliedPost = null;
post.RepliedGone = true;
}
}
// Check visibility for forwarded post
if (post.ForwardedPost != null)
{
if (!CanViewPost(post.ForwardedPost, currentUser, publishers, userFriends))
{
post.ForwardedPost = null;
post.ForwardedGone = true;
}
}
// Track view for each post in the list
if (currentUser != null)
await IncreaseViewCount(post.Id, currentUser.Id);
@@ -729,6 +759,39 @@ public partial class PostService(
return posts;
}
private bool CanViewPost(SnPost post, Account? currentUser, List<SnPublisher> publishers, List<Guid> userFriends)
{
var now = SystemClock.Instance.GetCurrentInstant();
var publishersId = publishers.Select(e => e.Id).ToList();
// Check if post is deleted
if (post.DeletedAt != null)
return false;
if (currentUser is null)
{
// Anonymous user can only view public posts that are published
return post.PublishedAt != null && now >= post.PublishedAt && post.Visibility == PostVisibility.Public;
}
// Check publication status - either published or user is member
var isPublished = post.PublishedAt != null && now >= post.PublishedAt;
var isMember = publishersId.Contains(post.PublisherId);
if (!isPublished && !isMember)
return false;
// Check visibility
if (post.Visibility == PostVisibility.Private && !isMember)
return false;
if (post.Visibility == PostVisibility.Friends &&
!(post.Publisher.AccountId.HasValue && userFriends.Contains(post.Publisher.AccountId.Value) || isMember))
return false;
// Public and Unlisted are allowed
return true;
}
private async Task<Dictionary<Guid, int>> GetPostRepliesCountBatch(List<Guid> postIds)
{
return await db.Posts
@@ -739,47 +802,7 @@ public partial class PostService(
g => g.Count()
);
}
private async Task LoadPostEmbed(SnPost post, Account? currentUser)
{
if (!post.Meta!.TryGetValue("embeds", out var value))
return;
var embeds = value switch
{
JsonElement e => e.Deserialize<List<Dictionary<string, object>>>(),
_ => null
};
if (embeds is null)
return;
// Find the index of the poll embed first
var pollIndex = embeds.FindIndex(e =>
e.ContainsKey("type") && ((JsonElement)e["type"]).ToString() == "poll"
);
if (pollIndex < 0)
{
post.Meta["embeds"] = embeds;
return;
}
var pollEmbed = embeds[pollIndex];
try
{
var pollId = Guid.Parse(((JsonElement)pollEmbed["id"]).ToString());
Guid? currentUserId = currentUser is not null ? Guid.Parse(currentUser.Id) : null;
var updatedPoll = await polls.LoadPollEmbed(pollId, currentUserId);
embeds[pollIndex] = EmbeddableBase.ToDictionary(updatedPoll);
post.Meta["embeds"] = embeds;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to load poll embed for post {PostId}", post.Id);
}
}
public async Task<List<SnPost>> LoadPostInfo(
List<SnPost> posts,
Account? currentUser = null,
@@ -791,12 +814,6 @@ public partial class PostService(
posts = await LoadPublishers(posts);
posts = await LoadInteractive(posts, currentUser);
foreach (
var post in posts
.Where(e => e.Meta is not null && e.Meta.ContainsKey("embeds"))
)
await LoadPostEmbed(post, currentUser);
if (truncate)
posts = TruncatePostContent(posts);