159 lines
6.1 KiB
C#
159 lines
6.1 KiB
C#
using DysonNetwork.Shared.Proto;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using Grpc.Core;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using DysonNetwork.Pass.Localization;
|
|
using DysonNetwork.Shared;
|
|
using DysonNetwork.Shared.Cache;
|
|
using Microsoft.Extensions.Localization;
|
|
|
|
namespace DysonNetwork.Pass.Realm;
|
|
|
|
public class RealmServiceGrpc(
|
|
AppDatabase db,
|
|
RingService.RingServiceClient pusher,
|
|
IStringLocalizer<NotificationResource> localizer,
|
|
ICacheService cache
|
|
)
|
|
: Shared.Proto.RealmService.RealmServiceBase
|
|
{
|
|
private const string CacheKeyPrefix = "account:realms:";
|
|
|
|
public override async Task<Shared.Proto.Realm> GetRealm(GetRealmRequest request, ServerCallContext context)
|
|
{
|
|
var realm = request.QueryCase switch
|
|
{
|
|
GetRealmRequest.QueryOneofCase.Id when !string.IsNullOrWhiteSpace(request.Id) => await db.Realms.FindAsync(
|
|
Guid.Parse(request.Id)),
|
|
GetRealmRequest.QueryOneofCase.Slug when !string.IsNullOrWhiteSpace(request.Slug) => await db.Realms
|
|
.FirstOrDefaultAsync(r => r.Slug == request.Slug),
|
|
_ => throw new RpcException(new Status(StatusCode.InvalidArgument, "Must provide either id or slug"))
|
|
};
|
|
|
|
return realm == null
|
|
? throw new RpcException(new Status(StatusCode.NotFound, "Realm not found"))
|
|
: realm.ToProtoValue();
|
|
}
|
|
|
|
public override async Task<GetRealmBatchResponse> GetRealmBatch(GetRealmBatchRequest request, ServerCallContext context)
|
|
{
|
|
var ids = request.Ids.Select(Guid.Parse).ToList();
|
|
var realms = await db.Realms.Where(r => ids.Contains(r.Id)).ToListAsync();
|
|
var response = new GetRealmBatchResponse();
|
|
response.Realms.AddRange(realms.Select(r => r.ToProtoValue()));
|
|
return response;
|
|
}
|
|
|
|
public override async Task<GetUserRealmsResponse> GetUserRealms(GetUserRealmsRequest request,
|
|
ServerCallContext context)
|
|
{
|
|
var accountId = Guid.Parse(request.AccountId);
|
|
var cacheKey = $"{CacheKeyPrefix}{accountId}";
|
|
var (found, cachedRealms) = await cache.GetAsyncWithStatus<List<Guid>>(cacheKey);
|
|
if (found && cachedRealms != null)
|
|
return new GetUserRealmsResponse { RealmIds = { cachedRealms.Select(g => g.ToString()) } };
|
|
|
|
var realms = await db.RealmMembers
|
|
.Include(m => m.Realm)
|
|
.Where(m => m.AccountId == accountId)
|
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
|
.Where(m => m.Realm != null)
|
|
.Select(m => m.Realm!.Id)
|
|
.ToListAsync();
|
|
|
|
// Cache the result for 5 minutes
|
|
await cache.SetAsync(cacheKey, realms, TimeSpan.FromMinutes(5));
|
|
|
|
return new GetUserRealmsResponse { RealmIds = { realms.Select(g => g.ToString()) } };
|
|
}
|
|
|
|
public override async Task<GetPublicRealmsResponse> GetPublicRealms(Empty request, ServerCallContext context)
|
|
{
|
|
var realms = await db.Realms.Where(r => r.IsPublic).ToListAsync();
|
|
var response = new GetPublicRealmsResponse();
|
|
response.Realms.AddRange(realms.Select(r => r.ToProtoValue()));
|
|
return response;
|
|
}
|
|
|
|
public override async Task<Empty> SendInviteNotify(SendInviteNotifyRequest request, ServerCallContext context)
|
|
{
|
|
var member = request.Member;
|
|
var account = await db.Accounts
|
|
.AsNoTracking()
|
|
.Include(a => a.Profile)
|
|
.FirstOrDefaultAsync(a => a.Id == Guid.Parse(member.AccountId));
|
|
|
|
if (account == null) throw new RpcException(new Status(StatusCode.NotFound, "Account not found"));
|
|
|
|
CultureService.SetCultureInfo(account.Language);
|
|
|
|
await pusher.SendPushNotificationToUserAsync(
|
|
new SendPushNotificationToUserRequest
|
|
{
|
|
UserId = account.Id.ToString(),
|
|
Notification = new PushNotification
|
|
{
|
|
Topic = "invites.realms",
|
|
Title = localizer["RealmInviteTitle"],
|
|
Body = localizer["RealmInviteBody", member.Realm?.Name ?? "Unknown Realm"],
|
|
ActionUri = "/realms",
|
|
IsSavable = true
|
|
}
|
|
}
|
|
);
|
|
|
|
return new Empty();
|
|
}
|
|
|
|
public override async Task<BoolValue> IsMemberWithRole(IsMemberWithRoleRequest request, ServerCallContext context)
|
|
{
|
|
if (request.RequiredRoles.Count == 0)
|
|
return new BoolValue { Value = false };
|
|
|
|
var maxRequiredRole = request.RequiredRoles.Max();
|
|
var member = await db.RealmMembers
|
|
.Where(m => m.RealmId == Guid.Parse(request.RealmId) && m.AccountId == Guid.Parse(request.AccountId) &&
|
|
m.JoinedAt != null && m.LeaveAt == null)
|
|
.FirstOrDefaultAsync();
|
|
return new BoolValue { Value = member?.Role >= maxRequiredRole };
|
|
}
|
|
|
|
public override async Task<RealmMember> LoadMemberAccount(LoadMemberAccountRequest request,
|
|
ServerCallContext context)
|
|
{
|
|
var member = request.Member;
|
|
var account = await db.Accounts
|
|
.AsNoTracking()
|
|
.Include(a => a.Profile)
|
|
.FirstOrDefaultAsync(a => a.Id == Guid.Parse(member.AccountId));
|
|
|
|
var response = new RealmMember(member) { Account = account?.ToProtoValue() };
|
|
return response;
|
|
}
|
|
|
|
public override async Task<LoadMemberAccountsResponse> LoadMemberAccounts(LoadMemberAccountsRequest request,
|
|
ServerCallContext context)
|
|
{
|
|
var accountIds = request.Members.Select(m => Guid.Parse(m.AccountId)).ToList();
|
|
var accounts = await db.Accounts
|
|
.AsNoTracking()
|
|
.Include(a => a.Profile)
|
|
.Where(a => accountIds.Contains(a.Id))
|
|
.ToDictionaryAsync(a => a.Id, a => a.ToProtoValue());
|
|
|
|
var response = new LoadMemberAccountsResponse();
|
|
foreach (var member in request.Members)
|
|
{
|
|
var updatedMember = new RealmMember(member);
|
|
if (accounts.TryGetValue(Guid.Parse(member.AccountId), out var account))
|
|
{
|
|
updatedMember.Account = account;
|
|
}
|
|
|
|
response.Members.Add(updatedMember);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
}
|