🐛 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
{
private readonly object _lock = new();
private readonly IEtcdClient _etcdClient;
private readonly IConfiguration _configuration;
private readonly ILogger<RegistryProxyConfigProvider> _logger;
private readonly CancellationTokenSource _watchCts = new();
private CancellationTokenSource _cts = new();
private CancellationTokenSource _cts;
private IProxyConfig _config;
public RegistryProxyConfigProvider(IEtcdClient etcdClient, IConfiguration configuration,
ILogger<RegistryProxyConfigProvider> logger)
@@ -18,19 +20,33 @@ public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
_etcdClient = etcdClient;
_configuration = configuration;
_logger = logger;
_cts = new CancellationTokenSource();
_config = LoadConfig();
// Watch for changes in etcd
_etcdClient.WatchRange("/services/", _ =>
{
_logger.LogInformation("Etcd configuration changed. Reloading proxy config.");
_cts.Cancel();
_cts = new CancellationTokenSource();
ReloadConfig();
}, 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.");
var response = _etcdClient.GetRange("/services/");
var kvs = response.Kvs;
@@ -184,17 +200,15 @@ public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
_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
{
public IReadOnlyList<RouteConfig> Routes { get; } = routes;
public IReadOnlyList<ClusterConfig> Clusters { get; } = clusters;
public Microsoft.Extensions.Primitives.IChangeToken ChangeToken { get; } =
new Microsoft.Extensions.Primitives.CancellationChangeToken(CancellationToken.None);
public Microsoft.Extensions.Primitives.IChangeToken ChangeToken { get; } = changeToken;
}
public record DirectRouteConfig
@@ -211,4 +225,4 @@ public class RegistryProxyConfigProvider : IProxyConfigProvider, IDisposable
_watchCts.Cancel();
_watchCts.Dispose();
}
}
}

View File

@@ -71,7 +71,7 @@ public class PushService(IConfiguration config, AppDatabase db, IHttpClientFacto
string? title = null,
string? subtitle = null,
string? content = null,
Dictionary<string, object>? meta = null,
Dictionary<string, object?> meta = null,
string? actionUri = null,
bool isSilent = false,
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)
throw new ArgumentException("Unable to send notification that completely empty.");
meta ??= new Dictionary<string, object>();
if (actionUri is not null) meta["action_uri"] = actionUri;
var accountId = Guid.Parse(account.Id!);
@@ -102,7 +101,7 @@ public class PushService(IConfiguration config, AppDatabase db, IHttpClientFacto
if (!isSilent) _ = DeliveryNotification(notification);
}
public async Task DeliveryNotification(Pusher.Notification.Notification notification)
public async Task DeliveryNotification(Notification notification)
{
// Pushing the notification
var subscribers = await db.PushSubscriptions

View File

@@ -2,6 +2,7 @@ using DysonNetwork.Pusher.Connection;
using DysonNetwork.Pusher.Email;
using DysonNetwork.Pusher.Notification;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
@@ -10,7 +11,8 @@ namespace DysonNetwork.Pusher.Services;
public class PusherServiceGrpc(
EmailService emailService,
WebSocketService websocket,
PushService pushService
PushService pushService,
AccountClientHelper accountsHelper
) : PusherService.PusherServiceBase
{
public override async Task<Empty> SendEmail(SendEmailRequest request, ServerCallContext context)
@@ -79,56 +81,10 @@ public class PusherServiceGrpc(
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,
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 user ID.
var account = new Account();
var account = await accountsHelper.GetAccount(Guid.Parse(request.UserId));
await pushService.SendNotification(
account,
request.Notification.Topic,

View File

@@ -206,7 +206,8 @@ public class CacheServiceRedis : ICacheService
ContractResolver = new CamelCasePropertyNamesContractResolver(),
PreserveReferencesHandling = PreserveReferencesHandling.None,
NullValueHandling = NullValueHandling.Include,
DateParseHandling = DateParseHandling.None
DateParseHandling = DateParseHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
// Configure NodaTime serializers

View File

@@ -27,12 +27,6 @@ service PusherService {
// Pushes a packet to a list of devices via WebSocket.
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.
rpc SendPushNotificationToUser(SendPushNotificationToUserRequest) returns (google.protobuf.Empty) {}
@@ -60,9 +54,9 @@ message SendEmailRequest {
// Represents a WebSocket packet.
message WebSocketPacket {
string type = 1;
google.protobuf.Value data = 2;
google.protobuf.StringValue error_message = 3;
string type = 1;
google.protobuf.Value data = 2;
google.protobuf.StringValue error_message = 3;
}
message PushWebSocketPacketRequest {
@@ -87,49 +81,39 @@ message PushWebSocketPacketToDevicesRequest {
// Represents a push notification.
message PushNotification {
string topic = 1;
string title = 2;
string subtitle = 3;
string body = 4;
map<string, google.protobuf.Value> meta = 5;
optional string action_uri = 6;
bool is_silent = 7;
bool is_savable = 8;
}
message SendPushNotificationRequest {
string device_id = 1;
PushNotification notification = 2;
}
message SendPushNotificationToDevicesRequest {
repeated string device_ids = 1;
PushNotification notification = 2;
string topic = 1;
string title = 2;
string subtitle = 3;
string body = 4;
map<string, google.protobuf.Value> meta = 5;
optional string action_uri = 6;
bool is_silent = 7;
bool is_savable = 8;
}
message SendPushNotificationToUserRequest {
string user_id = 1;
PushNotification notification = 2;
string user_id = 1;
PushNotification notification = 2;
}
message SendPushNotificationToUsersRequest {
repeated string user_ids = 1;
PushNotification notification = 2;
repeated string user_ids = 1;
PushNotification notification = 2;
}
message UnsubscribePushNotificationsRequest {
string device_id = 1;
string device_id = 1;
}
message GetWebsocketConnectionStatusRequest {
oneof id {
string device_id = 1;
string user_id = 2;
}
oneof id {
string device_id = 1;
string user_id = 2;
}
}
message GetWebsocketConnectionStatusResponse {
bool is_connected = 1;
bool is_connected = 1;
}

View File

@@ -100,6 +100,12 @@ public partial class ChatController(
.Skip(offset)
.Take(take)
.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();

View File

@@ -48,9 +48,9 @@ public class ChatRoomService(
.Where(m => m.AccountId == accountId && m.ChatRoomId == chatRoomId)
.Include(m => m.ChatRoom)
.FirstOrDefaultAsync();
if (member == null) return member;
member = await LoadMemberAccount(member);
var chatRoomGroup = ChatRoomGroupPrefix + chatRoomId;
await cache.SetWithGroupsAsync(cacheKey, member,
@@ -91,14 +91,22 @@ public class ChatRoomService(
.ToList();
if (directRoomsId.Count == 0) return rooms;
var directMembers = directRoomsId.Count != 0
List<ChatMember> members = directRoomsId.Count != 0
? await db.ChatMembers
.Where(m => directRoomsId.Contains(m.ChatRoomId))
.Where(m => m.AccountId != userId)
.Where(m => m.LeaveAt == null)
.GroupBy(m => m.ChatRoomId)
.ToDictionaryAsync(g => g.Key, g => g.ToList())
: new Dictionary<Guid, List<ChatMember>>();
.ToListAsync()
: [];
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 =>
{

View File

@@ -58,7 +58,7 @@ public class RealmController(
var members = await db.RealmMembers
.Where(m => m.AccountId == accountId)
.Where(m => m.JoinedAt == null)
.Where(m => m.JoinedAt == null && m.LeaveAt == null)
.Include(e => e.Realm)
.ToListAsync();