🐛 Dozens of fixes
This commit is contained in:
@@ -6,11 +6,13 @@ namespace DysonNetwork.Gateway;
|
|||||||
|
|
||||||
public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
|
public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly object _lock = new();
|
||||||
private readonly IEtcdClient _etcdClient;
|
private readonly IEtcdClient _etcdClient;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
private readonly ILogger<RegistryProxyConfigProvider> _logger;
|
private readonly ILogger<RegistryProxyConfigProvider> _logger;
|
||||||
private readonly CancellationTokenSource _watchCts = new();
|
private readonly CancellationTokenSource _watchCts = new();
|
||||||
private CancellationTokenSource _cts = new();
|
private CancellationTokenSource _cts;
|
||||||
|
private IProxyConfig _config;
|
||||||
|
|
||||||
public RegistryProxyConfigProvider(IEtcdClient etcdClient, IConfiguration configuration,
|
public RegistryProxyConfigProvider(IEtcdClient etcdClient, IConfiguration configuration,
|
||||||
ILogger<RegistryProxyConfigProvider> logger)
|
ILogger<RegistryProxyConfigProvider> logger)
|
||||||
@@ -18,19 +20,33 @@ public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
|
|||||||
_etcdClient = etcdClient;
|
_etcdClient = etcdClient;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_config = LoadConfig();
|
||||||
|
|
||||||
// Watch for changes in etcd
|
// Watch for changes in etcd
|
||||||
_etcdClient.WatchRange("/services/", _ =>
|
_etcdClient.WatchRange("/services/", _ =>
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Etcd configuration changed. Reloading proxy config.");
|
_logger.LogInformation("Etcd configuration changed. Reloading proxy config.");
|
||||||
_cts.Cancel();
|
ReloadConfig();
|
||||||
_cts = new CancellationTokenSource();
|
|
||||||
}, cancellationToken: _watchCts.Token);
|
}, cancellationToken: _watchCts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProxyConfig GetConfig()
|
public IProxyConfig GetConfig() => _config;
|
||||||
|
|
||||||
|
private void ReloadConfig()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
var oldCts = _cts;
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_config = LoadConfig();
|
||||||
|
oldCts.Cancel();
|
||||||
|
oldCts.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IProxyConfig LoadConfig()
|
||||||
{
|
{
|
||||||
// This will be called by YARP when it needs a new config
|
|
||||||
_logger.LogInformation("Generating new proxy config.");
|
_logger.LogInformation("Generating new proxy config.");
|
||||||
var response = _etcdClient.GetRange("/services/");
|
var response = _etcdClient.GetRange("/services/");
|
||||||
var kvs = response.Kvs;
|
var kvs = response.Kvs;
|
||||||
@@ -184,17 +200,15 @@ public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
|
|||||||
_logger.LogInformation(" Added Path-based Route: {Path}", pathRoute.Match.Path);
|
_logger.LogInformation(" Added Path-based Route: {Path}", pathRoute.Match.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CustomProxyConfig(routes, clusters);
|
return new CustomProxyConfig(routes, clusters, new Microsoft.Extensions.Primitives.CancellationChangeToken(_cts.Token));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CustomProxyConfig(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
|
private class CustomProxyConfig(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters, Microsoft.Extensions.Primitives.IChangeToken changeToken)
|
||||||
: IProxyConfig
|
: IProxyConfig
|
||||||
{
|
{
|
||||||
public IReadOnlyList<RouteConfig> Routes { get; } = routes;
|
public IReadOnlyList<RouteConfig> Routes { get; } = routes;
|
||||||
public IReadOnlyList<ClusterConfig> Clusters { get; } = clusters;
|
public IReadOnlyList<ClusterConfig> Clusters { get; } = clusters;
|
||||||
|
public Microsoft.Extensions.Primitives.IChangeToken ChangeToken { get; } = changeToken;
|
||||||
public Microsoft.Extensions.Primitives.IChangeToken ChangeToken { get; } =
|
|
||||||
new Microsoft.Extensions.Primitives.CancellationChangeToken(CancellationToken.None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record DirectRouteConfig
|
public record DirectRouteConfig
|
||||||
@@ -211,4 +225,4 @@ public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
|
|||||||
_watchCts.Cancel();
|
_watchCts.Cancel();
|
||||||
_watchCts.Dispose();
|
_watchCts.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -71,7 +71,7 @@ public class PushService(IConfiguration config, AppDatabase db, IHttpClientFacto
|
|||||||
string? title = null,
|
string? title = null,
|
||||||
string? subtitle = null,
|
string? subtitle = null,
|
||||||
string? content = null,
|
string? content = null,
|
||||||
Dictionary<string, object>? meta = null,
|
Dictionary<string, object?> meta = null,
|
||||||
string? actionUri = null,
|
string? actionUri = null,
|
||||||
bool isSilent = false,
|
bool isSilent = false,
|
||||||
bool save = true)
|
bool save = true)
|
||||||
@@ -79,7 +79,6 @@ public class PushService(IConfiguration config, AppDatabase db, IHttpClientFacto
|
|||||||
if (title is null && subtitle is null && content is null)
|
if (title is null && subtitle is null && content is null)
|
||||||
throw new ArgumentException("Unable to send notification that completely empty.");
|
throw new ArgumentException("Unable to send notification that completely empty.");
|
||||||
|
|
||||||
meta ??= new Dictionary<string, object>();
|
|
||||||
if (actionUri is not null) meta["action_uri"] = actionUri;
|
if (actionUri is not null) meta["action_uri"] = actionUri;
|
||||||
|
|
||||||
var accountId = Guid.Parse(account.Id!);
|
var accountId = Guid.Parse(account.Id!);
|
||||||
@@ -102,7 +101,7 @@ public class PushService(IConfiguration config, AppDatabase db, IHttpClientFacto
|
|||||||
if (!isSilent) _ = DeliveryNotification(notification);
|
if (!isSilent) _ = DeliveryNotification(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeliveryNotification(Pusher.Notification.Notification notification)
|
public async Task DeliveryNotification(Notification notification)
|
||||||
{
|
{
|
||||||
// Pushing the notification
|
// Pushing the notification
|
||||||
var subscribers = await db.PushSubscriptions
|
var subscribers = await db.PushSubscriptions
|
||||||
|
@@ -2,6 +2,7 @@ using DysonNetwork.Pusher.Connection;
|
|||||||
using DysonNetwork.Pusher.Email;
|
using DysonNetwork.Pusher.Email;
|
||||||
using DysonNetwork.Pusher.Notification;
|
using DysonNetwork.Pusher.Notification;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
|
|
||||||
@@ -10,7 +11,8 @@ namespace DysonNetwork.Pusher.Services;
|
|||||||
public class PusherServiceGrpc(
|
public class PusherServiceGrpc(
|
||||||
EmailService emailService,
|
EmailService emailService,
|
||||||
WebSocketService websocket,
|
WebSocketService websocket,
|
||||||
PushService pushService
|
PushService pushService,
|
||||||
|
AccountClientHelper accountsHelper
|
||||||
) : PusherService.PusherServiceBase
|
) : PusherService.PusherServiceBase
|
||||||
{
|
{
|
||||||
public override async Task<Empty> SendEmail(SendEmailRequest request, ServerCallContext context)
|
public override async Task<Empty> SendEmail(SendEmailRequest request, ServerCallContext context)
|
||||||
@@ -79,56 +81,10 @@ public class PusherServiceGrpc(
|
|||||||
return Task.FromResult(new Empty());
|
return Task.FromResult(new Empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<Empty> SendPushNotification(SendPushNotificationRequest request,
|
|
||||||
ServerCallContext context)
|
|
||||||
{
|
|
||||||
// This is a placeholder implementation. In a real-world scenario, you would
|
|
||||||
// need to retrieve the account from the database based on the device token.
|
|
||||||
var account = new Account();
|
|
||||||
await pushService.SendNotification(
|
|
||||||
account,
|
|
||||||
request.Notification.Topic,
|
|
||||||
request.Notification.Title,
|
|
||||||
request.Notification.Subtitle,
|
|
||||||
request.Notification.Body,
|
|
||||||
GrpcTypeHelper.ConvertFromValueMap(request.Notification.Meta),
|
|
||||||
request.Notification.ActionUri,
|
|
||||||
request.Notification.IsSilent,
|
|
||||||
request.Notification.IsSavable
|
|
||||||
);
|
|
||||||
return new Empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<Empty> SendPushNotificationToDevices(SendPushNotificationToDevicesRequest request,
|
|
||||||
ServerCallContext context)
|
|
||||||
{
|
|
||||||
// This is a placeholder implementation. In a real-world scenario, you would
|
|
||||||
// need to retrieve the accounts from the database based on the device tokens.
|
|
||||||
var account = new Account();
|
|
||||||
foreach (var deviceId in request.DeviceIds)
|
|
||||||
{
|
|
||||||
await pushService.SendNotification(
|
|
||||||
account,
|
|
||||||
request.Notification.Topic,
|
|
||||||
request.Notification.Title,
|
|
||||||
request.Notification.Subtitle,
|
|
||||||
request.Notification.Body,
|
|
||||||
GrpcTypeHelper.ConvertFromValueMap(request.Notification.Meta),
|
|
||||||
request.Notification.ActionUri,
|
|
||||||
request.Notification.IsSilent,
|
|
||||||
request.Notification.IsSavable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<Empty> SendPushNotificationToUser(SendPushNotificationToUserRequest request,
|
public override async Task<Empty> SendPushNotificationToUser(SendPushNotificationToUserRequest request,
|
||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
{
|
{
|
||||||
// This is a placeholder implementation. In a real-world scenario, you would
|
var account = await accountsHelper.GetAccount(Guid.Parse(request.UserId));
|
||||||
// need to retrieve the account from the database based on the user ID.
|
|
||||||
var account = new Account();
|
|
||||||
await pushService.SendNotification(
|
await pushService.SendNotification(
|
||||||
account,
|
account,
|
||||||
request.Notification.Topic,
|
request.Notification.Topic,
|
||||||
|
@@ -206,7 +206,8 @@ public class CacheServiceRedis : ICacheService
|
|||||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||||
PreserveReferencesHandling = PreserveReferencesHandling.None,
|
PreserveReferencesHandling = PreserveReferencesHandling.None,
|
||||||
NullValueHandling = NullValueHandling.Include,
|
NullValueHandling = NullValueHandling.Include,
|
||||||
DateParseHandling = DateParseHandling.None
|
DateParseHandling = DateParseHandling.None,
|
||||||
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configure NodaTime serializers
|
// Configure NodaTime serializers
|
||||||
|
@@ -27,12 +27,6 @@ service PusherService {
|
|||||||
// Pushes a packet to a list of devices via WebSocket.
|
// Pushes a packet to a list of devices via WebSocket.
|
||||||
rpc PushWebSocketPacketToDevices(PushWebSocketPacketToDevicesRequest) returns (google.protobuf.Empty) {}
|
rpc PushWebSocketPacketToDevices(PushWebSocketPacketToDevicesRequest) returns (google.protobuf.Empty) {}
|
||||||
|
|
||||||
// Sends a push notification to a device.
|
|
||||||
rpc SendPushNotification(SendPushNotificationRequest) returns (google.protobuf.Empty) {}
|
|
||||||
|
|
||||||
// Sends a push notification to a list of devices.
|
|
||||||
rpc SendPushNotificationToDevices(SendPushNotificationToDevicesRequest) returns (google.protobuf.Empty) {}
|
|
||||||
|
|
||||||
// Sends a push notification to a user.
|
// Sends a push notification to a user.
|
||||||
rpc SendPushNotificationToUser(SendPushNotificationToUserRequest) returns (google.protobuf.Empty) {}
|
rpc SendPushNotificationToUser(SendPushNotificationToUserRequest) returns (google.protobuf.Empty) {}
|
||||||
|
|
||||||
@@ -60,9 +54,9 @@ message SendEmailRequest {
|
|||||||
|
|
||||||
// Represents a WebSocket packet.
|
// Represents a WebSocket packet.
|
||||||
message WebSocketPacket {
|
message WebSocketPacket {
|
||||||
string type = 1;
|
string type = 1;
|
||||||
google.protobuf.Value data = 2;
|
google.protobuf.Value data = 2;
|
||||||
google.protobuf.StringValue error_message = 3;
|
google.protobuf.StringValue error_message = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PushWebSocketPacketRequest {
|
message PushWebSocketPacketRequest {
|
||||||
@@ -87,49 +81,39 @@ message PushWebSocketPacketToDevicesRequest {
|
|||||||
|
|
||||||
// Represents a push notification.
|
// Represents a push notification.
|
||||||
message PushNotification {
|
message PushNotification {
|
||||||
string topic = 1;
|
string topic = 1;
|
||||||
string title = 2;
|
string title = 2;
|
||||||
string subtitle = 3;
|
string subtitle = 3;
|
||||||
string body = 4;
|
string body = 4;
|
||||||
map<string, google.protobuf.Value> meta = 5;
|
map<string, google.protobuf.Value> meta = 5;
|
||||||
optional string action_uri = 6;
|
optional string action_uri = 6;
|
||||||
bool is_silent = 7;
|
bool is_silent = 7;
|
||||||
bool is_savable = 8;
|
bool is_savable = 8;
|
||||||
}
|
|
||||||
|
|
||||||
message SendPushNotificationRequest {
|
|
||||||
string device_id = 1;
|
|
||||||
PushNotification notification = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SendPushNotificationToDevicesRequest {
|
|
||||||
repeated string device_ids = 1;
|
|
||||||
PushNotification notification = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message SendPushNotificationToUserRequest {
|
message SendPushNotificationToUserRequest {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
PushNotification notification = 2;
|
PushNotification notification = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SendPushNotificationToUsersRequest {
|
message SendPushNotificationToUsersRequest {
|
||||||
repeated string user_ids = 1;
|
repeated string user_ids = 1;
|
||||||
PushNotification notification = 2;
|
PushNotification notification = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UnsubscribePushNotificationsRequest {
|
message UnsubscribePushNotificationsRequest {
|
||||||
string device_id = 1;
|
string device_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetWebsocketConnectionStatusRequest {
|
message GetWebsocketConnectionStatusRequest {
|
||||||
oneof id {
|
oneof id {
|
||||||
string device_id = 1;
|
string device_id = 1;
|
||||||
string user_id = 2;
|
string user_id = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetWebsocketConnectionStatusResponse {
|
message GetWebsocketConnectionStatusResponse {
|
||||||
bool is_connected = 1;
|
bool is_connected = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -100,6 +100,12 @@ public partial class ChatController(
|
|||||||
.Skip(offset)
|
.Skip(offset)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
var members = messages.Select(m => m.Sender).DistinctBy(x => x.Id).ToList();
|
||||||
|
members = await crs.LoadMemberAccounts(members);
|
||||||
|
|
||||||
|
foreach (var message in messages)
|
||||||
|
message.Sender = members.First(x => x.Id == message.SenderId);
|
||||||
|
|
||||||
Response.Headers["X-Total"] = totalCount.ToString();
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
@@ -48,9 +48,9 @@ public class ChatRoomService(
|
|||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == chatRoomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == chatRoomId)
|
||||||
.Include(m => m.ChatRoom)
|
.Include(m => m.ChatRoom)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (member == null) return member;
|
if (member == null) return member;
|
||||||
|
|
||||||
member = await LoadMemberAccount(member);
|
member = await LoadMemberAccount(member);
|
||||||
var chatRoomGroup = ChatRoomGroupPrefix + chatRoomId;
|
var chatRoomGroup = ChatRoomGroupPrefix + chatRoomId;
|
||||||
await cache.SetWithGroupsAsync(cacheKey, member,
|
await cache.SetWithGroupsAsync(cacheKey, member,
|
||||||
@@ -91,14 +91,22 @@ public class ChatRoomService(
|
|||||||
.ToList();
|
.ToList();
|
||||||
if (directRoomsId.Count == 0) return rooms;
|
if (directRoomsId.Count == 0) return rooms;
|
||||||
|
|
||||||
var directMembers = directRoomsId.Count != 0
|
List<ChatMember> members = directRoomsId.Count != 0
|
||||||
? await db.ChatMembers
|
? await db.ChatMembers
|
||||||
.Where(m => directRoomsId.Contains(m.ChatRoomId))
|
.Where(m => directRoomsId.Contains(m.ChatRoomId))
|
||||||
.Where(m => m.AccountId != userId)
|
.Where(m => m.AccountId != userId)
|
||||||
.Where(m => m.LeaveAt == null)
|
.Where(m => m.LeaveAt == null)
|
||||||
.GroupBy(m => m.ChatRoomId)
|
.ToListAsync()
|
||||||
.ToDictionaryAsync(g => g.Key, g => g.ToList())
|
: [];
|
||||||
: new Dictionary<Guid, List<ChatMember>>();
|
members = await LoadMemberAccounts(members);
|
||||||
|
|
||||||
|
Dictionary<Guid, List<ChatMember>> directMembers = new();
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (!directMembers.ContainsKey(member.ChatRoomId))
|
||||||
|
directMembers[member.ChatRoomId] = [];
|
||||||
|
directMembers[member.ChatRoomId].Add(member);
|
||||||
|
}
|
||||||
|
|
||||||
return rooms.Select(r =>
|
return rooms.Select(r =>
|
||||||
{
|
{
|
||||||
|
@@ -58,7 +58,7 @@ public class RealmController(
|
|||||||
|
|
||||||
var members = await db.RealmMembers
|
var members = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == accountId)
|
.Where(m => m.AccountId == accountId)
|
||||||
.Where(m => m.JoinedAt == null)
|
.Where(m => m.JoinedAt == null && m.LeaveAt == null)
|
||||||
.Include(e => e.Realm)
|
.Include(e => e.Realm)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user