From 1d46afefd5b290f1dd8f95986377ae001c9adae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Kuusik?= Date: Sun, 24 May 2026 19:04:45 +0300 Subject: [PATCH 1/3] Hangfire for background jobs --- docker-compose.yml | 15 + fullstack2026BE/BLL/BLL.csproj.lscache | 1 + fullstack2026BE/BLL/Jobs/INotificationJob.cs | 6 + .../BackgroundService.csproj | 22 + .../BackgroundService.csproj.lscache | 444 ++++++++++++++++++ .../BackgroundService/Config/CronJobConfig.cs | 7 + .../Config/CronJobDispatcher.cs | 16 + .../Config/CronJobRegistrar.cs | 32 ++ .../BackgroundService/Config/ICronJob.cs | 7 + fullstack2026BE/BackgroundService/Dockerfile | 23 + .../BackgroundService/HangfireBuilder.cs | 81 ++++ .../Jobs/DeleteLogsTill60DaysAgoJob.cs | 19 + .../Jobs/DeletesExpiredRefreshTokensJob.cs | 13 + fullstack2026BE/BackgroundService/Program.cs | 6 + .../Properties/launchSettings.json | 12 + .../appsettings.Development.json | 17 + .../BackgroundService/appsettings.json | 16 + .../Data.Access.Contracts.csproj.lscache | 1 + .../JobCalls/IJobActor.cs | 11 + .../Data.Access.Dapper.csproj.lscache | 5 +- .../Helpers/SqlKataDbConnectionExtensions.cs | 2 +- .../Data.Access.Dapper/JobCalls/JobActor.cs | 20 + .../Repositories/NotificationRepository.cs | 1 + .../Helpers/DependencyInjectionHelper.cs | 47 +- .../Infrastructure.csproj.lscache | 2 +- .../Infrastructure/Models/ApplicationName.cs | 7 + .../UnitTests/UnitTests.csproj.lscache | 4 + fullstack2026BE/WebApp/TripAppBuilder.cs | 21 +- fullstack2026BE/WebApp/WebApp.csproj | 2 + fullstack2026BE/WebApp/WebApp.csproj.lscache | 4 + fullstack2026BE/WebApp/appsettings.json | 1 + .../fullstack2026BE.csproj.lscache | 79 ++-- fullstack2026BE/fullstack2026BE.sln | 6 + 33 files changed, 882 insertions(+), 68 deletions(-) create mode 100644 fullstack2026BE/BLL/Jobs/INotificationJob.cs create mode 100644 fullstack2026BE/BackgroundService/BackgroundService.csproj create mode 100644 fullstack2026BE/BackgroundService/BackgroundService.csproj.lscache create mode 100644 fullstack2026BE/BackgroundService/Config/CronJobConfig.cs create mode 100644 fullstack2026BE/BackgroundService/Config/CronJobDispatcher.cs create mode 100644 fullstack2026BE/BackgroundService/Config/CronJobRegistrar.cs create mode 100644 fullstack2026BE/BackgroundService/Config/ICronJob.cs create mode 100644 fullstack2026BE/BackgroundService/Dockerfile create mode 100644 fullstack2026BE/BackgroundService/HangfireBuilder.cs create mode 100644 fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs create mode 100644 fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs create mode 100644 fullstack2026BE/BackgroundService/Program.cs create mode 100644 fullstack2026BE/BackgroundService/Properties/launchSettings.json create mode 100644 fullstack2026BE/BackgroundService/appsettings.Development.json create mode 100644 fullstack2026BE/BackgroundService/appsettings.json create mode 100644 fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs rename fullstack2026BE/{Infrastructure => Data.Access.Dapper}/Helpers/SqlKataDbConnectionExtensions.cs (92%) create mode 100644 fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs create mode 100644 fullstack2026BE/Infrastructure/Models/ApplicationName.cs diff --git a/docker-compose.yml b/docker-compose.yml index ae79381..a03f5ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,21 @@ services: networks: - app-network + background-service: + build: + context: ./fullstack2026BE + dockerfile: BackgroundService/Dockerfile + environment: + ConnectionStrings__DefaultConnection: "Server=sqlserver,1433;Database=${DB_NAME};User Id=sa;Password=${DB_PASSWORD};TrustServerCertificate=True" + ASPNETCORE_ENVIRONMENT: Development + ports: + - "5002:8080" # Hangfire dashboard at http://localhost:5002/hangfire + depends_on: + sqlserver: + condition: service_healthy + networks: + - app-network + nginx: build: context: . diff --git a/fullstack2026BE/BLL/BLL.csproj.lscache b/fullstack2026BE/BLL/BLL.csproj.lscache index b0051ff..9d99154 100644 --- a/fullstack2026BE/BLL/BLL.csproj.lscache +++ b/fullstack2026BE/BLL/BLL.csproj.lscache @@ -53,6 +53,7 @@ TemporaryDependencyNodeTargetIdentifier=net9.0 /warnaserror+:NU1605,SYSLIB0011 [sourceFiles] +Jobs/INotificationJob.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs BLL.AssemblyInfo.cs diff --git a/fullstack2026BE/BLL/Jobs/INotificationJob.cs b/fullstack2026BE/BLL/Jobs/INotificationJob.cs new file mode 100644 index 0000000..284e57c --- /dev/null +++ b/fullstack2026BE/BLL/Jobs/INotificationJob.cs @@ -0,0 +1,6 @@ +namespace BLL.Jobs; + +public interface INotificationJob +{ + Task SendUserDigestAsync(string userId); +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/BackgroundService.csproj b/fullstack2026BE/BackgroundService/BackgroundService.csproj new file mode 100644 index 0000000..f85f1a5 --- /dev/null +++ b/fullstack2026BE/BackgroundService/BackgroundService.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + dotnet-BackgroundService-284fb7f5-8953-4e33-9155-9a3ced1bb482 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/BackgroundService.csproj.lscache b/fullstack2026BE/BackgroundService/BackgroundService.csproj.lscache new file mode 100644 index 0000000..a45865f --- /dev/null +++ b/fullstack2026BE/BackgroundService/BackgroundService.csproj.lscache @@ -0,0 +1,444 @@ +version=1 + +# This file caches language service data to improve the performance of C# Dev Kit. +# It is not intended for manual editing. It can safely be deleted and will be +# regenerated automatically. For more information, see https://aka.ms/lscache +# +# To control where cache files are stored, use the following VS Code setting: +# "dotnet.projectsystem.cacheInProjectFolder": true + +[project] +language=C# +primary +lastDtbSucceeded + +[properties] +AssemblyName=BackgroundService +CommandLineArgsForDesignTimeEvaluation=-langversion:13.0 -define:TRACE +CompilerGeneratedFilesOutputPath= +MaxSupportedLangVersion=13.0 +ProjectAssetsFile=obj/project.assets.json +RootNamespace=BackgroundService +RunAnalyzers= +RunAnalyzersDuringLiveAnalysis= +SolutionPath=*Undefined* +TargetFrameworkIdentifier=.NETCoreApp +TargetPath=bin/Debug/net9.0/BackgroundService.dll +TargetRefPath=obj/Debug/net9.0/ref/BackgroundService.dll +TemporaryDependencyNodeTargetIdentifier=net9.0 + +[commandLineArguments] +/noconfig +/unsafe- +/checked- +/nowarn:1701,1702,1701,1702 +/fullpaths +/nostdlib+ +/errorreport:prompt +/warn:9 +/define:TRACE;DEBUG;NET;NET9_0;NETCOREAPP;NET5_0_OR_GREATER;NET6_0_OR_GREATER;NET7_0_OR_GREATER;NET8_0_OR_GREATER;NET9_0_OR_GREATER;NETCOREAPP1_0_OR_GREATER;NETCOREAPP1_1_OR_GREATER;NETCOREAPP2_0_OR_GREATER;NETCOREAPP2_1_OR_GREATER;NETCOREAPP2_2_OR_GREATER;NETCOREAPP3_0_OR_GREATER;NETCOREAPP3_1_OR_GREATER +/highentropyva+ +/nullable:enable +/debug+ +/debug:portable +/filealign:512 +/optimize- +/out:obj/Debug/net9.0/BackgroundService.dll +/refout:obj/Debug/net9.0/refint/BackgroundService.dll +/target:exe +/warnaserror- +/utf8output +/deterministic+ +/langversion:13.0 +/warnaserror+:NU1605,SYSLIB0011 + +[sourceFiles] +Config/ + CronJobConfig.cs + CronJobDispatcher.cs + CronJobRegistrar.cs + ICronJob.cs +HangfireBuilder.cs +Jobs/ + DeleteLogsTill60DaysAgoJob.cs + DeletesExpiredRefreshTokensJob.cs +obj/Debug/net9.0/ + .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + BackgroundService.AssemblyInfo.cs + BackgroundService.GlobalUsings.g.cs +Program.cs + +[metadataReferences] +../ + Data.Access.Contracts/obj/Debug/net9.0/ref/Data.Access.Contracts.dll + Data.Access.Dapper/obj/Debug/net9.0/ref/Data.Access.Dapper.dll + Data.Model/obj/Debug/net9.0/ref/Data.Model.dll + Infrastructure/obj/Debug/net9.0/ref/Infrastructure.dll +/packs/Microsoft.AspNetCore.App.Ref/9.0.0/ref/net9.0/ + Microsoft.AspNetCore.Antiforgery.dll + Microsoft.AspNetCore.Authentication.Abstractions.dll + Microsoft.AspNetCore.Authentication.BearerToken.dll + Microsoft.AspNetCore.Authentication.Cookies.dll + Microsoft.AspNetCore.Authentication.Core.dll + Microsoft.AspNetCore.Authentication.dll + Microsoft.AspNetCore.Authentication.OAuth.dll + Microsoft.AspNetCore.Authorization.dll + Microsoft.AspNetCore.Authorization.Policy.dll + Microsoft.AspNetCore.Components.Authorization.dll + Microsoft.AspNetCore.Components.dll + Microsoft.AspNetCore.Components.Endpoints.dll + Microsoft.AspNetCore.Components.Forms.dll + Microsoft.AspNetCore.Components.Server.dll + Microsoft.AspNetCore.Components.Web.dll + Microsoft.AspNetCore.Connections.Abstractions.dll + Microsoft.AspNetCore.CookiePolicy.dll + Microsoft.AspNetCore.Cors.dll + Microsoft.AspNetCore.Cryptography.Internal.dll + Microsoft.AspNetCore.Cryptography.KeyDerivation.dll + Microsoft.AspNetCore.DataProtection.Abstractions.dll + Microsoft.AspNetCore.DataProtection.dll + Microsoft.AspNetCore.DataProtection.Extensions.dll + Microsoft.AspNetCore.Diagnostics.Abstractions.dll + Microsoft.AspNetCore.Diagnostics.dll + Microsoft.AspNetCore.Diagnostics.HealthChecks.dll + Microsoft.AspNetCore.dll + Microsoft.AspNetCore.HostFiltering.dll + Microsoft.AspNetCore.Hosting.Abstractions.dll + Microsoft.AspNetCore.Hosting.dll + Microsoft.AspNetCore.Hosting.Server.Abstractions.dll + Microsoft.AspNetCore.Html.Abstractions.dll + Microsoft.AspNetCore.Http.Abstractions.dll + Microsoft.AspNetCore.Http.Connections.Common.dll + Microsoft.AspNetCore.Http.Connections.dll + Microsoft.AspNetCore.Http.dll + Microsoft.AspNetCore.Http.Extensions.dll + Microsoft.AspNetCore.Http.Features.dll + Microsoft.AspNetCore.Http.Results.dll + Microsoft.AspNetCore.HttpLogging.dll + Microsoft.AspNetCore.HttpOverrides.dll + Microsoft.AspNetCore.HttpsPolicy.dll + Microsoft.AspNetCore.Identity.dll + Microsoft.AspNetCore.Localization.dll + Microsoft.AspNetCore.Localization.Routing.dll + Microsoft.AspNetCore.Metadata.dll + Microsoft.AspNetCore.Mvc.Abstractions.dll + Microsoft.AspNetCore.Mvc.ApiExplorer.dll + Microsoft.AspNetCore.Mvc.Core.dll + Microsoft.AspNetCore.Mvc.Cors.dll + Microsoft.AspNetCore.Mvc.DataAnnotations.dll + Microsoft.AspNetCore.Mvc.dll + Microsoft.AspNetCore.Mvc.Formatters.Json.dll + Microsoft.AspNetCore.Mvc.Formatters.Xml.dll + Microsoft.AspNetCore.Mvc.Localization.dll + Microsoft.AspNetCore.Mvc.Razor.dll + Microsoft.AspNetCore.Mvc.RazorPages.dll + Microsoft.AspNetCore.Mvc.TagHelpers.dll + Microsoft.AspNetCore.Mvc.ViewFeatures.dll + Microsoft.AspNetCore.OutputCaching.dll + Microsoft.AspNetCore.RateLimiting.dll + Microsoft.AspNetCore.Razor.dll + Microsoft.AspNetCore.Razor.Runtime.dll + Microsoft.AspNetCore.RequestDecompression.dll + Microsoft.AspNetCore.ResponseCaching.Abstractions.dll + Microsoft.AspNetCore.ResponseCaching.dll + Microsoft.AspNetCore.ResponseCompression.dll + Microsoft.AspNetCore.Rewrite.dll + Microsoft.AspNetCore.Routing.Abstractions.dll + Microsoft.AspNetCore.Routing.dll + Microsoft.AspNetCore.Server.HttpSys.dll + Microsoft.AspNetCore.Server.IIS.dll + Microsoft.AspNetCore.Server.IISIntegration.dll + Microsoft.AspNetCore.Server.Kestrel.Core.dll + Microsoft.AspNetCore.Server.Kestrel.dll + Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.dll + Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.dll + Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll + Microsoft.AspNetCore.Session.dll + Microsoft.AspNetCore.SignalR.Common.dll + Microsoft.AspNetCore.SignalR.Core.dll + Microsoft.AspNetCore.SignalR.dll + Microsoft.AspNetCore.SignalR.Protocols.Json.dll + Microsoft.AspNetCore.StaticAssets.dll + Microsoft.AspNetCore.StaticFiles.dll + Microsoft.AspNetCore.WebSockets.dll + Microsoft.AspNetCore.WebUtilities.dll + Microsoft.Extensions.Caching.Abstractions.dll + Microsoft.Extensions.Caching.Memory.dll + Microsoft.Extensions.Configuration.Abstractions.dll + Microsoft.Extensions.Configuration.Binder.dll + Microsoft.Extensions.Configuration.CommandLine.dll + Microsoft.Extensions.Configuration.dll + Microsoft.Extensions.Configuration.EnvironmentVariables.dll + Microsoft.Extensions.Configuration.FileExtensions.dll + Microsoft.Extensions.Configuration.Ini.dll + Microsoft.Extensions.Configuration.Json.dll + Microsoft.Extensions.Configuration.KeyPerFile.dll + Microsoft.Extensions.Configuration.UserSecrets.dll + Microsoft.Extensions.Configuration.Xml.dll + Microsoft.Extensions.DependencyInjection.dll + Microsoft.Extensions.Diagnostics.Abstractions.dll + Microsoft.Extensions.Diagnostics.dll + Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.dll + Microsoft.Extensions.Diagnostics.HealthChecks.dll + Microsoft.Extensions.Features.dll + Microsoft.Extensions.FileProviders.Abstractions.dll + Microsoft.Extensions.FileProviders.Composite.dll + Microsoft.Extensions.FileProviders.Embedded.dll + Microsoft.Extensions.FileProviders.Physical.dll + Microsoft.Extensions.FileSystemGlobbing.dll + Microsoft.Extensions.Hosting.Abstractions.dll + Microsoft.Extensions.Hosting.dll + Microsoft.Extensions.Http.dll + Microsoft.Extensions.Identity.Core.dll + Microsoft.Extensions.Identity.Stores.dll + Microsoft.Extensions.Localization.Abstractions.dll + Microsoft.Extensions.Localization.dll + Microsoft.Extensions.Logging.Configuration.dll + Microsoft.Extensions.Logging.Console.dll + Microsoft.Extensions.Logging.Debug.dll + Microsoft.Extensions.Logging.dll + Microsoft.Extensions.Logging.EventLog.dll + Microsoft.Extensions.Logging.EventSource.dll + Microsoft.Extensions.Logging.TraceSource.dll + Microsoft.Extensions.ObjectPool.dll + Microsoft.Extensions.Options.ConfigurationExtensions.dll + Microsoft.Extensions.Options.DataAnnotations.dll + Microsoft.Extensions.WebEncoders.dll + Microsoft.JSInterop.dll + Microsoft.Net.Http.Headers.dll + System.Security.Cryptography.Xml.dll + System.Threading.RateLimiting.dll +/packs/Microsoft.NETCore.App.Ref/9.0.0/ref/net9.0/ + Microsoft.CSharp.dll + Microsoft.VisualBasic.Core.dll + Microsoft.VisualBasic.dll + Microsoft.Win32.Primitives.dll + Microsoft.Win32.Registry.dll + mscorlib.dll + netstandard.dll + System.AppContext.dll + System.Buffers.dll + System.Collections.Concurrent.dll + System.Collections.dll + System.Collections.Immutable.dll + System.Collections.NonGeneric.dll + System.Collections.Specialized.dll + System.ComponentModel.Annotations.dll + System.ComponentModel.DataAnnotations.dll + System.ComponentModel.dll + System.ComponentModel.EventBasedAsync.dll + System.ComponentModel.Primitives.dll + System.ComponentModel.TypeConverter.dll + System.Configuration.dll + System.Console.dll + System.Core.dll + System.Data.Common.dll + System.Data.DataSetExtensions.dll + System.Data.dll + System.Diagnostics.Contracts.dll + System.Diagnostics.Debug.dll + System.Diagnostics.DiagnosticSource.dll + System.Diagnostics.FileVersionInfo.dll + System.Diagnostics.Process.dll + System.Diagnostics.StackTrace.dll + System.Diagnostics.TextWriterTraceListener.dll + System.Diagnostics.Tools.dll + System.Diagnostics.TraceSource.dll + System.Diagnostics.Tracing.dll + System.dll + System.Drawing.dll + System.Drawing.Primitives.dll + System.Dynamic.Runtime.dll + System.Formats.Asn1.dll + System.Formats.Tar.dll + System.Globalization.Calendars.dll + System.Globalization.dll + System.Globalization.Extensions.dll + System.IO.Compression.Brotli.dll + System.IO.Compression.dll + System.IO.Compression.FileSystem.dll + System.IO.Compression.ZipFile.dll + System.IO.dll + System.IO.FileSystem.AccessControl.dll + System.IO.FileSystem.dll + System.IO.FileSystem.DriveInfo.dll + System.IO.FileSystem.Primitives.dll + System.IO.FileSystem.Watcher.dll + System.IO.IsolatedStorage.dll + System.IO.MemoryMappedFiles.dll + System.IO.Pipelines.dll + System.IO.Pipes.AccessControl.dll + System.IO.Pipes.dll + System.IO.UnmanagedMemoryStream.dll + System.Linq.dll + System.Linq.Expressions.dll + System.Linq.Parallel.dll + System.Linq.Queryable.dll + System.Memory.dll + System.Net.dll + System.Net.Http.dll + System.Net.Http.Json.dll + System.Net.HttpListener.dll + System.Net.Mail.dll + System.Net.NameResolution.dll + System.Net.NetworkInformation.dll + System.Net.Ping.dll + System.Net.Primitives.dll + System.Net.Quic.dll + System.Net.Requests.dll + System.Net.Security.dll + System.Net.ServicePoint.dll + System.Net.Sockets.dll + System.Net.WebClient.dll + System.Net.WebHeaderCollection.dll + System.Net.WebProxy.dll + System.Net.WebSockets.Client.dll + System.Net.WebSockets.dll + System.Numerics.dll + System.Numerics.Vectors.dll + System.ObjectModel.dll + System.Reflection.DispatchProxy.dll + System.Reflection.dll + System.Reflection.Emit.dll + System.Reflection.Emit.ILGeneration.dll + System.Reflection.Emit.Lightweight.dll + System.Reflection.Extensions.dll + System.Reflection.Metadata.dll + System.Reflection.Primitives.dll + System.Reflection.TypeExtensions.dll + System.Resources.Reader.dll + System.Resources.ResourceManager.dll + System.Resources.Writer.dll + System.Runtime.CompilerServices.Unsafe.dll + System.Runtime.CompilerServices.VisualC.dll + System.Runtime.dll + System.Runtime.Extensions.dll + System.Runtime.Handles.dll + System.Runtime.InteropServices.dll + System.Runtime.InteropServices.JavaScript.dll + System.Runtime.InteropServices.RuntimeInformation.dll + System.Runtime.Intrinsics.dll + System.Runtime.Loader.dll + System.Runtime.Numerics.dll + System.Runtime.Serialization.dll + System.Runtime.Serialization.Formatters.dll + System.Runtime.Serialization.Json.dll + System.Runtime.Serialization.Primitives.dll + System.Runtime.Serialization.Xml.dll + System.Security.AccessControl.dll + System.Security.Claims.dll + System.Security.Cryptography.Algorithms.dll + System.Security.Cryptography.Cng.dll + System.Security.Cryptography.Csp.dll + System.Security.Cryptography.dll + System.Security.Cryptography.Encoding.dll + System.Security.Cryptography.OpenSsl.dll + System.Security.Cryptography.Primitives.dll + System.Security.Cryptography.X509Certificates.dll + System.Security.dll + System.Security.Principal.dll + System.Security.Principal.Windows.dll + System.Security.SecureString.dll + System.ServiceModel.Web.dll + System.ServiceProcess.dll + System.Text.Encoding.CodePages.dll + System.Text.Encoding.dll + System.Text.Encoding.Extensions.dll + System.Text.Encodings.Web.dll + System.Text.Json.dll + System.Text.RegularExpressions.dll + System.Threading.Channels.dll + System.Threading.dll + System.Threading.Overlapped.dll + System.Threading.Tasks.Dataflow.dll + System.Threading.Tasks.dll + System.Threading.Tasks.Extensions.dll + System.Threading.Tasks.Parallel.dll + System.Threading.Thread.dll + System.Threading.ThreadPool.dll + System.Threading.Timer.dll + System.Transactions.dll + System.Transactions.Local.dll + System.ValueTuple.dll + System.Web.dll + System.Web.HttpUtility.dll + System.Windows.dll + System.Xml.dll + System.Xml.Linq.dll + System.Xml.ReaderWriter.dll + System.Xml.Serialization.dll + System.Xml.XDocument.dll + System.Xml.XmlDocument.dll + System.Xml.XmlSerializer.dll + System.Xml.XPath.dll + System.Xml.XPath.XDocument.dll + WindowsBase.dll +/ + dapper/2.0.123/lib/net5.0/Dapper.dll + hangfire.aspnetcore/1.8.23/lib/netcoreapp3.0/Hangfire.AspNetCore.dll + hangfire.core/1.8.23/lib/netstandard2.0/Hangfire.Core.dll + hangfire.netcore/1.8.23/lib/netstandard2.1/Hangfire.NetCore.dll + hangfire.sqlserver/1.8.23/lib/netstandard2.0/Hangfire.SqlServer.dll + humanizer.core/2.8.26/lib/netstandard2.0/Humanizer.dll + microsoft.aspnetcore.authentication.jwtbearer/9.0.0/lib/net9.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll + microsoft.bcl.cryptography/9.0.13/lib/net9.0/Microsoft.Bcl.Cryptography.dll + microsoft.data.sqlclient.extensions.abstractions/1.0.0/lib/netstandard2.0/Microsoft.Data.SqlClient.Extensions.Abstractions.dll + microsoft.data.sqlclient.internal.logging/1.0.0/lib/netstandard2.0/Microsoft.Data.SqlClient.Internal.Logging.dll + microsoft.data.sqlclient/7.0.0/ref/net9.0/Microsoft.Data.SqlClient.dll + microsoft.extensions.dependencyinjection.abstractions/9.0.16/lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll + microsoft.extensions.logging.abstractions/9.0.13/lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll + microsoft.extensions.options/9.0.13/lib/net9.0/Microsoft.Extensions.Options.dll + microsoft.extensions.primitives/9.0.13/lib/net9.0/Microsoft.Extensions.Primitives.dll + microsoft.identitymodel.abstractions/8.16.0/lib/net9.0/Microsoft.IdentityModel.Abstractions.dll + microsoft.identitymodel.jsonwebtokens/8.16.0/lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll + microsoft.identitymodel.logging/8.16.0/lib/net9.0/Microsoft.IdentityModel.Logging.dll + microsoft.identitymodel.protocols.openidconnect/8.16.0/lib/net9.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll + microsoft.identitymodel.protocols/8.16.0/lib/net9.0/Microsoft.IdentityModel.Protocols.dll + microsoft.identitymodel.tokens/8.16.0/lib/net9.0/Microsoft.IdentityModel.Tokens.dll + microsoft.sqlserver.server/1.0.0/lib/netstandard2.0/Microsoft.SqlServer.Server.dll + newtonsoft.json/13.0.3/lib/net6.0/Newtonsoft.Json.dll + npgsql/9.0.0/lib/net8.0/Npgsql.dll + serilog.enrichers.environment/3.0.1/lib/net8.0/Serilog.Enrichers.Environment.dll + serilog.sinks.console/6.1.1/lib/net8.0/Serilog.Sinks.Console.dll + serilog.sinks.mssqlserver/8.0.0/lib/net8.0/Serilog.Sinks.MSSqlServer.dll + serilog/4.3.1/lib/net9.0/Serilog.dll + sqlkata.execution/4.0.1/lib/net8.0/SqlKata.Execution.dll + sqlkata/4.0.1/lib/net8.0/SqlKata.Core.dll + system.configuration.configurationmanager/9.0.13/lib/net9.0/System.Configuration.ConfigurationManager.dll + system.diagnostics.eventlog/9.0.13/lib/net9.0/System.Diagnostics.EventLog.dll + system.identitymodel.tokens.jwt/8.16.0/lib/net9.0/System.IdentityModel.Tokens.Jwt.dll + system.security.cryptography.pkcs/9.0.13/lib/net9.0/System.Security.Cryptography.Pkcs.dll + system.security.cryptography.protecteddata/9.0.13/lib/net9.0/System.Security.Cryptography.ProtectedData.dll + +[analyzerReferences] +/packs/Microsoft.AspNetCore.App.Ref/9.0.0/analyzers/dotnet/cs/ + Microsoft.AspNetCore.App.Analyzers.dll + Microsoft.AspNetCore.App.CodeFixes.dll + Microsoft.AspNetCore.Components.Analyzers.dll +/packs/Microsoft.NETCore.App.Ref/9.0.0/analyzers/dotnet/cs/ + Microsoft.Interop.ComInterfaceGenerator.dll + Microsoft.Interop.JavaScript.JSImportGenerator.dll + Microsoft.Interop.LibraryImportGenerator.dll + Microsoft.Interop.SourceGeneration.dll + System.Text.Json.SourceGeneration.dll + System.Text.RegularExpressions.Generator.dll +/sdk/9.0.100/Sdks/Microsoft.NET.Sdk.Razor/source-generators/ + Microsoft.AspNetCore.Razor.Utilities.Shared.dll + Microsoft.CodeAnalysis.Razor.Compiler.dll + Microsoft.Extensions.ObjectPool.dll + System.Collections.Immutable.dll +/sdk/9.0.100/Sdks/Microsoft.NET.Sdk.Web/analyzers/cs/ + Microsoft.AspNetCore.Analyzers.dll + Microsoft.AspNetCore.Mvc.Analyzers.dll +/sdk/9.0.100/Sdks/Microsoft.NET.Sdk/analyzers/ + Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll + Microsoft.CodeAnalysis.NetAnalyzers.dll +/ + microsoft.extensions.logging.abstractions/9.0.13/analyzers/dotnet/roslyn4.4/cs/Microsoft.Extensions.Logging.Generators.dll + microsoft.extensions.options/9.0.13/analyzers/dotnet/roslyn4.4/cs/Microsoft.Extensions.Options.SourceGeneration.dll + +[analyzerConfigFiles] +/sdk/9.0.100/Sdks/Microsoft.NET.Sdk/ + analyzers/build/config/analysislevel_9_default.globalconfig + codestyle/cs/build/config/analysislevelstyle_default.globalconfig +obj/Debug/net9.0/BackgroundService.GeneratedMSBuildEditorConfig.editorconfig diff --git a/fullstack2026BE/BackgroundService/Config/CronJobConfig.cs b/fullstack2026BE/BackgroundService/Config/CronJobConfig.cs new file mode 100644 index 0000000..49bcda7 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Config/CronJobConfig.cs @@ -0,0 +1,7 @@ +namespace BackgroundService.Config; + +public class CronJobConfig +{ + public string CronExpression { get; set; } = string.Empty; + public bool Enabled { get; set; } = true; +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Config/CronJobDispatcher.cs b/fullstack2026BE/BackgroundService/Config/CronJobDispatcher.cs new file mode 100644 index 0000000..7155ad8 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Config/CronJobDispatcher.cs @@ -0,0 +1,16 @@ +using BackgroundService.Jobs; +using Infrastructure.DependencyInjection; + +namespace BackgroundService.Config; + +// Hangfire resolves this type from DI; it routes the call to the correct ICronJob by ID. +public class CronJobDispatcher(IEnumerable jobs) : IScopedDependency +{ + public Task Dispatch(string jobId) + { + var job = jobs.FirstOrDefault(j => j.JobId == jobId) + ?? throw new InvalidOperationException($"No cron job registered with id '{jobId}'"); + + return job.ExecuteAsync(); + } +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Config/CronJobRegistrar.cs b/fullstack2026BE/BackgroundService/Config/CronJobRegistrar.cs new file mode 100644 index 0000000..3ad1e04 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Config/CronJobRegistrar.cs @@ -0,0 +1,32 @@ +using Hangfire; +using Infrastructure.Logging; + +namespace BackgroundService.Config; + +public static class CronJobRegistrar +{ + public static void RegisterCronJobs(this WebApplication app) + { + using var scope = app.Services.CreateScope(); + var jobs = scope.ServiceProvider.GetServices().ToList(); + if (jobs.Count == 0) return; + + var config = app.Configuration.GetSection("CronJobs"); + foreach (var job in jobs) + { + var jobConfig = config.GetSection(job.JobId).Get() ?? new CronJobConfig(); + + if (!jobConfig.Enabled || string.IsNullOrEmpty(jobConfig.CronExpression)) + { + RecurringJob.RemoveIfExists(job.JobId); + continue; + } + + RecurringJob.AddOrUpdate( + job.JobId, + dispatcher => dispatcher.Dispatch(job.JobId), + jobConfig.CronExpression); + } + Logger.Info($"Registered jobs on build: {string.Join(", ", jobs.Select(j => j.JobId))}"); + } +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Config/ICronJob.cs b/fullstack2026BE/BackgroundService/Config/ICronJob.cs new file mode 100644 index 0000000..de85c68 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Config/ICronJob.cs @@ -0,0 +1,7 @@ +namespace BackgroundService.Config; + +public interface ICronJob +{ + string JobId { get; } + Task ExecuteAsync(); +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Dockerfile b/fullstack2026BE/BackgroundService/Dockerfile new file mode 100644 index 0000000..131cc07 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Dockerfile @@ -0,0 +1,23 @@ +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy project files for layer-cached restore +COPY fullstack2026BE.sln ./ +COPY BackgroundService/BackgroundService.csproj BackgroundService/ +COPY BLL/BLL.csproj BLL/ +COPY Data.Access.Contracts/Data.Access.Contracts.csproj Data.Access.Contracts/ +COPY Data.Access.Dapper/Data.Access.Dapper.csproj Data.Access.Dapper/ +COPY Data.Model/Data.Model.csproj Data.Model/ +COPY Infrastructure/Infrastructure.csproj Infrastructure/ +RUN dotnet restore BackgroundService/BackgroundService.csproj + +COPY . . + +FROM build AS publish +RUN dotnet publish BackgroundService/BackgroundService.csproj -c Release -o /app/publish + +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime +WORKDIR /app +COPY --from=publish /app/publish . +EXPOSE 8080 +ENTRYPOINT ["dotnet", "BackgroundService.dll"] \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/HangfireBuilder.cs b/fullstack2026BE/BackgroundService/HangfireBuilder.cs new file mode 100644 index 0000000..e7dcdda --- /dev/null +++ b/fullstack2026BE/BackgroundService/HangfireBuilder.cs @@ -0,0 +1,81 @@ +using BackgroundService.Config; +using BackgroundService.Jobs; +using Hangfire; +using Hangfire.Dashboard; +using Hangfire.SqlServer; +using Infrastructure.Core; +using Infrastructure.Helpers; +using Infrastructure.Logging; +using Microsoft.Data.SqlClient; +using Serilog; + +namespace BackgroundService; + +internal class HangfireBuilder(string[] args) : IAppBuilder +{ + public WebApplication Build() + { + var builder = WebApplication.CreateBuilder(args); + var appName = builder.Configuration["AppName"] ?? throw new NullReferenceException("AppName configuration is missing."); + var emv = builder.Environment.EnvironmentName; + var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") + ?? throw new InvalidOperationException( + "DefaultConnection connection string is missing."); + + MainDb.Initialize(() => new SqlConnection(connectionString)); + + builder.Services.AddAppLogger( + builder.Configuration, + new MsSqlLogSink(connectionString)); + + builder.Services.AddHangfire(config => config + + .SetDataCompatibilityLevel(CompatibilityLevel.Version_180) + .UseSimpleAssemblyNameTypeSerializer() + .UseRecommendedSerializerSettings() + .UseSqlServerStorage(connectionString, new SqlServerStorageOptions + { + CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), + SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), + QueuePollInterval = TimeSpan.Zero, + UseRecommendedIsolationLevel = true, + DisableGlobalLocks = true + })); + + builder.Services.AddHangfireServer(options => { options.WorkerCount = Environment.ProcessorCount * 2; }); + //var jobs = GetJobClassesFromSettings(builder.Configuration); + + builder.Services.ConfigureAppsDi(appName); + + var app = builder.Build(); + + // Dashboard is open for internal/dev access — add auth middleware in production + app.UseHangfireDashboard("/hangfire", new DashboardOptions + { + Authorization = [new AllowAllDashboardAuthorizationFilter(builder.Environment.IsDevelopment())] + }); + + app.MapGet("/health", () => Results.Ok()); + + app.RegisterCronJobs(); + Logger.Info(emv); + return app; + } + + private static IEnumerable GetJobClassesFromSettings(IConfiguration configuration) + { + return configuration + .GetSection("CronJobs") + .GetChildren() + .Select(s => s["JobClassName"]) + .Where(name => !string.IsNullOrWhiteSpace(name))!; + } +} + +file class AllowAllDashboardAuthorizationFilter(bool isDev) : IDashboardAuthorizationFilter +{ + public bool Authorize(DashboardContext context) + { + return isDev; + } +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs b/fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs new file mode 100644 index 0000000..9d126c5 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs @@ -0,0 +1,19 @@ +using BackgroundService.Config; +using Data.Access.Contracts.JobCalls; +using Hangfire; +using Infrastructure.DependencyInjection; +using Infrastructure.Logging; + +namespace BackgroundService.Jobs; + +[AutomaticRetry(Attempts = 2)] +public class DeleteLogsTill60DaysAgoJob(IJobActor jobActor) : ICronJob, IScopedDependency +{ + public string JobId => "database-delete-logs-till60-days-ago-job"; + + public async Task ExecuteAsync() + { + var entitiesDeleted = await jobActor.ClearLogsFromDaysAgoAsync(60); + Logger.Info($"Database logging cleanup job executed at {DateTimeOffset.UtcNow}, entities deleted: {entitiesDeleted}"); + } +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs b/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs new file mode 100644 index 0000000..db4a8c1 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs @@ -0,0 +1,13 @@ +using BackgroundService.Config; +using Infrastructure.DependencyInjection; + +namespace BackgroundService.Jobs; + +public class DeletesExpiredRefreshTokensJob : ICronJob, IScopedDependency +{ + public string JobId => "database-delete-expired-refresh-tokens-job"; + public Task ExecuteAsync() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Program.cs b/fullstack2026BE/BackgroundService/Program.cs new file mode 100644 index 0000000..66b4bd2 --- /dev/null +++ b/fullstack2026BE/BackgroundService/Program.cs @@ -0,0 +1,6 @@ +using BackgroundService; + +var builder = new HangfireBuilder(args); +var app = builder.Build(); + +app.Run(); diff --git a/fullstack2026BE/BackgroundService/Properties/launchSettings.json b/fullstack2026BE/BackgroundService/Properties/launchSettings.json new file mode 100644 index 0000000..46df0be --- /dev/null +++ b/fullstack2026BE/BackgroundService/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "BackgroundService": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/fullstack2026BE/BackgroundService/appsettings.Development.json b/fullstack2026BE/BackgroundService/appsettings.Development.json new file mode 100644 index 0000000..7975622 --- /dev/null +++ b/fullstack2026BE/BackgroundService/appsettings.Development.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "DefaultConnection": "Server=localhost,1433;Database=FullStackApp;User Id=sa;Password=jnwKUghLAUWGh3t73t;TrustServerCertificate=True" + }, + "CronJobs": { + "database-cleanup-daily": { + "CronExpression": "0 2 * * *", + "Enabled": true + } + } +} \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/appsettings.json b/fullstack2026BE/BackgroundService/appsettings.json new file mode 100644 index 0000000..6b6aeba --- /dev/null +++ b/fullstack2026BE/BackgroundService/appsettings.json @@ -0,0 +1,16 @@ +{ + "AppName": "BackgroundService", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "CronJobs": { + "database-delete-logs-till60-days-ago-job": { + "JobClassName" : "DeleteLogsTill60DaysAgoJob", + "CronExpression": "0 0 1 */2 *", + "Enabled": true + } + } +} \ No newline at end of file diff --git a/fullstack2026BE/Data.Access.Contracts/Data.Access.Contracts.csproj.lscache b/fullstack2026BE/Data.Access.Contracts/Data.Access.Contracts.csproj.lscache index b71d541..d51bfac 100644 --- a/fullstack2026BE/Data.Access.Contracts/Data.Access.Contracts.csproj.lscache +++ b/fullstack2026BE/Data.Access.Contracts/Data.Access.Contracts.csproj.lscache @@ -53,6 +53,7 @@ TemporaryDependencyNodeTargetIdentifier=net9.0 /warnaserror+:NU1605,SYSLIB0011 [sourceFiles] +JobCalls/IJobActor.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs Data.Access.Contracts.AssemblyInfo.cs diff --git a/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs b/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs new file mode 100644 index 0000000..9e4b32e --- /dev/null +++ b/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs @@ -0,0 +1,11 @@ +namespace Data.Access.Contracts.JobCalls; + +public interface IJobActor +{ + /// + /// Method to remove logs from specific days ago. + /// + /// Till what time will the last logs be kept. + /// How many entities were deleted. + Task ClearLogsFromDaysAgoAsync(int daysAgo); +} \ No newline at end of file diff --git a/fullstack2026BE/Data.Access.Dapper/Data.Access.Dapper.csproj.lscache b/fullstack2026BE/Data.Access.Dapper/Data.Access.Dapper.csproj.lscache index 1d206ec..671f814 100644 --- a/fullstack2026BE/Data.Access.Dapper/Data.Access.Dapper.csproj.lscache +++ b/fullstack2026BE/Data.Access.Dapper/Data.Access.Dapper.csproj.lscache @@ -53,7 +53,10 @@ TemporaryDependencyNodeTargetIdentifier=net9.0 /warnaserror+:NU1605,SYSLIB0011 [sourceFiles] -Helpers/InsertHelper.cs +Helpers/ + InsertHelper.cs + SqlKataDbConnectionExtensions.cs +JobCalls/JobActor.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs Data.Access.Dapper.AssemblyInfo.cs diff --git a/fullstack2026BE/Infrastructure/Helpers/SqlKataDbConnectionExtensions.cs b/fullstack2026BE/Data.Access.Dapper/Helpers/SqlKataDbConnectionExtensions.cs similarity index 92% rename from fullstack2026BE/Infrastructure/Helpers/SqlKataDbConnectionExtensions.cs rename to fullstack2026BE/Data.Access.Dapper/Helpers/SqlKataDbConnectionExtensions.cs index d0a00c7..e4d7433 100644 --- a/fullstack2026BE/Infrastructure/Helpers/SqlKataDbConnectionExtensions.cs +++ b/fullstack2026BE/Data.Access.Dapper/Helpers/SqlKataDbConnectionExtensions.cs @@ -2,7 +2,7 @@ using SqlKata.Compilers; using SqlKata.Execution; -namespace Infrastructure.Helpers; +namespace Data.Access.Dapper.Helpers; public static class SqlKataDbConnectionExtensions { diff --git a/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs b/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs new file mode 100644 index 0000000..0bf0e7b --- /dev/null +++ b/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs @@ -0,0 +1,20 @@ +using Data.Access.Contracts.JobCalls; +using Data.Access.Dapper.Helpers; +using Infrastructure.DependencyInjection; +using Infrastructure.Helpers; +using SqlKata.Execution; + +namespace Data.Access.Dapper.JobCalls; + +public class JobActor : IJobActor, IScopedDependency +{ + public async Task ClearLogsFromDaysAgoAsync(int daysAgo) + { + var cutoffDate = DateTimeOffset.UtcNow.AddDays(-daysAgo); + await using var conn = await MainDb.OpenConnectionAsync(); + return await conn.SqlKata() + .Query("Logs") + .WhereDate("Timestamp", "<", cutoffDate) + .DeleteAsync(); + } +} \ No newline at end of file diff --git a/fullstack2026BE/Data.Access.Dapper/Repositories/NotificationRepository.cs b/fullstack2026BE/Data.Access.Dapper/Repositories/NotificationRepository.cs index 99cf061..f40ef8a 100644 --- a/fullstack2026BE/Data.Access.Dapper/Repositories/NotificationRepository.cs +++ b/fullstack2026BE/Data.Access.Dapper/Repositories/NotificationRepository.cs @@ -1,5 +1,6 @@ using Dapper; using Data.Access.Contracts.Repositories; +using Data.Access.Dapper.Helpers; using Data.Model.BusinessStructs; using Data.Model.Entities; using Data.Model.SignalR; diff --git a/fullstack2026BE/Infrastructure/Helpers/DependencyInjectionHelper.cs b/fullstack2026BE/Infrastructure/Helpers/DependencyInjectionHelper.cs index 5d3c97a..377c374 100644 --- a/fullstack2026BE/Infrastructure/Helpers/DependencyInjectionHelper.cs +++ b/fullstack2026BE/Infrastructure/Helpers/DependencyInjectionHelper.cs @@ -1,5 +1,6 @@ using System.Reflection; using Infrastructure.DependencyInjection; +using Infrastructure.Models; using Microsoft.Extensions.DependencyInjection; namespace Infrastructure.Helpers; @@ -13,14 +14,15 @@ public static class DependencyInjectionHelper typeof(ISingletonDependency) ]; - public static void ConfigureAppsDi(this IServiceCollection services) where T : class + public static void ConfigureAppsDi(this IServiceCollection services, string appName) where T : class { - var assemblies = LoadAllReferencedAssemblies(services); + var assemblies = LoadAllReferencedAssemblies(); var concreteTypes = assemblies .SelectMany(a => a.GetTypes()) .Where(t => t is { IsClass: true, IsAbstract: false }); - + + foreach (var type in concreteTypes) { if (typeof(IScopedDependency).IsAssignableFrom(type)) @@ -32,43 +34,30 @@ public static void ConfigureAppsDi(this IServiceCollection services) where T } } - private static List LoadAllReferencedAssemblies(IServiceCollection services) where T: class + private static List LoadAllReferencedAssemblies() { - // Start from the entry point assembly and recursively load all referenced assemblies. - // This ensures lazy-loaded project assemblies (BLL, Data.Access.Dapper, etc.) are included. - var loaded = new Dictionary(); - var queue = new Queue(); + // MSBuild copies all ProjectReference outputs to the same directory, so scanning it + // is equivalent to walking all transitive project references of the top-level assembly. + // This avoids relying on GetReferencedAssemblies(), which only includes assemblies whose + // types are directly used in code and would miss indirectly-referenced projects. + var result = new List(); - var entry = typeof(T).Assembly; - queue.Enqueue(entry); - while (queue.Count > 0) + foreach (var dll in Directory.GetFiles(AppContext.BaseDirectory, "*.dll")) { - var asm = queue.Dequeue(); - if (loaded.ContainsKey(asm.FullName!)) - continue; - - loaded[asm.FullName!] = asm; - - foreach (var refName in asm.GetReferencedAssemblies()) - { - // Only walk assemblies that look like project assemblies, - // skip framework / NuGet assemblies for performance - if (!loaded.ContainsKey(refName.FullName) && IsProjectAssembly(refName.Name)) - { - try { queue.Enqueue(Assembly.Load(refName)); } - catch { /* ignore assemblies that can't be loaded */ } - } - } + var name = Path.GetFileNameWithoutExtension(dll); + if (!IsAcceptableAssembly(name)) continue; + try { result.Add(Assembly.LoadFrom(dll)); } + catch { /* ignore assemblies that can't be loaded */ } } - return [.. loaded.Values]; + return result; } /// /// Returns true for assemblies that belong to this solution. /// Filters out used packages (like Newtonsoft, Dapper etc.) /// - private static bool IsProjectAssembly(string? name) + private static bool IsAcceptableAssembly(string? name) { if (string.IsNullOrEmpty(name)) return false; return !name.StartsWith("System", StringComparison.Ordinal) diff --git a/fullstack2026BE/Infrastructure/Infrastructure.csproj.lscache b/fullstack2026BE/Infrastructure/Infrastructure.csproj.lscache index 57233d2..8d7ef83 100644 --- a/fullstack2026BE/Infrastructure/Infrastructure.csproj.lscache +++ b/fullstack2026BE/Infrastructure/Infrastructure.csproj.lscache @@ -61,7 +61,6 @@ DependencyInjection/ Helpers/ DependencyInjectionHelper.cs MainDb.cs - SqlKataDbConnectionExtensions.cs StronglyTypedIdConverter.cs StronglyTypedIdHelper.cs StronglyTypedIdTypeHandler.cs @@ -71,6 +70,7 @@ Logging/ LoggerSetup.cs MsSqlLogSink.cs Models/ + ApplicationName.cs EUserRoles.cs LoggingErrorLevel.cs StronglyTypedId.cs diff --git a/fullstack2026BE/Infrastructure/Models/ApplicationName.cs b/fullstack2026BE/Infrastructure/Models/ApplicationName.cs new file mode 100644 index 0000000..44c7247 --- /dev/null +++ b/fullstack2026BE/Infrastructure/Models/ApplicationName.cs @@ -0,0 +1,7 @@ +namespace Infrastructure.Models; + +public static class ApplicationName +{ + public const string Tripster = nameof(Tripster); + public const string BackgroundService = nameof(BackgroundService); +} \ No newline at end of file diff --git a/fullstack2026BE/UnitTests/UnitTests.csproj.lscache b/fullstack2026BE/UnitTests/UnitTests.csproj.lscache index 19bc221..4fd20c6 100644 --- a/fullstack2026BE/UnitTests/UnitTests.csproj.lscache +++ b/fullstack2026BE/UnitTests/UnitTests.csproj.lscache @@ -370,6 +370,10 @@ obj/Debug/net9.0/ emptyfiles/4.4.0/lib/net7.0/EmptyFiles.dll fluentmigrator.abstractions/6.2.0/lib/netstandard2.0/FluentMigrator.Abstractions.dll fluentmigrator/6.2.0/lib/netstandard2.0/FluentMigrator.dll + hangfire.aspnetcore/1.8.23/lib/netcoreapp3.0/Hangfire.AspNetCore.dll + hangfire.core/1.8.23/lib/netstandard2.0/Hangfire.Core.dll + hangfire.netcore/1.8.23/lib/netstandard2.1/Hangfire.NetCore.dll + hangfire.sqlserver/1.8.23/lib/netstandard2.0/Hangfire.SqlServer.dll humanizer.core/2.14.1/lib/net6.0/Humanizer.dll microsoft.aspnetcore.authentication.jwtbearer/9.0.0/lib/net9.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll microsoft.aspnetcore.jsonpatch/9.0.0/lib/net9.0/Microsoft.AspNetCore.JsonPatch.dll diff --git a/fullstack2026BE/WebApp/TripAppBuilder.cs b/fullstack2026BE/WebApp/TripAppBuilder.cs index 3d95c9b..ba6feef 100644 --- a/fullstack2026BE/WebApp/TripAppBuilder.cs +++ b/fullstack2026BE/WebApp/TripAppBuilder.cs @@ -2,6 +2,8 @@ using BLL.SignalR.Hubs; using Data.Access.Contracts.Repositories; using Data.Access.Dapper.Repositories; +using Hangfire; +using Hangfire.SqlServer; using Infrastructure.Core; using Infrastructure.Helpers; using Infrastructure.Logging; @@ -24,11 +26,23 @@ public WebApplication Build() { var builder = WebApplication.CreateBuilder(args); + var appName = builder.Configuration["AppName"] ?? throw new NullReferenceException("No name set for application."); + var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("DefaultConnection connection string is missing."); MainDb.Initialize(() => new SqlConnection(connectionString)); + builder.Services.AddHangfire(config => config + .SetDataCompatibilityLevel(CompatibilityLevel.Version_180) + .UseSimpleAssemblyNameTypeSerializer() + .UseRecommendedSerializerSettings() + .UseSqlServerStorage(connectionString, new SqlServerStorageOptions + { + UseRecommendedIsolationLevel = true, + DisableGlobalLocks = true + })); + builder.Services.AddSignalR(); builder.Services.AddSingleton(); @@ -92,8 +106,7 @@ public WebApplication Build() }); // Add services to the container. // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi - ConfigureDi(builder); - builder.Services.AddScoped(); + ConfigureDi(builder, appName); builder.Services.AddControllers() .AddNewtonsoftJson(options => @@ -171,9 +184,9 @@ public WebApplication Build() return app; } - private static void ConfigureDi(WebApplicationBuilder builder) + private static void ConfigureDi(WebApplicationBuilder builder, string appName) { var services = builder.Services; - services.ConfigureAppsDi(); + services.ConfigureAppsDi(appName); } } \ No newline at end of file diff --git a/fullstack2026BE/WebApp/WebApp.csproj b/fullstack2026BE/WebApp/WebApp.csproj index 2eb4e1e..94c0585 100644 --- a/fullstack2026BE/WebApp/WebApp.csproj +++ b/fullstack2026BE/WebApp/WebApp.csproj @@ -8,6 +8,8 @@ + + diff --git a/fullstack2026BE/WebApp/WebApp.csproj.lscache b/fullstack2026BE/WebApp/WebApp.csproj.lscache index 99e74e5..d700540 100644 --- a/fullstack2026BE/WebApp/WebApp.csproj.lscache +++ b/fullstack2026BE/WebApp/WebApp.csproj.lscache @@ -382,6 +382,10 @@ TripAppBuilder.cs dapper/2.0.123/lib/net5.0/Dapper.dll fluentmigrator.abstractions/6.2.0/lib/netstandard2.0/FluentMigrator.Abstractions.dll fluentmigrator/6.2.0/lib/netstandard2.0/FluentMigrator.dll + hangfire.aspnetcore/1.8.23/lib/netcoreapp3.0/Hangfire.AspNetCore.dll + hangfire.core/1.8.23/lib/netstandard2.0/Hangfire.Core.dll + hangfire.netcore/1.8.23/lib/netstandard2.1/Hangfire.NetCore.dll + hangfire.sqlserver/1.8.23/lib/netstandard2.0/Hangfire.SqlServer.dll humanizer.core/2.14.1/lib/net6.0/Humanizer.dll microsoft.aspnetcore.authentication.jwtbearer/9.0.0/lib/net9.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll microsoft.aspnetcore.jsonpatch/9.0.0/lib/net9.0/Microsoft.AspNetCore.JsonPatch.dll diff --git a/fullstack2026BE/WebApp/appsettings.json b/fullstack2026BE/WebApp/appsettings.json index 28bad1a..d8f76ae 100644 --- a/fullstack2026BE/WebApp/appsettings.json +++ b/fullstack2026BE/WebApp/appsettings.json @@ -1,4 +1,5 @@ { + "AppName": "Tripster", "Logging": { "LogLevel": { "Default": "Information", diff --git a/fullstack2026BE/fullstack2026BE.csproj.lscache b/fullstack2026BE/fullstack2026BE.csproj.lscache index 676d182..91d854b 100644 --- a/fullstack2026BE/fullstack2026BE.csproj.lscache +++ b/fullstack2026BE/fullstack2026BE.csproj.lscache @@ -53,32 +53,53 @@ TemporaryDependencyNodeTargetIdentifier=net9.0 /warnaserror+:NU1605,SYSLIB0011 [sourceFiles] -BLL/obj/Debug/net9.0/ - .NETCoreApp,Version=v9.0.AssemblyAttributes.cs - BLL.AssemblyInfo.cs - BLL.GlobalUsings.g.cs -BLL/Services/ - AcquaintanceService.cs - Auth/ - AuthService.cs - IAuthService.cs - IAcquaintanceService.cs - IUserService.cs - Notifications/ - INotificationService.cs - NotificationService.cs - UserService.cs -BLL/SignalR/Hubs/MainHub.cs -Data.Access.Contracts/obj/Debug/net9.0/ - .NETCoreApp,Version=v9.0.AssemblyAttributes.cs - Data.Access.Contracts.AssemblyInfo.cs - Data.Access.Contracts.GlobalUsings.g.cs -Data.Access.Contracts/Repositories/ - IAcquaintanceRepository.cs - INotificationRepository.cs - IUserRepository.cs +BackgroundService/Config/ + CronJobConfig.cs + CronJobDispatcher.cs + CronJobRegistrar.cs + ICronJob.cs +BackgroundService/ + HangfireBuilder.cs + Jobs/ + DeleteLogsTill60DaysAgoJob.cs + DeletesExpiredRefreshTokensJob.cs + obj/Debug/net9.0/ + .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + BackgroundService.AssemblyInfo.cs + BackgroundService.GlobalUsings.g.cs + Program.cs +BLL/ + Jobs/INotificationJob.cs + obj/Debug/net9.0/ + .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + BLL.GlobalUsings.g.cs + Services/ + AcquaintanceService.cs + Auth/ + AuthService.cs + IAuthService.cs + IAcquaintanceService.cs + IUserService.cs + Notifications/ + INotificationService.cs + NotificationService.cs + UserService.cs + SignalR/Hubs/MainHub.cs +Data.Access.Contracts/ + JobCalls/IJobActor.cs + obj/Debug/net9.0/ + .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + Data.Access.Contracts.AssemblyInfo.cs + Data.Access.Contracts.GlobalUsings.g.cs + Repositories/ + IAcquaintanceRepository.cs + INotificationRepository.cs + IUserRepository.cs +Data.Access.Dapper/Helpers/ + InsertHelper.cs + SqlKataDbConnectionExtensions.cs Data.Access.Dapper/ - Helpers/InsertHelper.cs + JobCalls/JobActor.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs Data.Access.Dapper.AssemblyInfo.cs @@ -125,7 +146,6 @@ DbScripts/Migrations/2026/ 202604251200-AddRolesToUsers.cs DbScripts/obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs - DbScripts.AssemblyInfo.cs DbScripts.GlobalUsings.g.cs Infrastructure/ Core/IAppBuilder.cs @@ -136,7 +156,6 @@ Infrastructure/ Helpers/ DependencyInjectionHelper.cs MainDb.cs - SqlKataDbConnectionExtensions.cs StronglyTypedIdConverter.cs StronglyTypedIdHelper.cs StronglyTypedIdTypeHandler.cs @@ -146,6 +165,7 @@ Infrastructure/ LoggerSetup.cs MsSqlLogSink.cs Models/ + ApplicationName.cs EUserRoles.cs LoggingErrorLevel.cs StronglyTypedId.cs @@ -160,7 +180,6 @@ obj/Debug/net9.0/ fullstack2026BE.GlobalUsings.g.cs SignalRTester/obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs - SignalRTester.AssemblyInfo.cs SignalRTester.GlobalUsings.g.cs SignalRTester/Program.cs Temp/obj/Debug/net9.0/ @@ -169,7 +188,6 @@ Temp/obj/Debug/net9.0/ Temp.GlobalUsings.g.cs Tools/MigrationsCli/obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs - MigrationsCli.AssemblyInfo.cs MigrationsCli.GlobalUsings.g.cs Tools/MigrationsCli/Program.cs UnitTests/ @@ -178,7 +196,6 @@ UnitTests/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs UniTests.AssemblyInfo.cs UniTests.GlobalUsings.g.cs - UnitTests.AssemblyInfo.cs UnitTests.GlobalUsings.g.cs WebApp/ Configs/DapperConfig.cs @@ -200,9 +217,7 @@ WebApp/ RegisterRequest.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs - WebApp.AssemblyInfo.cs WebApp.GlobalUsings.g.cs - WebApp.MvcApplicationPartsAssemblyInfo.cs Program.cs TripAppBuilder.cs diff --git a/fullstack2026BE/fullstack2026BE.sln b/fullstack2026BE/fullstack2026BE.sln index 4e0f229..0030b1c 100644 --- a/fullstack2026BE/fullstack2026BE.sln +++ b/fullstack2026BE/fullstack2026BE.sln @@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data.Access.Contracts", "Da EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data.Access.Dapper", "Data.Access.Dapper\Data.Access.Dapper.csproj", "{745F26CD-F5DC-427B-B765-B0161F3AD93E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackgroundService", "BackgroundService\BackgroundService.csproj", "{475D6F60-C203-4349-A229-1D53676CA402}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -71,5 +73,9 @@ Global {745F26CD-F5DC-427B-B765-B0161F3AD93E}.Debug|Any CPU.Build.0 = Debug|Any CPU {745F26CD-F5DC-427B-B765-B0161F3AD93E}.Release|Any CPU.ActiveCfg = Release|Any CPU {745F26CD-F5DC-427B-B765-B0161F3AD93E}.Release|Any CPU.Build.0 = Release|Any CPU + {475D6F60-C203-4349-A229-1D53676CA402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {475D6F60-C203-4349-A229-1D53676CA402}.Debug|Any CPU.Build.0 = Debug|Any CPU + {475D6F60-C203-4349-A229-1D53676CA402}.Release|Any CPU.ActiveCfg = Release|Any CPU + {475D6F60-C203-4349-A229-1D53676CA402}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From e98053f7835d73804032d041bb888a2ec266b36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Kuusik?= Date: Sat, 30 May 2026 21:14:34 +0300 Subject: [PATCH 2/3] Logging made to db fix --- fullstack2026BE/Infrastructure/Logging/Logger.cs | 3 +-- fullstack2026BE/Infrastructure/Logging/MsSqlLogSink.cs | 10 +++------- .../WebApp/Controllers/v1/BaseController.cs | 1 - fullstack2026BE/fullstack2026BE.csproj.lscache | 4 ++++ 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/fullstack2026BE/Infrastructure/Logging/Logger.cs b/fullstack2026BE/Infrastructure/Logging/Logger.cs index 43045db..48a3c61 100644 --- a/fullstack2026BE/Infrastructure/Logging/Logger.cs +++ b/fullstack2026BE/Infrastructure/Logging/Logger.cs @@ -29,6 +29,7 @@ public static void LogBasedOnStatus(LoggingErrorLevel errorLevel, string message switch (errorLevel) { case LoggingErrorLevel.Info: + Info(message, args); break; case LoggingErrorLevel.Warning: Warning(message, args); @@ -37,8 +38,6 @@ public static void LogBasedOnStatus(LoggingErrorLevel errorLevel, string message case LoggingErrorLevel.Critical: Error(message, ex, args); break; - default: - break; } } } \ No newline at end of file diff --git a/fullstack2026BE/Infrastructure/Logging/MsSqlLogSink.cs b/fullstack2026BE/Infrastructure/Logging/MsSqlLogSink.cs index b50f22c..1adcf47 100644 --- a/fullstack2026BE/Infrastructure/Logging/MsSqlLogSink.cs +++ b/fullstack2026BE/Infrastructure/Logging/MsSqlLogSink.cs @@ -12,13 +12,13 @@ public LoggerConfiguration Apply(LoggerConfiguration configuration) { TimeStamp = { - ColumnName = "CreatedAt", + ColumnName = "Timestamp", DataType = SqlDbType.DateTimeOffset }, Level = { ColumnName = "Level", - StoreAsEnum = true // stores as int, matches your int column + StoreAsEnum = false }, Message = { @@ -27,19 +27,15 @@ public LoggerConfiguration Apply(LoggerConfiguration configuration) Exception = { ColumnName = "Exception" - }, - MessageTemplate = - { - ColumnName = "MessageTemplate" } }; columnOptions.Store.Remove(StandardColumn.Properties); + columnOptions.Store.Remove(StandardColumn.MessageTemplate); columnOptions.AdditionalColumns = [ new SqlColumn { ColumnName = "ExceptionType", DataType = SqlDbType.NVarChar, DataLength = 256, AllowNull = true }, - new SqlColumn { ColumnName = "StackTrace", DataType = SqlDbType.NVarChar, DataLength = -1, AllowNull = true }, new SqlColumn { ColumnName = "CorrelationId", DataType = SqlDbType.NVarChar, DataLength = 128, AllowNull = true }, new SqlColumn { ColumnName = "UserId", DataType = SqlDbType.NVarChar, DataLength = 128, AllowNull = true }, new SqlColumn { ColumnName = "MachineName", DataType = SqlDbType.NVarChar, DataLength = 256, AllowNull = true }, diff --git a/fullstack2026BE/WebApp/Controllers/v1/BaseController.cs b/fullstack2026BE/WebApp/Controllers/v1/BaseController.cs index 4ee2e34..3cbb761 100644 --- a/fullstack2026BE/WebApp/Controllers/v1/BaseController.cs +++ b/fullstack2026BE/WebApp/Controllers/v1/BaseController.cs @@ -1,6 +1,5 @@ using Data.Model.BusinessStructs; using Data.Model.Utils; -using Infrastructure.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using WebApp.Helpers; diff --git a/fullstack2026BE/fullstack2026BE.csproj.lscache b/fullstack2026BE/fullstack2026BE.csproj.lscache index 91d854b..b859832 100644 --- a/fullstack2026BE/fullstack2026BE.csproj.lscache +++ b/fullstack2026BE/fullstack2026BE.csproj.lscache @@ -72,6 +72,7 @@ BLL/ Jobs/INotificationJob.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + BLL.AssemblyInfo.cs BLL.GlobalUsings.g.cs Services/ AcquaintanceService.cs @@ -146,6 +147,7 @@ DbScripts/Migrations/2026/ 202604251200-AddRolesToUsers.cs DbScripts/obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + DbScripts.AssemblyInfo.cs DbScripts.GlobalUsings.g.cs Infrastructure/ Core/IAppBuilder.cs @@ -217,7 +219,9 @@ WebApp/ RegisterRequest.cs obj/Debug/net9.0/ .NETCoreApp,Version=v9.0.AssemblyAttributes.cs + WebApp.AssemblyInfo.cs WebApp.GlobalUsings.g.cs + WebApp.MvcApplicationPartsAssemblyInfo.cs Program.cs TripAppBuilder.cs From 6ad1762491c0b02820636a4a6d2182b13bb87e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Kuusik?= Date: Sat, 30 May 2026 21:29:10 +0300 Subject: [PATCH 3/3] Extra job for refreshTokens --- ...gsTill60DaysAgoJob.cs => DeleteLogsFromPastJob.cs} | 6 +++--- .../Jobs/DeletesExpiredRefreshTokensJob.cs | 11 ++++++++--- .../BackgroundService/appsettings.Development.json | 2 +- fullstack2026BE/BackgroundService/appsettings.json | 7 +++++-- .../Data.Access.Contracts/JobCalls/IJobActor.cs | 7 +++++++ .../Data.Access.Dapper/JobCalls/JobActor.cs | 10 ++++++++++ 6 files changed, 34 insertions(+), 9 deletions(-) rename fullstack2026BE/BackgroundService/Jobs/{DeleteLogsTill60DaysAgoJob.cs => DeleteLogsFromPastJob.cs} (55%) diff --git a/fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs b/fullstack2026BE/BackgroundService/Jobs/DeleteLogsFromPastJob.cs similarity index 55% rename from fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs rename to fullstack2026BE/BackgroundService/Jobs/DeleteLogsFromPastJob.cs index 9d126c5..668cc11 100644 --- a/fullstack2026BE/BackgroundService/Jobs/DeleteLogsTill60DaysAgoJob.cs +++ b/fullstack2026BE/BackgroundService/Jobs/DeleteLogsFromPastJob.cs @@ -7,13 +7,13 @@ namespace BackgroundService.Jobs; [AutomaticRetry(Attempts = 2)] -public class DeleteLogsTill60DaysAgoJob(IJobActor jobActor) : ICronJob, IScopedDependency +public class DeleteLogsFromPastJob(IJobActor jobActor) : ICronJob, IScopedDependency { - public string JobId => "database-delete-logs-till60-days-ago-job"; + public string JobId => "database-delete-logs-from-past-job"; public async Task ExecuteAsync() { var entitiesDeleted = await jobActor.ClearLogsFromDaysAgoAsync(60); - Logger.Info($"Database logging cleanup job executed at {DateTimeOffset.UtcNow}, entities deleted: {entitiesDeleted}"); + Logger.Info($"Job {JobId} performed at {DateTimeOffset.UtcNow}, entities deleted: {entitiesDeleted}"); } } \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs b/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs index db4a8c1..9e626bd 100644 --- a/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs +++ b/fullstack2026BE/BackgroundService/Jobs/DeletesExpiredRefreshTokensJob.cs @@ -1,13 +1,18 @@ using BackgroundService.Config; +using Data.Access.Contracts.JobCalls; +using Hangfire; using Infrastructure.DependencyInjection; +using Infrastructure.Logging; namespace BackgroundService.Jobs; -public class DeletesExpiredRefreshTokensJob : ICronJob, IScopedDependency +[AutomaticRetry(Attempts = 1)] +public class DeletesExpiredRefreshTokensJob(IJobActor jobActor) : ICronJob, IScopedDependency { public string JobId => "database-delete-expired-refresh-tokens-job"; - public Task ExecuteAsync() + public async Task ExecuteAsync() { - throw new NotImplementedException(); + var entitiesDeleted = await jobActor.ClearLogsFromDaysAgoAsync(60); + Logger.Info($"Job {JobId} performed at {DateTimeOffset.UtcNow}, entities deleted: {entitiesDeleted}"); } } \ No newline at end of file diff --git a/fullstack2026BE/BackgroundService/appsettings.Development.json b/fullstack2026BE/BackgroundService/appsettings.Development.json index 7975622..ff93df1 100644 --- a/fullstack2026BE/BackgroundService/appsettings.Development.json +++ b/fullstack2026BE/BackgroundService/appsettings.Development.json @@ -9,7 +9,7 @@ "DefaultConnection": "Server=localhost,1433;Database=FullStackApp;User Id=sa;Password=jnwKUghLAUWGh3t73t;TrustServerCertificate=True" }, "CronJobs": { - "database-cleanup-daily": { + "database-delete-logs-from-past-job": { "CronExpression": "0 2 * * *", "Enabled": true } diff --git a/fullstack2026BE/BackgroundService/appsettings.json b/fullstack2026BE/BackgroundService/appsettings.json index 6b6aeba..c91cf70 100644 --- a/fullstack2026BE/BackgroundService/appsettings.json +++ b/fullstack2026BE/BackgroundService/appsettings.json @@ -7,10 +7,13 @@ } }, "CronJobs": { - "database-delete-logs-till60-days-ago-job": { - "JobClassName" : "DeleteLogsTill60DaysAgoJob", + "database-delete-logs-from-past-job": { "CronExpression": "0 0 1 */2 *", "Enabled": true + }, + "database-delete-expired-refresh-tokens-job": { + "CronExpression": "0 0 1 * *", + "Enabled": true } } } \ No newline at end of file diff --git a/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs b/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs index 9e4b32e..199c458 100644 --- a/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs +++ b/fullstack2026BE/Data.Access.Contracts/JobCalls/IJobActor.cs @@ -8,4 +8,11 @@ public interface IJobActor /// Till what time will the last logs be kept. /// How many entities were deleted. Task ClearLogsFromDaysAgoAsync(int daysAgo); + + /// + /// Method to delete refresh tokens created before specific days ago. + /// + /// + /// + Task ClearRefreshTokensAsync(int daysAgo); } \ No newline at end of file diff --git a/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs b/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs index 0bf0e7b..190544c 100644 --- a/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs +++ b/fullstack2026BE/Data.Access.Dapper/JobCalls/JobActor.cs @@ -17,4 +17,14 @@ public async Task ClearLogsFromDaysAgoAsync(int daysAgo) .WhereDate("Timestamp", "<", cutoffDate) .DeleteAsync(); } + + public async Task ClearRefreshTokensAsync(int daysAgo) + { + var cutoffDate = DateTimeOffset.UtcNow.AddDays(-daysAgo); + await using var conn = await MainDb.OpenConnectionAsync(); + return await conn.SqlKata() + .Query("RefreshTokens") + .WhereDate("CreatedAt", "<", cutoffDate) + .DeleteAsync(); + } } \ No newline at end of file