diff --git a/DysonNetwork.Pass/Account/AccountEventService.cs b/DysonNetwork.Pass/Account/AccountEventService.cs index d6f4e63..db89ee3 100644 --- a/DysonNetwork.Pass/Account/AccountEventService.cs +++ b/DysonNetwork.Pass/Account/AccountEventService.cs @@ -84,16 +84,16 @@ public class AccountEventService( foreach (var userId in userIds) { var cacheKey = $"{StatusCacheKey}{userId}"; - // var cachedStatus = await cache.GetAsync(cacheKey); - // if (cachedStatus != null) - // { - // cachedStatus.IsOnline = !cachedStatus.IsInvisible && ws.GetAccountIsConnected(userId); - // results[userId] = cachedStatus; - // } - // else - // { - cacheMissUserIds.Add(userId); - // } + var cachedStatus = await cache.GetAsync(cacheKey); + if (cachedStatus != null) + { + cachedStatus.IsOnline = !cachedStatus.IsInvisible /*&& ws.GetAccountIsConnected(userId)*/; + results[userId] = cachedStatus; + } + else + { + cacheMissUserIds.Add(userId); + } } if (cacheMissUserIds.Any()) @@ -115,7 +115,7 @@ public class AccountEventService( status.IsOnline = !status.IsInvisible && isOnline; results[status.AccountId] = status; var cacheKey = $"{StatusCacheKey}{status.AccountId}"; - // await cache.SetAsync(cacheKey, status, TimeSpan.FromMinutes(5)); + await cache.SetAsync(cacheKey, status, TimeSpan.FromMinutes(5)); foundUserIds.Add(status.AccountId); } @@ -170,12 +170,12 @@ public class AccountEventService( public async Task CheckInDailyDoAskCaptcha(Shared.Models.Account user) { var cacheKey = $"{CaptchaCacheKey}{user.Id}"; - // var needsCaptcha = await cache.GetAsync(cacheKey); - // if (needsCaptcha is not null) - // return needsCaptcha!.Value; + var needsCaptcha = await cache.GetAsync(cacheKey); + if (needsCaptcha is not null) + return needsCaptcha!.Value; var result = Random.Next(100) < CaptchaProbabilityPercent; - // await cache.SetAsync(cacheKey, result, TimeSpan.FromHours(24)); + await cache.SetAsync(cacheKey, result, TimeSpan.FromHours(24)); return result; } diff --git a/DysonNetwork.Pass/Account/AccountService.cs b/DysonNetwork.Pass/Account/AccountService.cs index 4bc367d..8fc055b 100644 --- a/DysonNetwork.Pass/Account/AccountService.cs +++ b/DysonNetwork.Pass/Account/AccountService.cs @@ -184,6 +184,7 @@ public class AccountService( public async Task RequestAccountDeletion(Shared.Models.Account account) { + await Task.CompletedTask; // var spell = await spells.CreateMagicSpell( // account, // MagicSpellType.AccountRemoval, @@ -196,6 +197,7 @@ public class AccountService( public async Task RequestPasswordReset(Shared.Models.Account account) { + await Task.CompletedTask; // var spell = await spells.CreateMagicSpell( // account, // MagicSpellType.AuthPasswordReset, @@ -520,6 +522,7 @@ public class AccountService( public async Task VerifyContactMethod(Shared.Models.Account account, AccountContact contact) { + await Task.CompletedTask; // var spell = await spells.CreateMagicSpell( // account, // MagicSpellType.ContactVerification, diff --git a/DysonNetwork.Pass/Account/NotificationService.cs b/DysonNetwork.Pass/Account/NotificationService.cs index 8ef7f6c..5b6d83c 100644 --- a/DysonNetwork.Pass/Account/NotificationService.cs +++ b/DysonNetwork.Pass/Account/NotificationService.cs @@ -66,8 +66,8 @@ public class NotificationService( AccountId = account.Id, }; - // db.NotificationPushSubscriptions.Add(subscription); - // await db.SaveChangesAsync(); + db.NotificationPushSubscriptions.Add(subscription); + await db.SaveChangesAsync(); return subscription; } @@ -107,7 +107,7 @@ public class NotificationService( } if (!isSilent) - Console.WriteLine("Simulating notification delivery."); // _ = DeliveryNotification(notification); + _ = DeliveryNotification(notification); return notification; } @@ -134,10 +134,10 @@ public class NotificationService( var id = notifications.Where(n => n.ViewedAt == null).Select(n => n.Id).ToList(); if (id.Count == 0) return; - // await db.Notifications - // .Where(n => id.Contains(n.Id)) - // .ExecuteUpdateAsync(s => s.SetProperty(n => n.ViewedAt, now) - // ); + await db.Notifications + .Where(n => id.Contains(n.Id)) + .ExecuteUpdateAsync(s => s.SetProperty(n => n.ViewedAt, now) + ); } public async Task BroadcastNotification(Notification notification, bool save = false) @@ -161,7 +161,7 @@ public class NotificationService( }; return newNotification; }).ToList(); - // await db.BulkInsertAsync(notifications); + await db.BulkInsertAsync(notifications); } foreach (var account in accounts) diff --git a/DysonNetwork.Pass/Auth/Auth.cs b/DysonNetwork.Pass/Auth/Auth.cs index 5eb90d1..5d0a603 100644 --- a/DysonNetwork.Pass/Auth/Auth.cs +++ b/DysonNetwork.Pass/Auth/Auth.cs @@ -189,8 +189,6 @@ public class DysonTokenAuthHandler( { return false; } - - break; default: return false; } diff --git a/DysonNetwork.Pass/Auth/AuthService.cs b/DysonNetwork.Pass/Auth/AuthService.cs index 9dab8eb..d0db306 100644 --- a/DysonNetwork.Pass/Auth/AuthService.cs +++ b/DysonNetwork.Pass/Auth/AuthService.cs @@ -105,6 +105,7 @@ public class AuthService( public async Task ValidateCaptcha(string token) { + await Task.CompletedTask; if (string.IsNullOrWhiteSpace(token)) return false; // var provider = config.GetSection("Captcha")["Provider"]?.ToLower(); diff --git a/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs b/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs index 073ebc8..d5c7a90 100644 --- a/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs +++ b/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs @@ -21,8 +21,7 @@ public class OidcProviderController( AppDatabase db, OidcProviderService oidcService, IConfiguration configuration, - IOptions options, - ILogger logger + IOptions options ) : ControllerBase { diff --git a/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs b/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs index b8c7b7c..7eac0e7 100644 --- a/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs +++ b/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs @@ -27,6 +27,7 @@ public class OidcProviderService( public async Task FindClientByIdAsync(Guid clientId) { + await Task.CompletedTask; return null; // return await db.CustomApps // .Include(c => c.Secrets) @@ -35,6 +36,7 @@ public class OidcProviderService( public async Task FindClientByAppIdAsync(Guid appId) { + await Task.CompletedTask; return null; // return await db.CustomApps // .Include(c => c.Secrets) diff --git a/DysonNetwork.Shared/DysonNetwork.Shared.csproj b/DysonNetwork.Shared/DysonNetwork.Shared.csproj index 114aaa0..0ffbe5c 100644 --- a/DysonNetwork.Shared/DysonNetwork.Shared.csproj +++ b/DysonNetwork.Shared/DysonNetwork.Shared.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -12,7 +12,7 @@ - + @@ -33,4 +33,6 @@ + + diff --git a/DysonNetwork.Shared/Etcd/EtcdService.cs b/DysonNetwork.Shared/Etcd/EtcdService.cs new file mode 100644 index 0000000..c2211c0 --- /dev/null +++ b/DysonNetwork.Shared/Etcd/EtcdService.cs @@ -0,0 +1,70 @@ +using dotnet_etcd; +using Etcdserverpb; +using Grpc.Core; + +namespace DysonNetwork.Shared.Etcd; + +public class EtcdService(string connectionString) : IEtcdService +{ + private readonly EtcdClient _etcdClient = new(connectionString); + private long _leaseId; + private string? _serviceKey; + private readonly CancellationTokenSource _cts = new(); + + public async Task RegisterServiceAsync(string serviceName, string serviceAddress, int ttl = 15) + { + _serviceKey = $"/services/{serviceName}/{Guid.NewGuid()}"; + var leaseGrantResponse = await _etcdClient.LeaseGrantAsync(new LeaseGrantRequest { TTL = ttl }); + _leaseId = leaseGrantResponse.ID; + + await _etcdClient.PutAsync(new PutRequest + { + Key = Google.Protobuf.ByteString.CopyFromUtf8(_serviceKey), + Value = Google.Protobuf.ByteString.CopyFromUtf8(serviceAddress), + Lease = _leaseId + }); + + _ = Task.Run(async () => + { + while (!_cts.Token.IsCancellationRequested) + { + try + { + await _etcdClient.LeaseKeepAlive(new LeaseKeepAliveRequest { ID = _leaseId }, + _ => { }, _cts.Token); + await Task.Delay(TimeSpan.FromSeconds(ttl / 3), _cts.Token); + } + catch (RpcException) + { + // Ignored + } + } + }, _cts.Token); + } + + public async Task UnregisterServiceAsync() + { + if (!string.IsNullOrEmpty(_serviceKey)) + { + await _etcdClient.DeleteRangeAsync(_serviceKey); + } + } + + public async Task> DiscoverServicesAsync(string serviceName) + { + var prefix = $"/services/{serviceName}/"; + var rangeResponse = await _etcdClient.GetRangeAsync(prefix); + return rangeResponse.Kvs.Select(kv => kv.Value.ToStringUtf8()).ToList(); + } + + public void Dispose() + { + _cts.Cancel(); + if (_leaseId != 0) + { + _etcdClient.LeaseRevoke(new LeaseRevokeRequest { ID = _leaseId }); + } + + _etcdClient.Dispose(); + } +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Etcd/EtcdServiceExtensions.cs b/DysonNetwork.Shared/Etcd/EtcdServiceExtensions.cs new file mode 100644 index 0000000..a787525 --- /dev/null +++ b/DysonNetwork.Shared/Etcd/EtcdServiceExtensions.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Grpc.Net.Client; +using MagicOnion.Client; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace DysonNetwork.Shared.Etcd +{ + public static class EtcdServiceExtensions + { + public static IServiceCollection AddEtcdService(this IServiceCollection services, IConfiguration configuration) + { + var etcdConnectionString = configuration.GetConnectionString("Etcd"); + services.AddSingleton(new EtcdService(etcdConnectionString!)); + return services; + } + + public static IServiceCollection AddMagicOnionService(this IServiceCollection services) + where TService : class, MagicOnion.IService + { + services.AddSingleton(serviceProvider => + { + var etcdService = serviceProvider.GetRequiredService(); + var serviceName = typeof(TService).Name.TrimStart('I'); // Convention: IMyService -> MyService + + // Synchronously wait for service discovery (or handle asynchronously if preferred) + var endpoints = etcdService.DiscoverServicesAsync(serviceName).GetAwaiter().GetResult(); + + if (!endpoints.Any()) + { + throw new InvalidOperationException($"No endpoints found for MagicOnion service: {serviceName}"); + } + + // For simplicity, use the first discovered endpoint + var endpoint = endpoints.First(); + + var channel = GrpcChannel.ForAddress(endpoint); + return MagicOnionClient.Create(channel); + }); + + return services; + } + } +} diff --git a/DysonNetwork.Shared/Etcd/IEtcdService.cs b/DysonNetwork.Shared/Etcd/IEtcdService.cs new file mode 100644 index 0000000..bd06b97 --- /dev/null +++ b/DysonNetwork.Shared/Etcd/IEtcdService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace DysonNetwork.Shared.Etcd +{ + public interface IEtcdService : IDisposable + { + Task RegisterServiceAsync(string serviceName, string serviceAddress, int ttl = 15); + Task UnregisterServiceAsync(); + Task> DiscoverServicesAsync(string serviceName); + } +}