From 91a1f7aa610a3e525d127f060df9de54b321c7a6 Mon Sep 17 00:00:00 2001 From: masiton Date: Sat, 5 Jul 2025 08:48:14 +0200 Subject: [PATCH] Broker implementation updated. --- code/MessengerBroker.sln | 62 +++- code/MessengerBroker/Dockerfile | 29 -- .../Extensions/EntityExtensions.cs | 22 -- .../Factories/BrokerDbContextFactory.cs | 27 ++ code/MessengerBroker/GlobalUsings.cs | 2 + code/MessengerBroker/Handlers/AuthHandler.cs | 28 -- .../CustomBearerAuthenticationHandler.cs | 78 +++++ code/MessengerBroker/Handlers/DataHandler.cs | 81 ----- .../Endpoint/MessagesEndpointHandler.cs | 49 +++ .../Handlers/Endpoint/UsersEndpointHandler.cs | 65 ++++ .../MessengerBroker/Handlers/MasterHandler.cs | 328 +++++++++--------- code/MessengerBroker/MessengerBroker.csproj | 4 +- code/MessengerBroker/MessengerBroker.http | 6 - code/MessengerBroker/Model/Http/Sync.cs | 42 --- code/MessengerBroker/Models/CachedIdentity.cs | 12 + code/MessengerBroker/Models/Http/Messages.cs | 17 + .../{Model => Models}/Http/Users.cs | 4 - .../MessengerBroker/Models/Scoped/Identity.cs | 9 + code/MessengerBroker/Program.cs | 179 ++++++---- code/MessengerBroker/Settings.cs | 36 -- .../appsettings.Development.json | 8 - code/MessengerBroker/appsettings.json | 9 - 22 files changed, 587 insertions(+), 510 deletions(-) delete mode 100644 code/MessengerBroker/Dockerfile delete mode 100644 code/MessengerBroker/Extensions/EntityExtensions.cs create mode 100644 code/MessengerBroker/Factories/BrokerDbContextFactory.cs create mode 100644 code/MessengerBroker/GlobalUsings.cs delete mode 100644 code/MessengerBroker/Handlers/AuthHandler.cs create mode 100644 code/MessengerBroker/Handlers/CustomBearerAuthenticationHandler.cs delete mode 100644 code/MessengerBroker/Handlers/DataHandler.cs create mode 100644 code/MessengerBroker/Handlers/Endpoint/MessagesEndpointHandler.cs create mode 100644 code/MessengerBroker/Handlers/Endpoint/UsersEndpointHandler.cs delete mode 100644 code/MessengerBroker/MessengerBroker.http delete mode 100644 code/MessengerBroker/Model/Http/Sync.cs create mode 100644 code/MessengerBroker/Models/CachedIdentity.cs create mode 100644 code/MessengerBroker/Models/Http/Messages.cs rename code/MessengerBroker/{Model => Models}/Http/Users.cs (86%) create mode 100644 code/MessengerBroker/Models/Scoped/Identity.cs delete mode 100644 code/MessengerBroker/Settings.cs delete mode 100644 code/MessengerBroker/appsettings.Development.json delete mode 100644 code/MessengerBroker/appsettings.json diff --git a/code/MessengerBroker.sln b/code/MessengerBroker.sln index 7fdbe7e..0db3991 100644 --- a/code/MessengerBroker.sln +++ b/code/MessengerBroker.sln @@ -9,9 +9,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerBroker.Db", "Messe EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Db", "Db", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MessengerApi", "MessengerApi", "{D520DC2F-BD81-4588-8818-D493553ACD3D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Db.Contracts", "..\subm\messengerapi\code\MessengerApi.Db.Contracts\MessengerApi.Db.Contracts.csproj", "{B75EB44A-7B25-1E85-8E93-73C1A83F91B5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Db", "..\sub\messengerapi\code\MessengerApi.Db\MessengerApi.Db.csproj", "{2F366BD9-AA41-7C4B-C6FA-1C5CCDF56E34}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Db", "..\subm\messengerapi\code\MessengerApi.Db\MessengerApi.Db.csproj", "{9DFB9A27-503F-0FE5-9414-18A85C9F4BF4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerBroker.Configuration", "MessengerBroker.Configuration\MessengerBroker.Configuration.csproj", "{B4964B49-DB24-4A91-BD4E-2EE8009CF5B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Configuration", "..\subm\messengerapi\code\MessengerApi.Configuration\MessengerApi.Configuration.csproj", "{2138EEC0-ADB6-4C2A-3F81-B53B3884EFBB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{929526E6-6BE1-4769-B55F-85284F774523}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerBroker.Db.Sql", "MessengerBroker.Db.Sql\MessengerBroker.Db.Sql.csproj", "{FCE9214C-CDCB-4D79-B7AB-2BCBAD41AC35}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi", "..\subm\messengerapi\code\MessengerApi\MessengerApi.csproj", "{36AEE097-901C-171C-BA84-C231C2EC600F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Db.Sql", "..\subm\messengerapi\code\MessengerApi.Db.Sql\MessengerApi.Db.Sql.csproj", "{5F0E1A67-056B-E6B9-1940-5F31587C05CE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessengerApi.Db.Npg", "..\subm\messengerapi\code\MessengerApi.Db.Npg\MessengerApi.Db.Npg.csproj", "{2A08099B-1A1E-FE40-9220-43F26AF852EC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,17 +41,51 @@ Global {52CF80F3-A938-437B-B9DD-5E64A206A641}.Debug|Any CPU.Build.0 = Debug|Any CPU {52CF80F3-A938-437B-B9DD-5E64A206A641}.Release|Any CPU.ActiveCfg = Release|Any CPU {52CF80F3-A938-437B-B9DD-5E64A206A641}.Release|Any CPU.Build.0 = Release|Any CPU - {2F366BD9-AA41-7C4B-C6FA-1C5CCDF56E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F366BD9-AA41-7C4B-C6FA-1C5CCDF56E34}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F366BD9-AA41-7C4B-C6FA-1C5CCDF56E34}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F366BD9-AA41-7C4B-C6FA-1C5CCDF56E34}.Release|Any CPU.Build.0 = Release|Any CPU + {B75EB44A-7B25-1E85-8E93-73C1A83F91B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B75EB44A-7B25-1E85-8E93-73C1A83F91B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B75EB44A-7B25-1E85-8E93-73C1A83F91B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B75EB44A-7B25-1E85-8E93-73C1A83F91B5}.Release|Any CPU.Build.0 = Release|Any CPU + {9DFB9A27-503F-0FE5-9414-18A85C9F4BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DFB9A27-503F-0FE5-9414-18A85C9F4BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DFB9A27-503F-0FE5-9414-18A85C9F4BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DFB9A27-503F-0FE5-9414-18A85C9F4BF4}.Release|Any CPU.Build.0 = Release|Any CPU + {B4964B49-DB24-4A91-BD4E-2EE8009CF5B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4964B49-DB24-4A91-BD4E-2EE8009CF5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4964B49-DB24-4A91-BD4E-2EE8009CF5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4964B49-DB24-4A91-BD4E-2EE8009CF5B0}.Release|Any CPU.Build.0 = Release|Any CPU + {2138EEC0-ADB6-4C2A-3F81-B53B3884EFBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2138EEC0-ADB6-4C2A-3F81-B53B3884EFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2138EEC0-ADB6-4C2A-3F81-B53B3884EFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2138EEC0-ADB6-4C2A-3F81-B53B3884EFBB}.Release|Any CPU.Build.0 = Release|Any CPU + {FCE9214C-CDCB-4D79-B7AB-2BCBAD41AC35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCE9214C-CDCB-4D79-B7AB-2BCBAD41AC35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCE9214C-CDCB-4D79-B7AB-2BCBAD41AC35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCE9214C-CDCB-4D79-B7AB-2BCBAD41AC35}.Release|Any CPU.Build.0 = Release|Any CPU + {36AEE097-901C-171C-BA84-C231C2EC600F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36AEE097-901C-171C-BA84-C231C2EC600F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36AEE097-901C-171C-BA84-C231C2EC600F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36AEE097-901C-171C-BA84-C231C2EC600F}.Release|Any CPU.Build.0 = Release|Any CPU + {5F0E1A67-056B-E6B9-1940-5F31587C05CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F0E1A67-056B-E6B9-1940-5F31587C05CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F0E1A67-056B-E6B9-1940-5F31587C05CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F0E1A67-056B-E6B9-1940-5F31587C05CE}.Release|Any CPU.Build.0 = Release|Any CPU + {2A08099B-1A1E-FE40-9220-43F26AF852EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A08099B-1A1E-FE40-9220-43F26AF852EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A08099B-1A1E-FE40-9220-43F26AF852EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A08099B-1A1E-FE40-9220-43F26AF852EC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {52CF80F3-A938-437B-B9DD-5E64A206A641} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {2F366BD9-AA41-7C4B-C6FA-1C5CCDF56E34} = {D520DC2F-BD81-4588-8818-D493553ACD3D} + {B75EB44A-7B25-1E85-8E93-73C1A83F91B5} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {9DFB9A27-503F-0FE5-9414-18A85C9F4BF4} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {B4964B49-DB24-4A91-BD4E-2EE8009CF5B0} = {929526E6-6BE1-4769-B55F-85284F774523} + {2138EEC0-ADB6-4C2A-3F81-B53B3884EFBB} = {929526E6-6BE1-4769-B55F-85284F774523} + {FCE9214C-CDCB-4D79-B7AB-2BCBAD41AC35} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {5F0E1A67-056B-E6B9-1940-5F31587C05CE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {2A08099B-1A1E-FE40-9220-43F26AF852EC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F0F93DDE-2CDC-4A7D-9D70-A7A12B3AF9CE} diff --git a/code/MessengerBroker/Dockerfile b/code/MessengerBroker/Dockerfile deleted file mode 100644 index 3377d36..0000000 --- a/code/MessengerBroker/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. - -# This stage is used when running from VS in fast mode (Default for Debug configuration) -FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base -USER $APP_UID -WORKDIR /app -EXPOSE 8080 - - -# This stage is used to build the service project -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build -ARG BUILD_CONFIGURATION=Release -WORKDIR /src -COPY ["MessengerBroker/MessengerBroker.csproj", "MessengerBroker/"] -RUN dotnet restore "./MessengerBroker/MessengerBroker.csproj" -COPY . . -WORKDIR "/src/MessengerBroker" -RUN dotnet build "./MessengerBroker.csproj" -c $BUILD_CONFIGURATION -o /app/build - -# This stage is used to publish the service project to be copied to the final stage -FROM build AS publish -ARG BUILD_CONFIGURATION=Release -RUN dotnet publish "./MessengerBroker.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false - -# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "MessengerBroker.dll"] \ No newline at end of file diff --git a/code/MessengerBroker/Extensions/EntityExtensions.cs b/code/MessengerBroker/Extensions/EntityExtensions.cs deleted file mode 100644 index c01be5b..0000000 --- a/code/MessengerBroker/Extensions/EntityExtensions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Linq.Expressions; - -namespace MessengerBroker -{ - public static class EntityExtensions - { - public static IQueryable GetUsers(this IQueryable source, Guid[] guids) - { - return source.Where(x => guids.Any(g => g == x.Id)); - } - - public static IQueryable GetUsersExcept(this IQueryable source, IEnumerable guids) - { - return source.Where(x => !guids.Any(g => g == x.Id)); - } - - public static bool GetUserExists(this IQueryable source, Guid id) - { - return source.Any(x=> x.Id == id); - } - } -} diff --git a/code/MessengerBroker/Factories/BrokerDbContextFactory.cs b/code/MessengerBroker/Factories/BrokerDbContextFactory.cs new file mode 100644 index 0000000..fbfd187 --- /dev/null +++ b/code/MessengerBroker/Factories/BrokerDbContextFactory.cs @@ -0,0 +1,27 @@ +using MessengerApi.Configuration.Model.Persistence; +using MessengerBroker.Configuration.Model; +using MessengerBroker.Db; +using MessengerBroker.Db.Sql; + +namespace MessengerBroker.Factories +{ + public class BrokerDbContextFactory + { + private readonly BrokerConfiguration configuration; + + public BrokerDbContextFactory(BrokerConfiguration configuration) + { + this.configuration = configuration; + } + + public BrokerDbContext CreateDbContext() + { + if(this.configuration.BrokerPersistenceConfiguration.PersistenceType == MessengerApi.Configuration.Enums.PersistenceTypes.Sql) + { + return new BrokerSqlDbContext((this.configuration.BrokerPersistenceConfiguration as SqlPersistenceConfiguration).ConnectionString); + } + + throw new NotImplementedException(); + } + } +} diff --git a/code/MessengerBroker/GlobalUsings.cs b/code/MessengerBroker/GlobalUsings.cs new file mode 100644 index 0000000..0489b53 --- /dev/null +++ b/code/MessengerBroker/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using ILogger = portaloggy.ILogger; +global using portaloggy; \ No newline at end of file diff --git a/code/MessengerBroker/Handlers/AuthHandler.cs b/code/MessengerBroker/Handlers/AuthHandler.cs deleted file mode 100644 index 86777a4..0000000 --- a/code/MessengerBroker/Handlers/AuthHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace MessengerBroker.Handlers -{ - public class AuthHandler - { - private readonly Settings settings; - - public AuthHandler(Settings settings) - { - this.settings = settings; - } - - public Guid? Auth(HttpContext context) - { - var authHeader = context.Request.Headers["Authorization"].ToString(); - - if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ")) - { - var token = authHeader.Substring("Bearer ".Length).Trim(); - if (Guid.TryParse(token, out Guid brokerId) && this.settings.Slaves.Any(x => x.BrokerId == brokerId)) - { - return brokerId; - } - } - - return null; - } - } -} \ No newline at end of file diff --git a/code/MessengerBroker/Handlers/CustomBearerAuthenticationHandler.cs b/code/MessengerBroker/Handlers/CustomBearerAuthenticationHandler.cs new file mode 100644 index 0000000..b02addb --- /dev/null +++ b/code/MessengerBroker/Handlers/CustomBearerAuthenticationHandler.cs @@ -0,0 +1,78 @@ +using MessengerBroker.Configuration.Model; +using MessengerBroker.Models; +using MessengerBroker.Models.Scoped; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using System.Security.Claims; +using System.Text.Encodings.Web; + +namespace MessengerBroker.Handlers +{ + /// + /// Validates our permananet API keys sent over as Bearer tokens. + /// + public class CustomBearerAuthenticationHandler : AuthenticationHandler + { + private readonly IMemoryCache memoryCache; + private readonly BrokerConfiguration configuration; + + public CustomBearerAuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory loggerFactory, + UrlEncoder encoder, + IMemoryCache memoryCache, + BrokerConfiguration configuration) + : base(options, loggerFactory, encoder) + { + this.memoryCache = memoryCache; + this.configuration = configuration; + } + + protected override Task HandleAuthenticateAsync() + { + const string HEADER = "Authorization"; + const string PREFIX = "Bearer "; + + if (!Request.Headers.TryGetValue(HEADER, out var authHeader) || + !authHeader.ToString().StartsWith(PREFIX)) + { + return Task.FromResult(AuthenticateResult.NoResult()); + } + + var token = authHeader.ToString().Substring(PREFIX.Length).Trim(); + + if(memoryCache.TryGetValue(token, out CachedIdentity oldCache)) + { + var identity = Context.RequestServices.GetRequiredService(); + identity.Server = oldCache.Server; + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(oldCache.ClaimsPrincipal, Scheme.Name))); + } + else + { + var brokerId = Guid.Parse(token); + var server = configuration.SlaveServers.SingleOrDefault(x => x.BrokerId == brokerId); + + var principal = new ClaimsPrincipal( + new ClaimsIdentity( + new List + { + new Claim(ClaimTypes.NameIdentifier, token), + new Claim(ClaimTypes.Name, token) + }, Scheme.Name)); + + var cache = new CachedIdentity + { + ClaimsPrincipal = principal, + Server = server + }; + + memoryCache.Set(token, cache, TimeSpan.FromMinutes(5)); + + var identity = Context.RequestServices.GetRequiredService(); + identity.Server = server; + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(cache.ClaimsPrincipal, Scheme.Name))); + } + } + } +} \ No newline at end of file diff --git a/code/MessengerBroker/Handlers/DataHandler.cs b/code/MessengerBroker/Handlers/DataHandler.cs deleted file mode 100644 index f3e9a1b..0000000 --- a/code/MessengerBroker/Handlers/DataHandler.cs +++ /dev/null @@ -1,81 +0,0 @@ -using MessengerApi.Db; -using MessengerBroker.Db; -using Microsoft.EntityFrameworkCore; - -namespace MessengerBroker.Handlers -{ - public class DataHandler - { - private readonly Settings _settings; - - public Task> GetLocalUsersAndRoutes() - { - var foreignUserIds = (Guid[])null; - using(var broCtx = new BrokerDbContext(this._settings.MessengerBrokerDbConnectionString)) - { - foreignUserIds = broCtx.Users.Select(x => x.Id).ToArray(); - } - - using (var apiCtx = new MessengerDbContext(this._settings.MessengerApiDbConnectionString)) - { - var localUsers = apiCtx.Users - .Where(x => !foreignUserIds.Any(f => f == x.Id)) - .ToArray(); - var localRoutes = apiCtx.UserRoutes - .Include(x => x.From) - .Include(x => x.To) - .Where(x => localUsers.Any(l => l.Id == x.From.Id) && localUsers.Any(l => l.Id == x.To.Id)) - .ToArray(); - - return Task.FromResult(new Tuple( - localUsers, - localRoutes)); - } - } - - private Task GetForeignUsers(Guid brokerId) - { - var foreignUserIds = (Guid[])null; - using (var broCtx = new BrokerDbContext(this._settings.MessengerBrokerDbConnectionString)) - { - foreignUserIds = broCtx.Users.Where(x=>x.BrokerId == brokerId).Select(x => x.Id).ToArray(); - } - - using (var apiCtx = new MessengerDbContext(this._settings.MessengerApiDbConnectionString)) - { - var localUsers = apiCtx.Users - .Where(x => !foreignUserIds.Any(f => f == x.Id)) - .ToArray(); - - return Task.FromResult(localUsers); - } - } - - public async Task GetMessages(Guid brokerId, DateTime sinceUtc) - { - var userIds = (Guid[])null; - - if(brokerId == this._settings.BrokerId) - { - // Our messages. - var users = await this.GetLocalUsersAndRoutes(); - userIds = users.Item1.Select(x => x.Id).ToArray(); - } - else - { - var users = await this.GetForeignUsers(brokerId); - userIds = users.Select(x => x.Id).ToArray(); - } - - using (var apiCtx = new MessengerDbContext(this._settings.MessengerApiDbConnectionString)) - { - var messages = apiCtx.Messages - .Include(x => x.From).Include(x => x.To) - .Where(x => x.CreatedUtc >= sinceUtc && userIds.Contains(x.From.Id)) - .ToArray(); - - return messages; - } - } - } -} diff --git a/code/MessengerBroker/Handlers/Endpoint/MessagesEndpointHandler.cs b/code/MessengerBroker/Handlers/Endpoint/MessagesEndpointHandler.cs new file mode 100644 index 0000000..e9bd20d --- /dev/null +++ b/code/MessengerBroker/Handlers/Endpoint/MessagesEndpointHandler.cs @@ -0,0 +1,49 @@ +using MessengerApi.Factories; +using MessengerBroker.Configuration.Model; +using MessengerBroker.Factories; +using MessengerBroker.Model.Http; +using Microsoft.EntityFrameworkCore; + +namespace MessengerBroker.Handlers.Endpoint +{ + public class MessagesEndpointHandler + { + private readonly BrokerConfiguration configuration; + private readonly BrokerDbContextFactory brokerDbContextFactory; + private readonly DbContextFactory messengerDbContextFactory; + + public MessagesEndpointHandler( + BrokerConfiguration configuration, + BrokerDbContextFactory brokerDbContextFactory, + DbContextFactory messengerDbContextFactory) + { + this.configuration = configuration; + this.brokerDbContextFactory = brokerDbContextFactory; + this.messengerDbContextFactory = messengerDbContextFactory; + } + + public async Task GetMessages(Messages.MessagesRequest request) + { + using var broCtx = this.brokerDbContextFactory.CreateDbContext(); + + var brokerMessageIdCollection = broCtx.Messages + .Where(x => x.BrokerId == request.OwnerBrokerId) + .Select(x => x.Id) + .Take(1000) + .ToArray(); + + using var apiCtx = this.messengerDbContextFactory.CreateDbContext(); + + var messages = apiCtx.Messages + .Where(x => x.CreatedUtc >= request.SinceUtc && brokerMessageIdCollection.Contains(x.Id)) + .ToArray(); + + var response = new Messages.MessagesResponse + { + Messages = messages + }; + + return response; + } + } +} \ No newline at end of file diff --git a/code/MessengerBroker/Handlers/Endpoint/UsersEndpointHandler.cs b/code/MessengerBroker/Handlers/Endpoint/UsersEndpointHandler.cs new file mode 100644 index 0000000..93fede5 --- /dev/null +++ b/code/MessengerBroker/Handlers/Endpoint/UsersEndpointHandler.cs @@ -0,0 +1,65 @@ +using MessengerApi.Factories; +using MessengerBroker.Configuration.Model; +using MessengerBroker.Factories; +using MessengerBroker.Model.Http; +using Microsoft.EntityFrameworkCore; + +namespace MessengerBroker.Handlers.Endpoint +{ + public class UsersEndpointHandler + { + private readonly BrokerConfiguration configuration; + private readonly BrokerDbContextFactory brokerDbContextFactory; + private readonly DbContextFactory messengerDbContextFactory; + + public UsersEndpointHandler( + BrokerConfiguration configuration, + BrokerDbContextFactory brokerDbContextFactory, + DbContextFactory messengerDbContextFactory) + { + this.configuration = configuration; + this.brokerDbContextFactory = brokerDbContextFactory; + this.messengerDbContextFactory = messengerDbContextFactory; + } + + public Task GetUsers() + { + var foreignUserIds = (Guid[])null; + + using(var broCtx = this.brokerDbContextFactory.CreateDbContext()) + { + foreignUserIds = broCtx.Users.Select(x => x.Id).ToArray(); + } + + using (var apiCtx = this.messengerDbContextFactory.CreateDbContext()) + { + var localUsers = apiCtx.Users + .Where(x => !foreignUserIds.Any(f => f == x.Id)) + .ToArray(); + + var localRoutes = apiCtx.UserRoutes + .Include(x => x.From) + .Include(x => x.To) + .Where(x => localUsers.Any(l => l.Id == x.From.Id) && localUsers.Any(l => l.Id == x.To.Id)) + .ToArray(); + + return Task.FromResult(new Users.UsersResponse + { + Users = localUsers.Select(x => new Users.UsersResponse.User + { + Id = x.Id, + ApiKey = x.ApiKey, + IsEnabled = x.IsEnabled, + Name = x.Name + }).ToArray(), + UserRoutes = localRoutes.Select(x => new Users.UsersResponse.UserRoute + { + Id = x.Id, + FromId = x.From.Id, + ToId = x.To.Id + }).ToArray() + }); + } + } + } +} diff --git a/code/MessengerBroker/Handlers/MasterHandler.cs b/code/MessengerBroker/Handlers/MasterHandler.cs index 97dd523..c41c6e5 100644 --- a/code/MessengerBroker/Handlers/MasterHandler.cs +++ b/code/MessengerBroker/Handlers/MasterHandler.cs @@ -1,196 +1,196 @@ -using MessengerApi.Db; -using MessengerBroker.Db; -using Microsoft.EntityFrameworkCore; -using System.Text.Json; +//using MessengerApi.Db; +//using MessengerBroker.Db; +//using Microsoft.EntityFrameworkCore; +//using System.Text.Json; -namespace MessengerBroker.Handlers -{ - public class MasterHandler - { - private readonly Settings settings; +//namespace MessengerBroker.Handlers +//{ +// public class MasterHandler +// { +// private readonly Settings settings; - private readonly HttpClient httpClient = new HttpClient(); +// private readonly HttpClient httpClient = new HttpClient(); - public async Task BeginSyncingWithMaster(Settings.MasterServer masterServer, CancellationToken ct = default) - { - while (!ct.IsCancellationRequested) - { - await Task.Delay(TimeSpan.FromSeconds(1)); +// public async Task BeginSyncingWithMaster(Settings.MasterServer masterServer, CancellationToken ct = default) +// { +// while (!ct.IsCancellationRequested) +// { +// await Task.Delay(TimeSpan.FromSeconds(1)); - var usersRequest = new HttpRequestMessage(HttpMethod.Get, $"{masterServer.BrokerApiUrl}/users"); - usersRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", this.settings.BrokerId.ToString()); +// var usersRequest = new HttpRequestMessage(HttpMethod.Get, $"{masterServer.BrokerApiUrl}/users"); +// usersRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", this.settings.BrokerId.ToString()); - var usersResponseMessage = await this.httpClient.SendAsync(usersRequest, ct); +// var usersResponseMessage = await this.httpClient.SendAsync(usersRequest, ct); - if (!usersResponseMessage.IsSuccessStatusCode) - { - continue; - } +// if (!usersResponseMessage.IsSuccessStatusCode) +// { +// continue; +// } - var usersResponse = JsonSerializer.Deserialize(await usersResponseMessage.Content.ReadAsStringAsync()); +// var usersResponse = JsonSerializer.Deserialize(await usersResponseMessage.Content.ReadAsStringAsync()); - using (var broCtx = new BrokerDbContext(this.settings.MessengerBrokerDbConnectionString)) - using (var apiCtx = new MessengerDbContext(this.settings.MessengerApiDbConnectionString)) - { - var updatedUsers = usersResponse.Users.Where(x => broCtx.Users.GetUserExists(x.Id)).ToList(); - updatedUsers.ForEach(async x => await this.UpdateUser(x, apiCtx)); +// using (var broCtx = new BrokerDbContext(this.settings.MessengerBrokerDbConnectionString)) +// using (var apiCtx = new MessengerDbContext(this.settings.MessengerApiDbConnectionString)) +// { +// var updatedUsers = usersResponse.Users.Where(x => broCtx.Users.GetUserExists(x.Id)).ToList(); +// updatedUsers.ForEach(async x => await this.UpdateUser(x, apiCtx)); - var deletedUsers = broCtx.Users.GetUsersExcept(usersResponse.Users.Select(x => x.Id)).ToList(); - deletedUsers.ForEach(async x => await this.RemoveUser(x.Id, broCtx, apiCtx)); +// var deletedUsers = broCtx.Users.GetUsersExcept(usersResponse.Users.Select(x => x.Id)).ToList(); +// deletedUsers.ForEach(async x => await this.RemoveUser(x.Id, broCtx, apiCtx)); - var addedUsers = usersResponse.Users.Where(x => !broCtx.Users.GetUserExists(x.Id)).ToList(); - addedUsers.ForEach(async x => await this.AddUser(x, masterServer.BrokerId, broCtx, apiCtx)); +// var addedUsers = usersResponse.Users.Where(x => !broCtx.Users.GetUserExists(x.Id)).ToList(); +// addedUsers.ForEach(async x => await this.AddUser(x, masterServer.BrokerId, broCtx, apiCtx)); - foreach (var route in usersResponse.UserRoutes - .Where(r => apiCtx.UserRoutes - .Include(x => x.From) - .Include(x => x.To) - .Any(apir => apir.Id == r.Id && (apir.From.Id != r.FromId || apir.To.Id != r.ToId)))) - { - var existing = apiCtx.UserRoutes.Include(x => x.From).Include(x => x.To).Single(x => x.Id == route.Id); - existing.From = apiCtx.Users.Single(x => x.Id == route.FromId); - existing.To = apiCtx.Users.Single(x => x.Id == route.ToId); - } +// foreach (var route in usersResponse.UserRoutes +// .Where(r => apiCtx.UserRoutes +// .Include(x => x.From) +// .Include(x => x.To) +// .Any(apir => apir.Id == r.Id && (apir.From.Id != r.FromId || apir.To.Id != r.ToId)))) +// { +// var existing = apiCtx.UserRoutes.Include(x => x.From).Include(x => x.To).Single(x => x.Id == route.Id); +// existing.From = apiCtx.Users.Single(x => x.Id == route.FromId); +// existing.To = apiCtx.Users.Single(x => x.Id == route.ToId); +// } - foreach (var deletedRoute in apiCtx.UserRoutes - .Where(x => !usersResponse.UserRoutes.Any(r => r.Id == x.Id))) - { - apiCtx.UserRoutes.Remove(deletedRoute); - } +// foreach (var deletedRoute in apiCtx.UserRoutes +// .Where(x => !usersResponse.UserRoutes.Any(r => r.Id == x.Id))) +// { +// apiCtx.UserRoutes.Remove(deletedRoute); +// } - foreach (var addedRoute in usersResponse.UserRoutes - .Where(r => !apiCtx.UserRoutes.Any(x => x.Id == r.Id))) - { - apiCtx.UserRoutes.Add(new MessengerApi.Db.Entities.UserRoute - { - Id = addedRoute.Id, - From = apiCtx.Users.Single(x => x.Id == addedRoute.FromId), - To = apiCtx.Users.Single(x => x.Id == addedRoute.ToId) - }); - } +// foreach (var addedRoute in usersResponse.UserRoutes +// .Where(r => !apiCtx.UserRoutes.Any(x => x.Id == r.Id))) +// { +// apiCtx.UserRoutes.Add(new MessengerApi.Db.Entities.UserRoute +// { +// Id = addedRoute.Id, +// From = apiCtx.Users.Single(x => x.Id == addedRoute.FromId), +// To = apiCtx.Users.Single(x => x.Id == addedRoute.ToId) +// }); +// } - broCtx.SaveChanges(); - apiCtx.SaveChanges(); - } +// broCtx.SaveChanges(); +// apiCtx.SaveChanges(); +// } - var messagesRequest = new HttpRequestMessage(HttpMethod.Get, $"{masterServer.BrokerApiUrl}/messages"); - messagesRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", this.settings.BrokerId.ToString()); +// var messagesRequest = new HttpRequestMessage(HttpMethod.Get, $"{masterServer.BrokerApiUrl}/messages"); +// messagesRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", this.settings.BrokerId.ToString()); - var messagesResponseMessage = await this.httpClient.SendAsync(messagesRequest, ct); +// var messagesResponseMessage = await this.httpClient.SendAsync(messagesRequest, ct); - if (!messagesResponseMessage.IsSuccessStatusCode) - { - continue; - } +// if (!messagesResponseMessage.IsSuccessStatusCode) +// { +// continue; +// } - var messagesResponse = JsonSerializer.Deserialize(await messagesResponseMessage.Content.ReadAsStringAsync()); +// var messagesResponse = JsonSerializer.Deserialize(await messagesResponseMessage.Content.ReadAsStringAsync()); - using (var broCtx = new BrokerDbContext(this.settings.MessengerBrokerDbConnectionString)) - using (var apiCtx = new MessengerDbContext(this.settings.MessengerApiDbConnectionString)) - { - var addedMessages = messagesResponse.Messages - .Where(m => broCtx.Messages.Any(x => x.BrokerId == masterServer.BrokerId && x.Id == m.Id) == false) - .ToList(); +// using (var broCtx = new BrokerDbContext(this.settings.MessengerBrokerDbConnectionString)) +// using (var apiCtx = new MessengerDbContext(this.settings.MessengerApiDbConnectionString)) +// { +// var addedMessages = messagesResponse.Messages +// .Where(m => broCtx.Messages.Any(x => x.BrokerId == masterServer.BrokerId && x.Id == m.Id) == false) +// .ToList(); - addedMessages.ForEach(x => - { - broCtx.Messages.Add(new Db.Model.Message - { - BrokerId = masterServer.BrokerId, - Id = x.Id - }); +// addedMessages.ForEach(x => +// { +// broCtx.Messages.Add(new Db.Model.Message +// { +// BrokerId = masterServer.BrokerId, +// Id = x.Id +// }); - apiCtx.Messages.Add(new MessengerApi.Db.Entities.Message - { - Id = x.Id, - CreatedUtc = x.CreatedUtc, - From = x.From != null ? (apiCtx.Users.SingleOrDefault(u => u.Id == x.From.Value)) ?? null : null, - To = x.To != null ? (apiCtx.Users.SingleOrDefault(u => u.Id == x.To.Value)) ?? null : null, - IsAcknowledged = x.IsAcknowledged, - IsDelivered = x.IsDelivered, - Payload = x.Payload, - PayloadId = x.PayloadId, - PayloadLifespanInSeconds = x.PayloadLifespanInSeconds, - PayloadTimestamp = x.PayloadTimestamp, - PayloadType = x.PayloadType, - }); - }); +// apiCtx.Messages.Add(new MessengerApi.Db.Entities.Message +// { +// Id = x.Id, +// CreatedUtc = x.CreatedUtc, +// From = x.From != null ? (apiCtx.Users.SingleOrDefault(u => u.Id == x.From.Value)) ?? null : null, +// To = x.To != null ? (apiCtx.Users.SingleOrDefault(u => u.Id == x.To.Value)) ?? null : null, +// IsAcknowledged = x.IsAcknowledged, +// IsDelivered = x.IsDelivered, +// Payload = x.Payload, +// PayloadId = x.PayloadId, +// PayloadLifespanInSeconds = x.PayloadLifespanInSeconds, +// PayloadTimestamp = x.PayloadTimestamp, +// PayloadType = x.PayloadType, +// }); +// }); - var existingMessages = messagesResponse.Messages - .Except(addedMessages) - .ToList(); +// var existingMessages = messagesResponse.Messages +// .Except(addedMessages) +// .ToList(); - existingMessages.ForEach(x => - { - var existing = apiCtx.Messages.SingleOrDefault(a => a.Id == x.Id); +// existingMessages.ForEach(x => +// { +// var existing = apiCtx.Messages.SingleOrDefault(a => a.Id == x.Id); - if(existing != null && (existing.IsDelivered != x.IsDelivered || existing.IsAcknowledged != x.IsAcknowledged)) - { - existing.IsDelivered = x.IsDelivered; - existing.IsAcknowledged = x.IsAcknowledged; - } - }); +// if(existing != null && (existing.IsDelivered != x.IsDelivered || existing.IsAcknowledged != x.IsAcknowledged)) +// { +// existing.IsDelivered = x.IsDelivered; +// existing.IsAcknowledged = x.IsAcknowledged; +// } +// }); - broCtx.SaveChanges(); - apiCtx.SaveChanges(); - } - } - } +// broCtx.SaveChanges(); +// apiCtx.SaveChanges(); +// } +// } +// } - private async Task RemoveUser(Guid id, BrokerDbContext broCtx, MessengerDbContext apiCtx) - { - var broUser = await broCtx.Users.SingleOrDefaultAsync(x => x.Id == id); +// private async Task RemoveUser(Guid id, BrokerDbContext broCtx, MessengerDbContext apiCtx) +// { +// var broUser = await broCtx.Users.SingleOrDefaultAsync(x => x.Id == id); - if (broUser != null) - { - broCtx.Users.Remove(broUser); - } +// if (broUser != null) +// { +// broCtx.Users.Remove(broUser); +// } - var apiUser = await apiCtx.Users.SingleOrDefaultAsync(x => x.Id == id); +// var apiUser = await apiCtx.Users.SingleOrDefaultAsync(x => x.Id == id); - if (apiUser != null) - { - apiCtx.Users.Remove(apiUser); - } - } +// if (apiUser != null) +// { +// apiCtx.Users.Remove(apiUser); +// } +// } - private async Task AddUser(Model.Http.Users.UsersResponse.User user, Guid brokerId, BrokerDbContext broCtx, MessengerDbContext apiCtx) - { - if (broCtx.Users.GetUserExists(user.Id) == false) - { - broCtx.Users.Add(new Db.Model.User - { - Id = user.Id, - BrokerId = brokerId - }); - } +// private async Task AddUser(Model.Http.Users.UsersResponse.User user, Guid brokerId, BrokerDbContext broCtx, MessengerDbContext apiCtx) +// { +// if (broCtx.Users.GetUserExists(user.Id) == false) +// { +// broCtx.Users.Add(new Db.Model.User +// { +// Id = user.Id, +// BrokerId = brokerId +// }); +// } - if (apiCtx.Users.Any(x => x.Id == user.Id)) - { - await this.UpdateUser(user, apiCtx); - } - else - { - await apiCtx.Users.AddAsync(new MessengerApi.Db.Entities.User - { - Id = user.Id, - ApiKey = user.ApiKey, - CanReceive = user.CanReceive, - CanSend = user.CanSend, - IsEnabled = user.IsEnabled, - Name = user.Name - }); - } - } +// if (apiCtx.Users.Any(x => x.Id == user.Id)) +// { +// await this.UpdateUser(user, apiCtx); +// } +// else +// { +// await apiCtx.Users.AddAsync(new MessengerApi.Db.Entities.User +// { +// Id = user.Id, +// ApiKey = user.ApiKey, +// CanReceive = user.CanReceive, +// CanSend = user.CanSend, +// IsEnabled = user.IsEnabled, +// Name = user.Name +// }); +// } +// } - private async Task UpdateUser(Model.Http.Users.UsersResponse.User user, MessengerDbContext apiCtx) - { - var apiUser = await apiCtx.Users.SingleAsync(x => x.Id == user.Id); - apiUser.ApiKey = user.ApiKey; - apiUser.CanReceive = user.CanReceive; - apiUser.CanSend = user.CanSend; - apiUser.IsEnabled = user.IsEnabled; - apiUser.Name = user.Name; - } - } -} \ No newline at end of file +// private async Task UpdateUser(Model.Http.Users.UsersResponse.User user, MessengerDbContext apiCtx) +// { +// var apiUser = await apiCtx.Users.SingleAsync(x => x.Id == user.Id); +// apiUser.ApiKey = user.ApiKey; +// apiUser.CanReceive = user.CanReceive; +// apiUser.CanSend = user.CanSend; +// apiUser.IsEnabled = user.IsEnabled; +// apiUser.Name = user.Name; +// } +// } +//} \ No newline at end of file diff --git a/code/MessengerBroker/MessengerBroker.csproj b/code/MessengerBroker/MessengerBroker.csproj index d5a8a4e..751b0a9 100644 --- a/code/MessengerBroker/MessengerBroker.csproj +++ b/code/MessengerBroker/MessengerBroker.csproj @@ -12,7 +12,9 @@ - + + + diff --git a/code/MessengerBroker/MessengerBroker.http b/code/MessengerBroker/MessengerBroker.http deleted file mode 100644 index 2562d21..0000000 --- a/code/MessengerBroker/MessengerBroker.http +++ /dev/null @@ -1,6 +0,0 @@ -@MessengerBroker_HostAddress = http://localhost:5048 - -GET {{MessengerBroker_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/code/MessengerBroker/Model/Http/Sync.cs b/code/MessengerBroker/Model/Http/Sync.cs deleted file mode 100644 index 4c4bd8d..0000000 --- a/code/MessengerBroker/Model/Http/Sync.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace MessengerBroker.Model.Http -{ - public class Sync - { - public class SyncRequest - { - public Guid BrokerId { get; set; } - - public DateTime SinceUtc { get; set; } - } - - public class SyncResponse - { - public Message[] Messages { get; set; } - - public class Message - { - public Guid Id { get; set; } - - public DateTime CreatedUtc { get; set; } - - public Guid? From { get; set; } - - public Guid? To { get; set; } - - public bool IsDelivered { get; set; } - - public bool IsAcknowledged { get; set; } - - public string PayloadId { get; set; } - - public string PayloadType { get; set; } - - public string Payload { get; set; } - - public DateTime? PayloadTimestamp { get; set; } - - public int? PayloadLifespanInSeconds { get; set; } - } - } - } -} \ No newline at end of file diff --git a/code/MessengerBroker/Models/CachedIdentity.cs b/code/MessengerBroker/Models/CachedIdentity.cs new file mode 100644 index 0000000..538382e --- /dev/null +++ b/code/MessengerBroker/Models/CachedIdentity.cs @@ -0,0 +1,12 @@ +using MessengerBroker.Configuration.Model.Servers; +using System.Security.Claims; + +namespace MessengerBroker.Models +{ + public class CachedIdentity + { + public SlaveServer Server { get; set; } + + public ClaimsPrincipal ClaimsPrincipal { get; set; } + } +} diff --git a/code/MessengerBroker/Models/Http/Messages.cs b/code/MessengerBroker/Models/Http/Messages.cs new file mode 100644 index 0000000..c1d7fb6 --- /dev/null +++ b/code/MessengerBroker/Models/Http/Messages.cs @@ -0,0 +1,17 @@ +namespace MessengerBroker.Model.Http +{ + public class Messages + { + public class MessagesRequest + { + public Guid OwnerBrokerId { get; set; } + + public DateTime SinceUtc { get; set; } + } + + public class MessagesResponse + { + public MessengerApi.Db.Entities.Message[] Messages { get; set; } + } + } +} \ No newline at end of file diff --git a/code/MessengerBroker/Model/Http/Users.cs b/code/MessengerBroker/Models/Http/Users.cs similarity index 86% rename from code/MessengerBroker/Model/Http/Users.cs rename to code/MessengerBroker/Models/Http/Users.cs index 495df81..09cf3f6 100644 --- a/code/MessengerBroker/Model/Http/Users.cs +++ b/code/MessengerBroker/Models/Http/Users.cs @@ -17,10 +17,6 @@ public string Name { get; set; } public bool IsEnabled { get; set; } - - public bool CanSend { get; set; } - - public bool CanReceive { get; set; } } public class UserRoute diff --git a/code/MessengerBroker/Models/Scoped/Identity.cs b/code/MessengerBroker/Models/Scoped/Identity.cs new file mode 100644 index 0000000..61e8863 --- /dev/null +++ b/code/MessengerBroker/Models/Scoped/Identity.cs @@ -0,0 +1,9 @@ +using MessengerBroker.Configuration.Model.Servers; + +namespace MessengerBroker.Models.Scoped +{ + public class Identity + { + public SlaveServer Server { get; set; } + } +} diff --git a/code/MessengerBroker/Program.cs b/code/MessengerBroker/Program.cs index c471fdf..4aeb56d 100644 --- a/code/MessengerBroker/Program.cs +++ b/code/MessengerBroker/Program.cs @@ -1,6 +1,14 @@ +using MessengerApi.Configuration.Sources.Environment; +using MessengerBroker.Configuration.Model; +using MessengerBroker.Factories; using MessengerBroker.Handlers; +using MessengerBroker.Handlers.Endpoint; using MessengerBroker.Model.Http; -using System.Runtime.CompilerServices; +using MessengerBroker.Models.Scoped; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.EntityFrameworkCore; +using System.Net; namespace MessengerBroker { @@ -8,94 +16,119 @@ namespace MessengerBroker { public static void Main(string[] args) { - var settings = (Settings)null; + BrokerConfiguration configuration = null; + + try + { + configuration = new BrokerConfiguration(new EnvironmentConfigurationSource()); + } + catch (Exception ex) + { + Console.WriteLine("Can't load settings.", ex); + throw; + } + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddMemoryCache(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(configuration); + builder.Services.AddSingleton(); + builder.Services.AddSingleton((sp) => + { + return new MessengerApi.Factories.DbContextFactory(configuration.ApiPersistenceConfiguration); + }); + + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + // Authentication. + builder.Services + .AddAuthentication("Bearer") + .AddScheme("Bearer", null); + + // Proxy registration to forward real client IPs. + builder.Services.Configure(options => + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + + foreach (var proxy in configuration.Proxies) + { + options.KnownProxies.Add(IPAddress.Parse(proxy)); + } + }); + var app = builder.Build(); - app.MapGet("/users", async (HttpContext httpContext) => + // DB Migrations + using (var ctx = app.Services.GetRequiredService().CreateDbContext()) { - var authHandler = app.Services.GetService(); - var brokerId = authHandler.Auth(httpContext); + var migrationLogger = app.Services.GetRequiredService(); - if(brokerId == null) + try { - return Results.Unauthorized(); + if (ctx.Database.GetPendingMigrations().Any()) + { + migrationLogger.Info("Applying migrations."); + ctx.Database.Migrate(); + } + else + { + migrationLogger.Info("No migrations pending."); + } } - - var dataHandler = app.Services.GetService(); - var usersAndRoutes = await dataHandler.GetLocalUsersAndRoutes(); - - var response = new Users.UsersResponse + catch (Exception ex) { - Users = usersAndRoutes.Item1.Select(x => new Users.UsersResponse.User - { - Id = x.Id, - Name = x.Name, - ApiKey = x.ApiKey, - CanReceive = x.CanReceive, - CanSend = x.CanSend, - IsEnabled = x.IsEnabled, - }).ToArray(), - UserRoutes = usersAndRoutes.Item2.Select(x => new Users.UsersResponse.UserRoute - { - Id = x.Id, - FromId = x.From.Id, - ToId = x.To.Id - }).ToArray() - }; + migrationLogger.Error("Can't run migrations successfully.", ex); + throw; + } + } + //// Housekeeping. + //if (configuration.HousekeepingEnabled) + //{ + // _ = Task.Run(async () => + // { + // while (true) + // { + // await app.Services.GetService().RemoveOldMessages(); + // await Task.Delay(TimeSpan.FromMinutes(1)); + // } + // }); + //} + + //// Run pull sync from masters. + //_ = Task.Run(async () => + //{ + // var cts = new CancellationTokenSource(); + // var handler = app.Services.GetService(); + + // foreach (var master in settings.Masters) + // { + // _ = handler.BeginSyncingWithMaster(master, cts.Token); + // } + //}); + + app.UseStaticFiles(); + app.UseRouting(); + app.UseAuthentication(); + app.UseForwardedHeaders(); + + app.MapGet("/users", async (UsersEndpointHandler handler) => + { + var response = await handler.GetUsers(); return Results.Json(response); }); - app.MapGet("/sync", async (HttpContext httpContext, [AsParameters] Sync.SyncRequest request) => + app.MapGet("/messages", async ( + MessagesEndpointHandler handler, + [AsParameters] Messages.MessagesRequest request) => { - var authHandler = app.Services.GetService(); - var brokerId = authHandler.Auth(httpContext); - - if(brokerId == null) - { - return Results.Unauthorized(); - } - else if(request.BrokerId != brokerId && request.BrokerId != settings.BrokerId) - { - return Results.Unauthorized(); - } - - var dataHandler = app.Services.GetService(); - var messages = await dataHandler.GetMessages(request.BrokerId, request.SinceUtc); - - var response = new Sync.SyncResponse - { - Messages = messages.Select(x => new Sync.SyncResponse.Message - { - Id = x.Id, - CreatedUtc = x.CreatedUtc, - From = x.From.Id, - To = x.To.Id, - IsAcknowledged = x.IsAcknowledged, - IsDelivered = x.IsDelivered, - Payload = x.Payload, - PayloadId = x.PayloadId, - PayloadLifespanInSeconds = x.PayloadLifespanInSeconds, - PayloadTimestamp = x.PayloadTimestamp, - PayloadType = x.PayloadType - }).ToArray() - }; - + var response = await handler.GetMessages(request); return Results.Json(response); }); - _ = Task.Run(async () => - { - var cts = new CancellationTokenSource(); - var handler = app.Services.GetService(); - - foreach(var master in settings.Masters) - { - _ = handler.BeginSyncingWithMaster(master, cts.Token); - } - }); - app.Run(); } } diff --git a/code/MessengerBroker/Settings.cs b/code/MessengerBroker/Settings.cs deleted file mode 100644 index 9efdb05..0000000 --- a/code/MessengerBroker/Settings.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace MessengerBroker -{ - public class Settings - { - /// - /// Connection string to Messenger API DB. - /// - public string MessengerApiDbConnectionString { get; set; } - - public string MessengerBrokerDbConnectionString { get; set; } - - public Guid BrokerId { get; set; } - - public MasterServer[] Masters { get; set; } - - public SlaveServer[] Slaves { get; set; } - - /// - /// A server that we are a slave to. If this server goes down, their users will alternate to us and we have to provide service during outage. We pull data from this server. - /// - public class MasterServer - { - public string BrokerApiUrl { get; set; } - - public Guid BrokerId { set; get; } - } - - /// - /// A server that slaves to us in case of our own outage. They pull from us. - /// - public class SlaveServer - { - public Guid BrokerId { get; set; } - } - } -} diff --git a/code/MessengerBroker/appsettings.Development.json b/code/MessengerBroker/appsettings.Development.json deleted file mode 100644 index 0c208ae..0000000 --- a/code/MessengerBroker/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/code/MessengerBroker/appsettings.json b/code/MessengerBroker/appsettings.json deleted file mode 100644 index 10f68b8..0000000 --- a/code/MessengerBroker/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -}