Foxnouns.NET/Foxnouns.Backend/Services/UserRendererService.cs

190 lines
6.4 KiB
C#
Raw Normal View History

using Foxnouns.Backend.Database;
using Foxnouns.Backend.Database.Models;
using Foxnouns.Backend.Utils;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NodaTime;
namespace Foxnouns.Backend.Services;
public class UserRendererService(
DatabaseContext db,
MemberRendererService memberRenderer,
Config config
)
{
public async Task<UserResponse> RenderUserAsync(
User user,
User? selfUser = null,
Token? token = null,
bool renderMembers = true,
bool renderAuthMethods = false,
2024-11-25 17:35:24 +01:00
string? overrideSid = null,
CancellationToken ct = default
) =>
await RenderUserInnerAsync(
user,
selfUser != null && selfUser.Id == user.Id,
token?.Scopes ?? [],
renderMembers,
renderAuthMethods,
overrideSid,
ct
);
public async Task<UserResponse> RenderUserInnerAsync(
User user,
bool isSelfUser,
string[] scopes,
bool renderMembers = true,
bool renderAuthMethods = false,
string? overrideSid = null,
CancellationToken ct = default
)
{
scopes = scopes.ExpandScopes();
bool tokenCanReadHiddenMembers = scopes.Contains("member.read") && isSelfUser;
bool tokenHidden = scopes.Contains("user.read_hidden") && isSelfUser;
bool tokenPrivileged = scopes.Contains("user.read_privileged") && isSelfUser;
renderMembers = renderMembers && (!user.ListHidden || tokenCanReadHiddenMembers);
renderAuthMethods = renderAuthMethods && tokenPrivileged;
IEnumerable<Member> members = renderMembers
? await db.Members.Where(m => m.UserId == user.Id).OrderBy(m => m.Name).ToListAsync(ct)
: [];
// Unless the user is requesting their own members AND the token can read hidden members, we filter out unlisted members.
if (!(isSelfUser && tokenCanReadHiddenMembers))
members = members.Where(m => !m.Unlisted);
List<UserFlag> flags = await db
.UserFlags.Where(f => f.UserId == user.Id)
.OrderBy(f => f.Id)
.ToListAsync(ct);
List<AuthMethod> authMethods = renderAuthMethods
? await db
.AuthMethods.Where(a => a.UserId == user.Id)
.Include(a => a.FediverseApplication)
.ToListAsync(ct)
: [];
2024-11-24 22:19:53 +01:00
int? utcOffset = null;
if (
user.Timezone != null
&& TimeZoneInfo.TryFindSystemTimeZoneById(user.Timezone, out TimeZoneInfo? tz)
2024-11-24 22:19:53 +01:00
)
{
2024-11-24 22:19:53 +01:00
utcOffset = (int)tz.GetUtcOffset(DateTimeOffset.UtcNow).TotalSeconds;
}
2024-11-24 22:19:53 +01:00
return new UserResponse(
user.Id,
2024-11-25 17:35:24 +01:00
overrideSid ?? user.Sid,
user.Username,
user.DisplayName,
user.Bio,
user.MemberTitle,
AvatarUrlFor(user),
user.Links,
user.Names,
user.Pronouns,
user.Fields,
user.CustomPreferences,
flags.Select(f => RenderPrideFlag(f.PrideFlag)),
2024-11-24 22:19:53 +01:00
utcOffset,
2024-10-01 16:06:02 +02:00
user.Role,
renderMembers
? members.Select(m => memberRenderer.RenderPartialMember(m, tokenHidden))
: null,
2024-11-04 22:04:04 +01:00
renderAuthMethods ? authMethods.Select(RenderAuthMethod) : null,
tokenHidden ? user.ListHidden : null,
tokenHidden ? user.LastActive : null,
2024-11-24 22:19:53 +01:00
tokenHidden ? user.LastSidReroll : null,
tokenHidden ? user.Timezone ?? "<none>" : null
);
}
2024-11-04 22:04:04 +01:00
public static AuthMethodResponse RenderAuthMethod(AuthMethod a) =>
new(
a.Id,
a.AuthType,
a.RemoteId,
a.FediverseApplication != null
? $"@{a.RemoteUsername}@{a.FediverseApplication.Domain}"
: a.RemoteUsername
);
public PartialUser RenderPartialUser(User user) =>
new(
user.Id,
user.Sid,
user.Username,
user.DisplayName,
AvatarUrlFor(user),
user.CustomPreferences
);
public string? AvatarUrlFor(User user) =>
user.Avatar != null
? $"{config.MediaBaseUrl}/users/{user.Id}/avatars/{user.Avatar}.webp"
: null;
public string ImageUrlFor(PrideFlag flag) => $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp";
public record UserResponse(
Snowflake Id,
2024-09-26 15:26:52 +02:00
string Sid,
string Username,
string? DisplayName,
string? Bio,
string? MemberTitle,
string? AvatarUrl,
string[] Links,
IEnumerable<FieldEntry> Names,
IEnumerable<Pronoun> Pronouns,
IEnumerable<Field> Fields,
Dictionary<Snowflake, User.CustomPreference> CustomPreferences,
IEnumerable<PrideFlagResponse> Flags,
2024-11-24 22:19:53 +01:00
int? UtcOffset,
[property: JsonConverter(typeof(ScreamingSnakeCaseEnumConverter))] UserRole Role,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
IEnumerable<MemberRendererService.PartialMember>? Members,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
2024-11-04 22:04:04 +01:00
IEnumerable<AuthMethodResponse>? AuthMethods,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
bool? MemberListHidden,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] Instant? LastActive,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
2024-11-24 22:19:53 +01:00
Instant? LastSidReroll,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Timezone
);
2024-11-04 22:04:04 +01:00
public record AuthMethodResponse(
Snowflake Id,
[property: JsonConverter(typeof(ScreamingSnakeCaseEnumConverter))] AuthType Type,
string RemoteId,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
string? RemoteUsername
);
public record PartialUser(
Snowflake Id,
2024-09-26 15:26:52 +02:00
string Sid,
string Username,
string? DisplayName,
string? AvatarUrl,
Dictionary<Snowflake, User.CustomPreference> CustomPreferences
);
public PrideFlagResponse RenderPrideFlag(PrideFlag flag) =>
new(flag.Id, ImageUrlFor(flag), flag.Name, flag.Description);
public record PrideFlagResponse(
Snowflake Id,
string ImageUrl,
string Name,
string? Description
);
}