2022-02-21 22:35:06 +00:00
|
|
|
using VoidCat.Model;
|
|
|
|
using VoidCat.Services.Abstractions;
|
|
|
|
|
|
|
|
namespace VoidCat.Services.Users;
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
public class CacheUserStore : IUserStore
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
|
|
|
private const string UserList = "users";
|
|
|
|
private readonly ICache _cache;
|
|
|
|
|
2022-06-13 14:20:12 +00:00
|
|
|
public CacheUserStore(ICache cache)
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
|
|
|
_cache = cache;
|
|
|
|
}
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
2022-02-21 22:35:06 +00:00
|
|
|
public async ValueTask<Guid?> LookupUser(string email)
|
|
|
|
{
|
|
|
|
return await _cache.Get<Guid>(MapKey(email));
|
|
|
|
}
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
2022-06-13 14:20:12 +00:00
|
|
|
public ValueTask<T?> Get<T>(Guid id) where T : VoidUser
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
2022-06-13 14:20:12 +00:00
|
|
|
return _cache.Get<T>(MapKey(id));
|
2022-02-21 22:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
public ValueTask<VoidUser?> Get(Guid id)
|
|
|
|
{
|
|
|
|
return Get<VoidUser>(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
public ValueTask<InternalVoidUser?> GetPrivate(Guid id)
|
|
|
|
{
|
|
|
|
return Get<InternalVoidUser>(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2022-03-07 13:38:53 +00:00
|
|
|
public async ValueTask Set(Guid id, InternalVoidUser user)
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
2022-03-07 13:38:53 +00:00
|
|
|
if (id != user.Id) throw new InvalidOperationException();
|
|
|
|
|
2022-02-21 22:35:06 +00:00
|
|
|
await _cache.Set(MapKey(user.Id), user);
|
|
|
|
await _cache.AddToList(UserList, user.Id.ToString());
|
|
|
|
await _cache.Set(MapKey(user.Email), user.Id.ToString());
|
|
|
|
}
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
2022-02-27 13:54:25 +00:00
|
|
|
public async ValueTask<PagedResult<PrivateVoidUser>> ListUsers(PagedRequest request)
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
2022-03-04 20:05:01 +00:00
|
|
|
var users = (await _cache.GetList(UserList))
|
|
|
|
.Select<string, Guid?>(a => Guid.TryParse(a, out var g) ? g : null)
|
|
|
|
.Where(a => a.HasValue).Select(a => a.Value);
|
2022-02-24 12:00:28 +00:00
|
|
|
users = (request.SortBy, request.SortOrder) switch
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
2022-02-24 12:00:28 +00:00
|
|
|
(PagedSortBy.Id, PageSortOrder.Asc) => users?.OrderBy(a => a),
|
|
|
|
(PagedSortBy.Id, PageSortOrder.Dsc) => users?.OrderByDescending(a => a),
|
|
|
|
_ => users
|
|
|
|
};
|
|
|
|
|
2022-02-27 13:54:25 +00:00
|
|
|
async IAsyncEnumerable<PrivateVoidUser> EnumerateUsers(IEnumerable<Guid> ids)
|
2022-02-24 12:00:28 +00:00
|
|
|
{
|
2022-02-27 13:54:25 +00:00
|
|
|
var usersLoaded = await Task.WhenAll(ids.Select(async a => await Get<PrivateVoidUser>(a)));
|
2022-02-24 12:00:28 +00:00
|
|
|
foreach (var user in usersLoaded)
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
2022-02-24 12:00:28 +00:00
|
|
|
if (user != default)
|
2022-02-21 22:35:06 +00:00
|
|
|
{
|
2022-02-24 12:00:28 +00:00
|
|
|
yield return user;
|
2022-02-21 22:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-24 12:00:28 +00:00
|
|
|
|
|
|
|
return new()
|
|
|
|
{
|
|
|
|
Page = request.Page,
|
|
|
|
PageSize = request.PageSize,
|
|
|
|
TotalResults = users?.Count() ?? 0,
|
|
|
|
Results = EnumerateUsers(users?.Skip(request.PageSize * request.Page).Take(request.PageSize))
|
|
|
|
};
|
2022-02-21 22:35:06 +00:00
|
|
|
}
|
2022-03-04 20:05:01 +00:00
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
2022-03-04 20:05:01 +00:00
|
|
|
public async ValueTask UpdateProfile(PublicVoidUser newUser)
|
2022-02-27 18:15:37 +00:00
|
|
|
{
|
|
|
|
var oldUser = await Get<InternalVoidUser>(newUser.Id);
|
|
|
|
if (oldUser == null) return;
|
|
|
|
|
2022-03-08 13:47:42 +00:00
|
|
|
//retain flags
|
|
|
|
var isEmailVerified = oldUser.Flags.HasFlag(VoidUserFlags.EmailVerified);
|
2022-05-19 22:27:49 +00:00
|
|
|
|
2022-02-27 18:15:37 +00:00
|
|
|
// update only a few props
|
|
|
|
oldUser.Avatar = newUser.Avatar;
|
2022-03-08 13:47:42 +00:00
|
|
|
oldUser.Flags = newUser.Flags | (isEmailVerified ? VoidUserFlags.EmailVerified : 0);
|
2022-02-27 18:15:37 +00:00
|
|
|
oldUser.DisplayName = newUser.DisplayName;
|
|
|
|
|
2022-03-07 13:38:53 +00:00
|
|
|
await Set(newUser.Id, oldUser);
|
|
|
|
}
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
public async ValueTask UpdateLastLogin(Guid id, DateTime timestamp)
|
|
|
|
{
|
|
|
|
var user = await Get<InternalVoidUser>(id);
|
|
|
|
if (user != default)
|
|
|
|
{
|
|
|
|
user.LastLogin = timestamp;
|
|
|
|
await Set(user.Id, user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2022-03-07 13:38:53 +00:00
|
|
|
public async ValueTask Delete(Guid id)
|
|
|
|
{
|
|
|
|
var user = await Get<InternalVoidUser>(id);
|
|
|
|
if (user == default) throw new InvalidOperationException();
|
|
|
|
await Delete(user);
|
2022-02-27 18:15:37 +00:00
|
|
|
}
|
2022-03-04 20:05:01 +00:00
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
private async ValueTask Delete(PrivateVoidUser user)
|
2022-03-04 20:05:01 +00:00
|
|
|
{
|
|
|
|
await _cache.Delete(MapKey(user.Id));
|
|
|
|
await _cache.RemoveFromList(UserList, user.Id.ToString());
|
|
|
|
await _cache.Delete(MapKey(user.Email));
|
|
|
|
}
|
|
|
|
|
2022-02-21 22:35:06 +00:00
|
|
|
private static string MapKey(Guid id) => $"user:{id}";
|
2022-03-04 20:05:01 +00:00
|
|
|
private static string MapKey(string email) => $"user:email:{email.Hash("md5")}";
|
|
|
|
}
|