added methods to update vanity colors and submodule bump
This commit is contained in:
Submodule LightlessAPI updated: eb04433427...b85b54f560
@@ -6,6 +6,7 @@ using LightlessSync.API.Data;
|
||||
using LightlessSync.API.Dto.Group;
|
||||
using LightlessSyncShared.Metrics;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using System.Threading;
|
||||
|
||||
namespace LightlessSyncServer.Hubs;
|
||||
|
||||
@@ -97,6 +98,28 @@ public partial class LightlessHub
|
||||
await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<User?> EnsureUserHasVanity(string uid, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken = cancellationToken == default && _contextAccessor.HttpContext != null
|
||||
? _contextAccessor.HttpContext.RequestAborted
|
||||
: cancellationToken;
|
||||
|
||||
var user = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid, cancellationToken).ConfigureAwait(false);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogCallWarning(LightlessHubLogger.Args("vanity check", uid, "missing user"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user.HasVanity != true)
|
||||
{
|
||||
_logger.LogCallWarning(LightlessHubLogger.Args("vanity check", uid, "no vanity"));
|
||||
return null;
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private async Task SendGroupDeletedToAll(List<GroupPair> groupUsers)
|
||||
{
|
||||
foreach (var pair in groupUsers)
|
||||
|
||||
@@ -605,6 +605,62 @@ public partial class LightlessHub
|
||||
_lightlessMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, recipientUids.Count);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserUpdateVanityColors(UserVanityColorsDto dto)
|
||||
{
|
||||
if (dto == null)
|
||||
{
|
||||
throw new HubException("Vanity color payload required");
|
||||
}
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto.TextColorHex, dto.TextGlowColorHex));
|
||||
|
||||
var cooldownKey = $"vanity:colors:{UserUID}";
|
||||
var existingCooldown = await _redis.GetAsync<string>(cooldownKey).ConfigureAwait(false);
|
||||
if (existingCooldown != null)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, "You can update vanity colors once per minute.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await EnsureUserHasVanity(UserUID).ConfigureAwait(false);
|
||||
if (user == null)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, "Vanity privileges are required to update colors.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryNormalizeColor(dto.TextColorHex, out var textColor, out var textColorError))
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, textColorError).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryNormalizeColor(dto.TextGlowColorHex, out var textGlowColor, out var textGlowError))
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, textGlowError).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var currentColor = user.TextColorHex ?? string.Empty;
|
||||
var currentGlow = user.TextGlowColorHex ?? string.Empty;
|
||||
|
||||
if (string.Equals(currentColor, textColor, StringComparison.Ordinal) &&
|
||||
string.Equals(currentGlow, textGlowColor, StringComparison.Ordinal))
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Vanity colors are already set to these values.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
user.TextColorHex = textColor;
|
||||
user.TextGlowColorHex = textGlowColor;
|
||||
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
await _redis.AddAsync(cooldownKey, "true", TimeSpan.FromMinutes(1)).ConfigureAwait(false);
|
||||
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Vanity colors updated.").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserRemovePair(UserDto dto)
|
||||
{
|
||||
@@ -741,6 +797,43 @@ public partial class LightlessHub
|
||||
await Clients.Caller.Client_UserUpdateProfile(new(dto.User)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static bool TryNormalizeColor(string? value, out string normalized, out string errorMessage)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
normalized = string.Empty;
|
||||
errorMessage = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
var trimmed = value.Trim();
|
||||
if (trimmed.StartsWith("#", StringComparison.Ordinal))
|
||||
{
|
||||
trimmed = trimmed[1..];
|
||||
}
|
||||
|
||||
if (trimmed.Length != 6 && trimmed.Length != 8)
|
||||
{
|
||||
normalized = string.Empty;
|
||||
errorMessage = "Colors must contain 6 or 8 hexadecimal characters.";
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var ch in trimmed)
|
||||
{
|
||||
if (!Uri.IsHexDigit(ch))
|
||||
{
|
||||
normalized = string.Empty;
|
||||
errorMessage = "Colors may only contain hexadecimal characters.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
normalized = "#" + trimmed.ToUpperInvariant();
|
||||
errorMessage = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^([a-z0-9_ '+&,\.\-\{\}]+\/)+([a-z0-9_ '+&,\.\-\{\}]+\.[a-z]{3,4})$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
private static partial Regex GamePathRegex();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user