🐛 Dozens of fixes

This commit is contained in:
2025-07-20 01:00:41 +08:00
parent 3380c8f688
commit 7b9150bd88
8 changed files with 75 additions and 107 deletions

View File

@@ -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();
} }
} }

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -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();

View File

@@ -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 =>
{ {

View File

@@ -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();