refactor(backend): use explicit types instead of var by default
This commit is contained in:
parent
bc7fd6d804
commit
649988db25
52 changed files with 506 additions and 420 deletions
|
@ -123,22 +123,27 @@ public class AuthService(
|
|||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var user = await db.Users.FirstOrDefaultAsync(
|
||||
User? user = await db.Users.FirstOrDefaultAsync(
|
||||
u => u.AuthMethods.Any(a => a.AuthType == AuthType.Email && a.RemoteId == email),
|
||||
ct
|
||||
);
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApiError.NotFound(
|
||||
"No user with that email address found, or password is incorrect",
|
||||
ErrorCode.UserNotFound
|
||||
);
|
||||
}
|
||||
|
||||
var pwResult = await VerifyHashedPasswordAsync(user, password, ct);
|
||||
PasswordVerificationResult pwResult = await VerifyHashedPasswordAsync(user, password, ct);
|
||||
if (pwResult == PasswordVerificationResult.Failed) // TODO: this seems to fail on some valid passwords?
|
||||
{
|
||||
throw new ApiError.NotFound(
|
||||
"No user with that email address found, or password is incorrect",
|
||||
ErrorCode.UserNotFound
|
||||
);
|
||||
}
|
||||
|
||||
if (pwResult == PasswordVerificationResult.SuccessRehashNeeded)
|
||||
{
|
||||
user.Password = await HashPasswordAsync(user, password, ct);
|
||||
|
@ -169,7 +174,7 @@ public class AuthService(
|
|||
throw new FoxnounsError("Password for user supplied to ValidatePasswordAsync was null");
|
||||
}
|
||||
|
||||
var pwResult = await VerifyHashedPasswordAsync(user, password, ct);
|
||||
PasswordVerificationResult pwResult = await VerifyHashedPasswordAsync(user, password, ct);
|
||||
return pwResult
|
||||
is PasswordVerificationResult.SuccessRehashNeeded
|
||||
or PasswordVerificationResult.Success;
|
||||
|
@ -231,13 +236,15 @@ public class AuthService(
|
|||
AssertValidAuthType(authType, app);
|
||||
|
||||
// This is already checked when
|
||||
var currentCount = await db
|
||||
int currentCount = await db
|
||||
.AuthMethods.Where(m => m.UserId == userId && m.AuthType == authType)
|
||||
.CountAsync(ct);
|
||||
if (currentCount >= AuthUtils.MaxAuthMethodsPerType)
|
||||
{
|
||||
throw new ApiError.BadRequest(
|
||||
"Too many linked accounts of this type, maximum of 3 per account."
|
||||
);
|
||||
}
|
||||
|
||||
var authMethod = new AuthMethod
|
||||
{
|
||||
|
@ -262,13 +269,15 @@ public class AuthService(
|
|||
)
|
||||
{
|
||||
if (!AuthUtils.ValidateScopes(application, scopes))
|
||||
{
|
||||
throw new ApiError.BadRequest(
|
||||
"Invalid scopes requested for this token",
|
||||
"scopes",
|
||||
scopes
|
||||
);
|
||||
}
|
||||
|
||||
var (token, hash) = GenerateToken();
|
||||
(string? token, byte[]? hash) = GenerateToken();
|
||||
return (
|
||||
token,
|
||||
new Token
|
||||
|
@ -293,9 +302,9 @@ public class AuthService(
|
|||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var frontendApp = await db.GetFrontendApplicationAsync(ct);
|
||||
Application frontendApp = await db.GetFrontendApplicationAsync(ct);
|
||||
|
||||
var (tokenStr, token) = GenerateToken(
|
||||
(string? tokenStr, Token? token) = GenerateToken(
|
||||
user,
|
||||
frontendApp,
|
||||
["*"],
|
||||
|
@ -308,17 +317,12 @@ public class AuthService(
|
|||
await db.SaveChangesAsync(ct);
|
||||
|
||||
return new CallbackResponse(
|
||||
HasAccount: true,
|
||||
Ticket: null,
|
||||
RemoteUsername: null,
|
||||
User: await userRenderer.RenderUserAsync(
|
||||
user,
|
||||
selfUser: user,
|
||||
renderMembers: false,
|
||||
ct: ct
|
||||
),
|
||||
Token: tokenStr,
|
||||
ExpiresAt: token.ExpiresAt
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
await userRenderer.RenderUserAsync(user, user, renderMembers: false, ct: ct),
|
||||
tokenStr,
|
||||
token.ExpiresAt
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -340,8 +344,8 @@ public class AuthService(
|
|||
|
||||
private static (string, byte[]) GenerateToken()
|
||||
{
|
||||
var token = AuthUtils.RandomToken();
|
||||
var hash = SHA512.HashData(Convert.FromBase64String(token));
|
||||
string token = AuthUtils.RandomToken();
|
||||
byte[] hash = SHA512.HashData(Convert.FromBase64String(token));
|
||||
|
||||
return (token, hash);
|
||||
}
|
||||
|
|
|
@ -18,22 +18,25 @@ public partial class FediverseAuthService
|
|||
Snowflake? existingAppId = null
|
||||
)
|
||||
{
|
||||
var resp = await _client.PostAsJsonAsync(
|
||||
HttpResponseMessage resp = await _client.PostAsJsonAsync(
|
||||
$"https://{instance}/api/v1/apps",
|
||||
new CreateMastodonApplicationRequest(
|
||||
ClientName: $"pronouns.cc (+{_config.BaseUrl})",
|
||||
RedirectUris: MastodonRedirectUri(instance),
|
||||
Scopes: "read read:accounts",
|
||||
Website: _config.BaseUrl
|
||||
$"pronouns.cc (+{_config.BaseUrl})",
|
||||
MastodonRedirectUri(instance),
|
||||
"read read:accounts",
|
||||
_config.BaseUrl
|
||||
)
|
||||
);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
var mastodonApp = await resp.Content.ReadFromJsonAsync<PartialMastodonApplication>();
|
||||
PartialMastodonApplication? mastodonApp =
|
||||
await resp.Content.ReadFromJsonAsync<PartialMastodonApplication>();
|
||||
if (mastodonApp == null)
|
||||
{
|
||||
throw new FoxnounsError(
|
||||
$"Application created on Mastodon-compatible instance {instance} was null"
|
||||
);
|
||||
}
|
||||
|
||||
FediverseApplication app;
|
||||
|
||||
|
@ -75,7 +78,7 @@ public partial class FediverseAuthService
|
|||
if (state != null)
|
||||
await _keyCacheService.ValidateAuthStateAsync(state);
|
||||
|
||||
var tokenResp = await _client.PostAsync(
|
||||
HttpResponseMessage tokenResp = await _client.PostAsync(
|
||||
MastodonTokenUri(app.Domain),
|
||||
new FormUrlEncodedContent(
|
||||
new Dictionary<string, string>
|
||||
|
@ -95,7 +98,7 @@ public partial class FediverseAuthService
|
|||
}
|
||||
|
||||
tokenResp.EnsureSuccessStatusCode();
|
||||
var token = (
|
||||
string? token = (
|
||||
await tokenResp.Content.ReadFromJsonAsync<MastodonTokenResponse>()
|
||||
)?.AccessToken;
|
||||
if (token == null)
|
||||
|
@ -106,9 +109,9 @@ public partial class FediverseAuthService
|
|||
var req = new HttpRequestMessage(HttpMethod.Get, MastodonCurrentUserUri(app.Domain));
|
||||
req.Headers.Add("Authorization", $"Bearer {token}");
|
||||
|
||||
var currentUserResp = await _client.SendAsync(req);
|
||||
HttpResponseMessage currentUserResp = await _client.SendAsync(req);
|
||||
currentUserResp.EnsureSuccessStatusCode();
|
||||
var user = await currentUserResp.Content.ReadFromJsonAsync<FediverseUser>();
|
||||
FediverseUser? user = await currentUserResp.Content.ReadFromJsonAsync<FediverseUser>();
|
||||
if (user == null)
|
||||
{
|
||||
throw new FoxnounsError($"User response from instance {app.Domain} was invalid");
|
||||
|
@ -131,7 +134,7 @@ public partial class FediverseAuthService
|
|||
"An app credentials refresh was requested for {ApplicationId}, creating a new application",
|
||||
app.Id
|
||||
);
|
||||
app = await CreateMastodonApplicationAsync(app.Domain, existingAppId: app.Id);
|
||||
app = await CreateMastodonApplicationAsync(app.Domain, app.Id);
|
||||
}
|
||||
|
||||
state ??= HttpUtility.UrlEncode(await _keyCacheService.GenerateAuthStateAsync());
|
||||
|
|
|
@ -43,7 +43,7 @@ public partial class FediverseAuthService
|
|||
string? state = null
|
||||
)
|
||||
{
|
||||
var app = await GetApplicationAsync(instance);
|
||||
FediverseApplication app = await GetApplicationAsync(instance);
|
||||
return await GenerateAuthUrlAsync(app, forceRefresh, state);
|
||||
}
|
||||
|
||||
|
@ -56,13 +56,15 @@ public partial class FediverseAuthService
|
|||
|
||||
public async Task<FediverseApplication> GetApplicationAsync(string instance)
|
||||
{
|
||||
var app = await _db.FediverseApplications.FirstOrDefaultAsync(a => a.Domain == instance);
|
||||
FediverseApplication? app = await _db.FediverseApplications.FirstOrDefaultAsync(a =>
|
||||
a.Domain == instance
|
||||
);
|
||||
if (app != null)
|
||||
return app;
|
||||
|
||||
_logger.Debug("No application for fediverse instance {Instance}, creating it", instance);
|
||||
|
||||
var softwareName = await GetSoftwareNameAsync(instance);
|
||||
string softwareName = await GetSoftwareNameAsync(instance);
|
||||
|
||||
if (IsMastodonCompatible(softwareName))
|
||||
{
|
||||
|
@ -76,13 +78,14 @@ public partial class FediverseAuthService
|
|||
{
|
||||
_logger.Debug("Requesting software name for fediverse instance {Instance}", instance);
|
||||
|
||||
var wellKnownResp = await _client.GetAsync(
|
||||
HttpResponseMessage wellKnownResp = await _client.GetAsync(
|
||||
new Uri($"https://{instance}/.well-known/nodeinfo")
|
||||
);
|
||||
wellKnownResp.EnsureSuccessStatusCode();
|
||||
|
||||
var wellKnown = await wellKnownResp.Content.ReadFromJsonAsync<WellKnownResponse>();
|
||||
var nodeInfoUrl = wellKnown?.Links.FirstOrDefault(l => l.Rel == NodeInfoRel)?.Href;
|
||||
WellKnownResponse? wellKnown =
|
||||
await wellKnownResp.Content.ReadFromJsonAsync<WellKnownResponse>();
|
||||
string? nodeInfoUrl = wellKnown?.Links.FirstOrDefault(l => l.Rel == NodeInfoRel)?.Href;
|
||||
if (nodeInfoUrl == null)
|
||||
{
|
||||
throw new FoxnounsError(
|
||||
|
@ -90,10 +93,10 @@ public partial class FediverseAuthService
|
|||
);
|
||||
}
|
||||
|
||||
var nodeInfoResp = await _client.GetAsync(nodeInfoUrl);
|
||||
HttpResponseMessage nodeInfoResp = await _client.GetAsync(nodeInfoUrl);
|
||||
nodeInfoResp.EnsureSuccessStatusCode();
|
||||
|
||||
var nodeInfo = await nodeInfoResp.Content.ReadFromJsonAsync<PartialNodeInfo>();
|
||||
PartialNodeInfo? nodeInfo = await nodeInfoResp.Content.ReadFromJsonAsync<PartialNodeInfo>();
|
||||
return nodeInfo?.Software.Name
|
||||
?? throw new FoxnounsError(
|
||||
$"Nodeinfo response for instance {instance} was invalid, no software name"
|
||||
|
|
|
@ -29,7 +29,7 @@ public class RemoteAuthService(
|
|||
)
|
||||
{
|
||||
var redirectUri = $"{config.BaseUrl}/auth/callback/discord";
|
||||
var resp = await _httpClient.PostAsync(
|
||||
HttpResponseMessage resp = await _httpClient.PostAsync(
|
||||
_discordTokenUri,
|
||||
new FormUrlEncodedContent(
|
||||
new Dictionary<string, string>
|
||||
|
@ -45,7 +45,7 @@ public class RemoteAuthService(
|
|||
);
|
||||
if (!resp.IsSuccessStatusCode)
|
||||
{
|
||||
var respBody = await resp.Content.ReadAsStringAsync(ct);
|
||||
string respBody = await resp.Content.ReadAsStringAsync(ct);
|
||||
_logger.Error(
|
||||
"Received error status {StatusCode} when exchanging OAuth token: {ErrorBody}",
|
||||
(int)resp.StatusCode,
|
||||
|
@ -55,16 +55,18 @@ public class RemoteAuthService(
|
|||
}
|
||||
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var token = await resp.Content.ReadFromJsonAsync<DiscordTokenResponse>(ct);
|
||||
DiscordTokenResponse? token = await resp.Content.ReadFromJsonAsync<DiscordTokenResponse>(
|
||||
ct
|
||||
);
|
||||
if (token == null)
|
||||
throw new FoxnounsError("Discord token response was null");
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, _discordUserUri);
|
||||
req.Headers.Add("Authorization", $"{token.token_type} {token.access_token}");
|
||||
|
||||
var resp2 = await _httpClient.SendAsync(req, ct);
|
||||
HttpResponseMessage resp2 = await _httpClient.SendAsync(req, ct);
|
||||
resp2.EnsureSuccessStatusCode();
|
||||
var user = await resp2.Content.ReadFromJsonAsync<DiscordUserResponse>(ct);
|
||||
DiscordUserResponse? user = await resp2.Content.ReadFromJsonAsync<DiscordUserResponse>(ct);
|
||||
if (user == null)
|
||||
throw new FoxnounsError("Discord user response was null");
|
||||
|
||||
|
@ -104,7 +106,7 @@ public class RemoteAuthService(
|
|||
string? instance = null
|
||||
)
|
||||
{
|
||||
var existingAccounts = await db
|
||||
int existingAccounts = await db
|
||||
.AuthMethods.Where(m => m.UserId == userId && m.AuthType == authType)
|
||||
.CountAsync();
|
||||
if (existingAccounts > AuthUtils.MaxAuthMethodsPerType)
|
||||
|
@ -131,7 +133,9 @@ public class RemoteAuthService(
|
|||
string? instance = null
|
||||
)
|
||||
{
|
||||
var accountState = await keyCacheService.GetAddExtraAccountStateAsync(state);
|
||||
AddExtraAccountState? accountState = await keyCacheService.GetAddExtraAccountStateAsync(
|
||||
state
|
||||
);
|
||||
if (
|
||||
accountState == null
|
||||
|| accountState.AuthType != authType
|
||||
|
|
|
@ -28,9 +28,9 @@ public class DataCleanupService(
|
|||
|
||||
private async Task CleanUsersAsync(CancellationToken ct = default)
|
||||
{
|
||||
var selfDeleteExpires = clock.GetCurrentInstant() - User.DeleteAfter;
|
||||
var suspendExpires = clock.GetCurrentInstant() - User.DeleteSuspendedAfter;
|
||||
var users = await db
|
||||
Instant selfDeleteExpires = clock.GetCurrentInstant() - User.DeleteAfter;
|
||||
Instant suspendExpires = clock.GetCurrentInstant() - User.DeleteSuspendedAfter;
|
||||
List<User> users = await db
|
||||
.Users.Include(u => u.Members)
|
||||
.Include(u => u.DataExports)
|
||||
.Where(u =>
|
||||
|
@ -92,13 +92,15 @@ public class DataCleanupService(
|
|||
private async Task CleanExportsAsync(CancellationToken ct = default)
|
||||
{
|
||||
var minExpiredId = Snowflake.FromInstant(clock.GetCurrentInstant() - DataExport.Expiration);
|
||||
var exports = await db.DataExports.Where(d => d.Id < minExpiredId).ToListAsync(ct);
|
||||
List<DataExport> exports = await db
|
||||
.DataExports.Where(d => d.Id < minExpiredId)
|
||||
.ToListAsync(ct);
|
||||
if (exports.Count == 0)
|
||||
return;
|
||||
|
||||
_logger.Debug("Deleting {Count} expired exports", exports.Count);
|
||||
|
||||
foreach (var export in exports)
|
||||
foreach (DataExport? export in exports)
|
||||
{
|
||||
_logger.Debug("Deleting export {ExportId}", export.Id);
|
||||
await objectStorageService.RemoveObjectAsync(
|
||||
|
|
|
@ -41,7 +41,7 @@ public class KeyCacheService(DatabaseContext db, IClock clock, ILogger logger)
|
|||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var value = await db.TemporaryKeys.FirstOrDefaultAsync(k => k.Key == key, ct);
|
||||
TemporaryKey? value = await db.TemporaryKeys.FirstOrDefaultAsync(k => k.Key == key, ct);
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class KeyCacheService(DatabaseContext db, IClock clock, ILogger logger)
|
|||
|
||||
public async Task DeleteExpiredKeysAsync(CancellationToken ct)
|
||||
{
|
||||
var count = await db
|
||||
int count = await db
|
||||
.TemporaryKeys.Where(k => k.Expires < clock.GetCurrentInstant())
|
||||
.ExecuteDeleteAsync(ct);
|
||||
if (count != 0)
|
||||
|
@ -79,7 +79,7 @@ public class KeyCacheService(DatabaseContext db, IClock clock, ILogger logger)
|
|||
)
|
||||
where T : class
|
||||
{
|
||||
var value = JsonConvert.SerializeObject(obj);
|
||||
string value = JsonConvert.SerializeObject(obj);
|
||||
await SetKeyAsync(key, value, expires, ct);
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ public class KeyCacheService(DatabaseContext db, IClock clock, ILogger logger)
|
|||
)
|
||||
where T : class
|
||||
{
|
||||
var value = await GetKeyAsync(key, delete, ct);
|
||||
string? value = await GetKeyAsync(key, delete, ct);
|
||||
return value == null ? default : JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@ public class MemberRendererService(DatabaseContext db, Config config)
|
|||
{
|
||||
public async Task<IEnumerable<PartialMember>> RenderUserMembersAsync(User user, Token? token)
|
||||
{
|
||||
var canReadHiddenMembers =
|
||||
bool canReadHiddenMembers =
|
||||
token != null && token.UserId == user.Id && token.HasScope("member.read");
|
||||
var renderUnlisted =
|
||||
bool renderUnlisted =
|
||||
token != null && token.UserId == user.Id && token.HasScope("user.read_hidden");
|
||||
var canReadMemberList = !user.ListHidden || canReadHiddenMembers;
|
||||
bool canReadMemberList = !user.ListHidden || canReadHiddenMembers;
|
||||
|
||||
IEnumerable<Member> members = canReadMemberList
|
||||
? await db.Members.Where(m => m.UserId == user.Id).OrderBy(m => m.Name).ToListAsync()
|
||||
|
@ -30,7 +30,7 @@ public class MemberRendererService(DatabaseContext db, Config config)
|
|||
string? overrideSid = null
|
||||
)
|
||||
{
|
||||
var renderUnlisted = token?.UserId == member.UserId && token.HasScope("user.read_hidden");
|
||||
bool renderUnlisted = token?.UserId == member.UserId && token.HasScope("user.read_hidden");
|
||||
|
||||
return new MemberResponse(
|
||||
member.Id,
|
||||
|
|
|
@ -3,6 +3,7 @@ using Foxnouns.Backend.Database;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using Prometheus;
|
||||
using ITimer = Prometheus.ITimer;
|
||||
|
||||
namespace Foxnouns.Backend.Services;
|
||||
|
||||
|
@ -16,19 +17,23 @@ public class MetricsCollectionService(ILogger logger, IServiceProvider services,
|
|||
|
||||
public async Task CollectMetricsAsync(CancellationToken ct = default)
|
||||
{
|
||||
var timer = FoxnounsMetrics.MetricsCollectionTime.NewTimer();
|
||||
var now = clock.GetCurrentInstant();
|
||||
ITimer timer = FoxnounsMetrics.MetricsCollectionTime.NewTimer();
|
||||
Instant now = clock.GetCurrentInstant();
|
||||
|
||||
await using var scope = services.CreateAsyncScope();
|
||||
await using AsyncServiceScope scope = services.CreateAsyncScope();
|
||||
// ReSharper disable once SuggestVarOrType_SimpleTypes
|
||||
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||
|
||||
var users = await db.Users.Where(u => !u.Deleted).Select(u => u.LastActive).ToListAsync(ct);
|
||||
List<Instant>? users = await db
|
||||
.Users.Where(u => !u.Deleted)
|
||||
.Select(u => u.LastActive)
|
||||
.ToListAsync(ct);
|
||||
FoxnounsMetrics.UsersCount.Set(users.Count);
|
||||
FoxnounsMetrics.UsersActiveMonthCount.Set(users.Count(i => i > now - Month));
|
||||
FoxnounsMetrics.UsersActiveWeekCount.Set(users.Count(i => i > now - Week));
|
||||
FoxnounsMetrics.UsersActiveDayCount.Set(users.Count(i => i > now - Day));
|
||||
|
||||
var memberCount = await db
|
||||
int memberCount = await db
|
||||
.Members.Include(m => m.User)
|
||||
.Where(m => !m.Unlisted && !m.User.ListHidden && !m.User.Deleted)
|
||||
.CountAsync(ct);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Minio;
|
||||
using Minio.DataModel;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio.Exceptions;
|
||||
|
||||
|
@ -48,13 +49,4 @@ public class ObjectStorageService(ILogger logger, Config config, IMinioClient mi
|
|||
ct
|
||||
);
|
||||
}
|
||||
|
||||
public async Task GetObjectAsync(string path, CancellationToken ct = default)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var resp = await minioClient.GetObjectAsync(
|
||||
new GetObjectArgs().WithBucket(config.Storage.Bucket).WithObject(path),
|
||||
ct
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,13 @@ public class PeriodicTasksService(ILogger logger, IServiceProvider services) : B
|
|||
{
|
||||
_logger.Debug("Running periodic tasks");
|
||||
|
||||
await using var scope = services.CreateAsyncScope();
|
||||
await using AsyncServiceScope scope = services.CreateAsyncScope();
|
||||
|
||||
// The type is literally written on the same line, we can just use `var`
|
||||
// ReSharper disable SuggestVarOrType_SimpleTypes
|
||||
var keyCacheService = scope.ServiceProvider.GetRequiredService<KeyCacheService>();
|
||||
var dataCleanupService = scope.ServiceProvider.GetRequiredService<DataCleanupService>();
|
||||
// ReSharper restore SuggestVarOrType_SimpleTypes
|
||||
|
||||
await keyCacheService.DeleteExpiredKeysAsync(ct);
|
||||
await dataCleanupService.InvokeAsync(ct);
|
||||
|
|
|
@ -43,9 +43,9 @@ public class UserRendererService(
|
|||
)
|
||||
{
|
||||
scopes = scopes.ExpandScopes();
|
||||
var tokenCanReadHiddenMembers = scopes.Contains("member.read") && isSelfUser;
|
||||
var tokenHidden = scopes.Contains("user.read_hidden") && isSelfUser;
|
||||
var tokenPrivileged = scopes.Contains("user.read_privileged") && isSelfUser;
|
||||
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;
|
||||
|
@ -57,12 +57,12 @@ public class UserRendererService(
|
|||
if (!(isSelfUser && tokenCanReadHiddenMembers))
|
||||
members = members.Where(m => !m.Unlisted);
|
||||
|
||||
var flags = await db
|
||||
List<UserFlag> flags = await db
|
||||
.UserFlags.Where(f => f.UserId == user.Id)
|
||||
.OrderBy(f => f.Id)
|
||||
.ToListAsync(ct);
|
||||
|
||||
var authMethods = renderAuthMethods
|
||||
List<AuthMethod> authMethods = renderAuthMethods
|
||||
? await db
|
||||
.AuthMethods.Where(a => a.UserId == user.Id)
|
||||
.Include(a => a.FediverseApplication)
|
||||
|
@ -72,7 +72,7 @@ public class UserRendererService(
|
|||
int? utcOffset = null;
|
||||
if (
|
||||
user.Timezone != null
|
||||
&& TimeZoneInfo.TryFindSystemTimeZoneById(user.Timezone, out var tz)
|
||||
&& TimeZoneInfo.TryFindSystemTimeZoneById(user.Timezone, out TimeZoneInfo? tz)
|
||||
)
|
||||
utcOffset = (int)tz.GetUtcOffset(DateTimeOffset.UtcNow).TotalSeconds;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue