Compare commits
2 Commits
8fe37ff9d7
...
ea254c1c02
Author | SHA1 | Date | |
---|---|---|---|
ea254c1c02 | |||
5f52e16dbd |
@@ -1,10 +1,12 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using SfeduSchedule.Services;
|
using SfeduSchedule.Services;
|
||||||
|
|
||||||
namespace SfeduSchedule.Controllers
|
namespace SfeduSchedule.Controllers
|
||||||
{
|
{
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
|
[EnableRateLimiting("throttle")]
|
||||||
public class ScheduleController(ModeusService modeusService) : ControllerBase
|
public class ScheduleController(ModeusService modeusService) : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -13,6 +15,7 @@ namespace SfeduSchedule.Controllers
|
|||||||
/// <param name="attendeePersonId">Список GUID пользователей, для которых запрашивается расписание.</param>
|
/// <param name="attendeePersonId">Список GUID пользователей, для которых запрашивается расписание.</param>
|
||||||
/// <returns>Список событий расписания.</returns>
|
/// <returns>Список событий расписания.</returns>
|
||||||
/// <response code="200">Возвращает расписание</response>
|
/// <response code="200">Возвращает расписание</response>
|
||||||
|
/// <response code="429">Слишком много запросов</response>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("test")]
|
[Route("test")]
|
||||||
public async Task<IActionResult> Get([FromQuery] List<Guid> attendeePersonId, [FromQuery] DateTime? startDate, [FromQuery] DateTime? endDate)
|
public async Task<IActionResult> Get([FromQuery] List<Guid> attendeePersonId, [FromQuery] DateTime? startDate, [FromQuery] DateTime? endDate)
|
||||||
@@ -31,6 +34,7 @@ namespace SfeduSchedule.Controllers
|
|||||||
/// <param name="request">Объект запроса, содержащий параметры фильтрации расписания.</param>
|
/// <param name="request">Объект запроса, содержащий параметры фильтрации расписания.</param>
|
||||||
/// <returns>Список событий расписания.</returns>
|
/// <returns>Список событий расписания.</returns>
|
||||||
/// <response code="200">Возвращает расписание</response>
|
/// <response code="200">Возвращает расписание</response>
|
||||||
|
/// <response code="429">Слишком много запросов</response>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Post([FromBody] ModeusScheduleRequest request)
|
public async Task<IActionResult> Post([FromBody] ModeusScheduleRequest request)
|
||||||
{
|
{
|
||||||
|
@@ -1,13 +1,17 @@
|
|||||||
|
using System.Threading.RateLimiting;
|
||||||
using Microsoft.Identity.Web;
|
using Microsoft.Identity.Web;
|
||||||
using Quartz;
|
using Quartz;
|
||||||
using SfeduSchedule;
|
using SfeduSchedule;
|
||||||
using SfeduSchedule.Jobs;
|
using SfeduSchedule.Jobs;
|
||||||
using SfeduSchedule.Services;
|
using SfeduSchedule.Services;
|
||||||
|
using X.Extensions.Logging.Telegram.Extensions;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
var configuration = builder.Configuration;
|
var configuration = builder.Configuration;
|
||||||
string? preinstsalledJwtToken = configuration["TOKEN"];
|
string? preinstalledJwtToken = configuration["TOKEN"];
|
||||||
|
string? tgChatId = configuration["TG_CHAT_ID"];
|
||||||
|
string? tgToken = configuration["TG_TOKEN"];
|
||||||
string updateJwtCron = configuration["UPDATE_JWT_CRON"] ?? "0 4 * ? * *";
|
string updateJwtCron = configuration["UPDATE_JWT_CRON"] ?? "0 4 * ? * *";
|
||||||
|
|
||||||
// создать папку data если не существует
|
// создать папку data если не существует
|
||||||
@@ -16,9 +20,27 @@ if (!Directory.Exists(dataDirectory))
|
|||||||
{
|
{
|
||||||
Directory.CreateDirectory(dataDirectory);
|
Directory.CreateDirectory(dataDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalVariables.JwtFilePath = Path.Combine(dataDirectory, "jwt.txt");
|
GlobalVariables.JwtFilePath = Path.Combine(dataDirectory, "jwt.txt");
|
||||||
|
|
||||||
builder.Services.AddOpenApi();
|
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
builder.Logging.AddConsole();
|
||||||
|
if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken))
|
||||||
|
builder.Logging.AddTelegram(options =>
|
||||||
|
{
|
||||||
|
options.ChatId = tgChatId;
|
||||||
|
options.AccessToken = tgToken;
|
||||||
|
options.FormatterConfiguration.UseEmoji = true;
|
||||||
|
options.FormatterConfiguration.ReadableApplicationName = "Sfedu Schedule";
|
||||||
|
options.LogLevel = new Dictionary<string, LogLevel>
|
||||||
|
{
|
||||||
|
{ "Default", LogLevel.Error },
|
||||||
|
{ "SfeduSchedule.Jobs.UpdateJwtJob", LogLevel.Information },
|
||||||
|
{ "Program", LogLevel.Information }
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddHttpClient<ModeusService>();
|
builder.Services.AddHttpClient<ModeusService>();
|
||||||
|
|
||||||
@@ -26,7 +48,7 @@ builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration)
|
|||||||
|
|
||||||
var jobKey = new JobKey("UpdateJWTJob");
|
var jobKey = new JobKey("UpdateJWTJob");
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(preinstsalledJwtToken))
|
if (string.IsNullOrEmpty(preinstalledJwtToken))
|
||||||
{
|
{
|
||||||
builder.Services.AddQuartz(q =>
|
builder.Services.AddQuartz(q =>
|
||||||
{
|
{
|
||||||
@@ -38,7 +60,7 @@ if (string.IsNullOrEmpty(preinstsalledJwtToken))
|
|||||||
.WithCronSchedule(updateJwtCron)
|
.WithCronSchedule(updateJwtCron)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
|
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,13 +72,38 @@ builder.Services.AddSwaggerGen(options =>
|
|||||||
options.IncludeXmlComments(xmlPath);
|
options.IncludeXmlComments(xmlPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
builder.Services.AddRateLimiter(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("throttle", httpContext =>
|
||||||
|
RateLimitPartition.GetFixedWindowLimiter(
|
||||||
|
partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
|
||||||
|
factory: _ => new FixedWindowRateLimiterOptions
|
||||||
|
{
|
||||||
|
PermitLimit = 20,
|
||||||
|
Window = TimeSpan.FromSeconds(10)
|
||||||
|
}));
|
||||||
|
|
||||||
app.UseForwardedHeaders();
|
options.OnRejected = async (context, cancellationToken) =>
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
|
||||||
|
context.HttpContext.Response.Headers["Retry-After"] = "60";
|
||||||
|
|
||||||
|
await context.HttpContext.Response.WriteAsync("Rate limit exceeded. Please try again later.",
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
var reqLogger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Program>>();
|
||||||
|
reqLogger.LogWarning("Rate limit exceeded for IP: {IpAddress}",
|
||||||
|
context.HttpContext.Connection.RemoteIpAddress);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(preinstsalledJwtToken))
|
app.UseForwardedHeaders();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(preinstalledJwtToken))
|
||||||
{
|
{
|
||||||
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
||||||
var scheduler = await schedulerFactory.GetScheduler();
|
var scheduler = await schedulerFactory.GetScheduler();
|
||||||
@@ -83,7 +130,8 @@ if (string.IsNullOrEmpty(preinstsalledJwtToken))
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogInformation("Файл jwt.txt не содержит дату истечения или она некорректна, выполняем обновление токена");
|
logger.LogInformation(
|
||||||
|
"Файл jwt.txt не содержит дату истечения или она некорректна, выполняем обновление токена");
|
||||||
await scheduler.TriggerJob(jobKey);
|
await scheduler.TriggerJob(jobKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,10 +154,11 @@ app.UseStaticFiles();
|
|||||||
app.MapGet("/", async context =>
|
app.MapGet("/", async context =>
|
||||||
{
|
{
|
||||||
context.Response.ContentType = "text/html; charset=utf-8";
|
context.Response.ContentType = "text/html; charset=utf-8";
|
||||||
await context.Response.SendFileAsync(Path.Combine(app.Environment.WebRootPath ?? "wwwroot", "index.html"));
|
await context.Response.SendFileAsync(Path.Combine(app.Environment.WebRootPath, "index.html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.UseRateLimiter();
|
||||||
|
|
||||||
|
app.Run();
|
@@ -15,6 +15,7 @@
|
|||||||
<PackageReference Include="Microsoft.Playwright" Version="1.55.0" />
|
<PackageReference Include="Microsoft.Playwright" Version="1.55.0" />
|
||||||
<PackageReference Include="Quartz.AspNetCore" Version="3.15.0" />
|
<PackageReference Include="Quartz.AspNetCore" Version="3.15.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" />
|
||||||
|
<PackageReference Include="X.Extensions.Logging.Telegram" Version="2.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -12,7 +12,9 @@ services:
|
|||||||
- AzureAd:CallbackPath=/signin-oidc
|
- AzureAd:CallbackPath=/signin-oidc
|
||||||
- MS_USERNAME=${MS_USERNAME}
|
- MS_USERNAME=${MS_USERNAME}
|
||||||
- MS_PASSWORD=${MS_PASSWORD}
|
- MS_PASSWORD=${MS_PASSWORD}
|
||||||
# - TOKEN=
|
- TG_CHAT_ID=${TG_CHAT_ID}
|
||||||
|
- TG_TOKEN=${TG_TOKEN}
|
||||||
|
# - TOKEN=${TOKEN}
|
||||||
volumes:
|
volumes:
|
||||||
- data:/app/data
|
- data:/app/data
|
||||||
restart: always
|
restart: always
|
||||||
|
@@ -10,9 +10,11 @@ services:
|
|||||||
- AzureAd:ClientSecret=
|
- AzureAd:ClientSecret=
|
||||||
- AzureAd:Domain=sfedu.onmicrosoft.com
|
- AzureAd:Domain=sfedu.onmicrosoft.com
|
||||||
- AzureAd:CallbackPath=/signin-oidc
|
- AzureAd:CallbackPath=/signin-oidc
|
||||||
- MS_USERNAME=
|
- MS_USERNAME=${MS_USERNAME}
|
||||||
- MS_PASSWORD=
|
- MS_PASSWORD=${MS_PASSWORD}
|
||||||
# - TOKEN=
|
- TG_CHAT_ID=${TG_CHAT_ID}
|
||||||
|
- TG_TOKEN=${TG_TOKEN}
|
||||||
|
# - TOKEN=${TOKEN}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
Reference in New Issue
Block a user