Release v1.0.0 #16

Merged
Wesser merged 492 commits from release/v1.0.0 into master 2025-02-12 09:19:32 +03:00
249 changed files with 11451 additions and 98 deletions
Showing only changes of commit 80e74b34c1 - Show all commits
.env
.gitea/workflows
.gitignore
ApiDto
Backend.slnDockerfile
Endpoint
Backend.http
Common
Configuration
Controllers
Endpoint.csprojProgram.cs
Sync
WeatherForecast.cs
wwwroot
css
README.md
Security
SqlData
Application
Application.csproj
Common
Cqrs
Campus
Discipline
Faculty
Group
LectureHall
Professor
Schedule
DependencyInjection.cs
Interfaces
Domain
Migrations
Persistence
nuget.config

@ -0,0 +1,15 @@
using System;
namespace Mirea.Api.Endpoint.Common.Services;
public static class ScheduleSyncManager
{
public static event Action? OnUpdateIntervalRequested;
public static event Action? OnForceSyncRequested;
public static void RequestIntervalUpdate() =>
OnUpdateIntervalRequested?.Invoke();
public static void RequestForceSync() =>
OnForceSyncRequested?.Invoke();
}

@ -0,0 +1,121 @@
using Cronos;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Sync;
using System;
using System.Threading;
using System.Threading.Tasks;
using IServiceProvider = System.IServiceProvider;
namespace Mirea.Api.Endpoint.Configuration.Core.BackgroundTasks;
public class ScheduleSyncService : IHostedService, IDisposable
{
private Timer? _timer;
private readonly IOptionsMonitor<GeneralConfig> _generalConfigMonitor;
private readonly ILogger<ScheduleSyncService> _logger;
private CancellationTokenSource _cancellationTokenSource = new();
private readonly IServiceProvider _serviceProvider;
public ScheduleSyncService(IOptionsMonitor<GeneralConfig> generalConfigMonitor, ILogger<ScheduleSyncService> logger, IServiceProvider serviceProvider)
{
_generalConfigMonitor = generalConfigMonitor;
_logger = logger;
_serviceProvider = serviceProvider;
ScheduleSyncManager.OnForceSyncRequested += OnForceSyncRequested;
ScheduleSyncManager.OnUpdateIntervalRequested += OnUpdateIntervalRequested;
}
private void OnForceSyncRequested()
{
StopAsync(default).ContinueWith(_ =>
{
_cancellationTokenSource = new CancellationTokenSource();
ExecuteTask(null);
});
}
private void OnUpdateIntervalRequested()
{
StopAsync(default).ContinueWith(_ =>
{
StartAsync(default);
});
}
private void ScheduleNextRun()
{
var cronExpression = _generalConfigMonitor.CurrentValue.ScheduleSettings?.CronUpdateSchedule;
if (string.IsNullOrEmpty(cronExpression))
{
_logger.LogWarning("Cron expression is not set. The scheduled task will not run.");
return;
}
var nextRunTime = CronExpression.Parse(cronExpression).GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);
if (!nextRunTime.HasValue)
{
_logger.LogWarning("No next run time found. The task will not be scheduled. Timezone: {TimeZone}", TimeZoneInfo.Local.DisplayName);
return;
}
_logger.LogInformation("Next task run in {Time}", nextRunTime.Value.ToString("G"));
var delay = (nextRunTime.Value - DateTimeOffset.Now).TotalMilliseconds;
// The chance is small, but it's better to check
if (delay <= 0)
delay = 1;
_cancellationTokenSource = new CancellationTokenSource();
_timer = new Timer(ExecuteTask, null, (int)delay, Timeout.Infinite);
}
private async void ExecuteTask(object? state)
{
try
{
using var scope = _serviceProvider.CreateScope();
var syncService = ActivatorUtilities.GetServiceOrCreateInstance<ScheduleSynchronizer>(scope.ServiceProvider);
await syncService.StartSync(_cancellationTokenSource.Token);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred during schedule synchronization.");
}
finally
{
ScheduleNextRun();
}
}
public Task StartAsync(CancellationToken cancellationToken)
{
ScheduleNextRun();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_cancellationTokenSource.Cancel();
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
StopAsync(default).GetAwaiter().GetResult();
_timer?.Dispose();
ScheduleSyncManager.OnForceSyncRequested -= OnForceSyncRequested;
ScheduleSyncManager.OnUpdateIntervalRequested -= OnUpdateIntervalRequested;
_cancellationTokenSource.Dispose();
GC.SuppressFinalize(this);
}
}

@ -5,9 +5,9 @@
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<Company>Winsomnia</Company>
<Version>1.0-rc3</Version>
<AssemblyVersion>1.0.2.3</AssemblyVersion>
<FileVersion>1.0.2.3</FileVersion>
<Version>1.0-rc4</Version>
<AssemblyVersion>1.0.2.4</AssemblyVersion>
<FileVersion>1.0.2.4</FileVersion>
<AssemblyName>Mirea.Api.Endpoint</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<OutputType>Exe</OutputType>

@ -10,6 +10,7 @@ using Mirea.Api.DataAccess.Persistence;
using Mirea.Api.DataAccess.Persistence.Common;
using Mirea.Api.Endpoint.Common.Interfaces;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.Core.BackgroundTasks;
using Mirea.Api.Endpoint.Configuration.Core.Middleware;
using Mirea.Api.Endpoint.Configuration.Core.Startup;
using Mirea.Api.Endpoint.Configuration.Model;
@ -63,6 +64,8 @@ public class Program
builder.Services.AddSingleton<IMaintenanceModeService, MaintenanceModeService>();
builder.Services.AddSingleton<ISetupToken, SetupTokenService>();
builder.Services.AddHostedService<ScheduleSyncService>();
builder.Services.AddMemoryCache();
builder.Services.AddCustomRedis(builder.Configuration, healthCheckBuilder);