Compare commits
6 Commits
f7c8db4921
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c098e6430d | |||
| 3f30812d7a | |||
| bed42a83bf | |||
| 33814bb6f4 | |||
| daf3639038 | |||
| 8ba1aea46a |
@@ -11,7 +11,7 @@ namespace SfeduSchedule.Controllers;
|
||||
[ApiController]
|
||||
[Route("api/proxy")]
|
||||
[EnableRateLimiting("throttle")]
|
||||
public class ProxyController(ModeusService modeusService, ILogger<ScheduleController> logger) : ControllerBase
|
||||
public class ProxyController(ModeusService modeusService, ILogger<ProxyController> logger) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить расписание по пользовательскому запросу.
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using ModeusSchedule.Abstractions;
|
||||
using ModeusSchedule.Abstractions.DTO;
|
||||
using SfeduSchedule.Services;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ public class UpdateJwtJob(
|
||||
IConfiguration configuration,
|
||||
ILogger<UpdateJwtJob> logger,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ModeusHttpClient modeusHttpClient,
|
||||
ModeusService modeusService) : IJob
|
||||
{
|
||||
private const int MaxAttempts = 5; // Максимальное число попыток
|
||||
@@ -70,7 +71,7 @@ public class UpdateJwtJob(
|
||||
}
|
||||
|
||||
configuration["TOKEN"] = body.Jwt;
|
||||
modeusService.SetToken(body.Jwt);
|
||||
modeusHttpClient.SetToken(body.Jwt);
|
||||
await File.WriteAllTextAsync(GlobalConsts.JwtFilePath,
|
||||
body.Jwt + "\n" + DateTime.Now.ToString("O"), cts.Token);
|
||||
logger.LogInformation("JWT успешно обновлён");
|
||||
|
||||
124
SfeduSchedule/Logging/ConsoleFormatter.cs
Normal file
124
SfeduSchedule/Logging/ConsoleFormatter.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Logging.Console;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace SfeduSchedule.Logging;
|
||||
|
||||
public sealed class ConsoleFormatter : Microsoft.Extensions.Logging.Console.ConsoleFormatter, IDisposable
|
||||
{
|
||||
private readonly IDisposable? _optionsReloadToken;
|
||||
private ConsoleFormatterOptions _formatterOptions;
|
||||
|
||||
public ConsoleFormatter(IOptionsMonitor<ConsoleFormatterOptions> options)
|
||||
: base("CustomConsoleFormatter")
|
||||
{
|
||||
_optionsReloadToken = options.OnChange(ReloadLoggerOptions);
|
||||
_formatterOptions = options.CurrentValue;
|
||||
}
|
||||
|
||||
private void ReloadLoggerOptions(ConsoleFormatterOptions options)
|
||||
{
|
||||
_formatterOptions = options;
|
||||
}
|
||||
|
||||
public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider? scopeProvider, TextWriter textWriter)
|
||||
{
|
||||
var message = logEntry.Formatter(logEntry.State, logEntry.Exception);
|
||||
if (logEntry.Exception == null && message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Timestamp
|
||||
if (!string.IsNullOrEmpty(_formatterOptions.TimestampFormat))
|
||||
{
|
||||
textWriter.Write(DateTime.Now.ToString(_formatterOptions.TimestampFormat));
|
||||
}
|
||||
|
||||
// Level
|
||||
|
||||
// Нужно для удаления цвета в логах при перенаправлении вывода
|
||||
var useColor = _formatterOptions.ColorBehavior == LoggerColorBehavior.Enabled ||
|
||||
(_formatterOptions.ColorBehavior == LoggerColorBehavior.Default && !System.Console.IsOutputRedirected);
|
||||
|
||||
textWriter.Write(GetLogLevelString(logEntry.LogLevel, useColor));
|
||||
|
||||
// Write :
|
||||
textWriter.Write(":");
|
||||
|
||||
// TraceId
|
||||
var traceIdHolder = new TraceIdHolder();
|
||||
scopeProvider?.ForEachScope((scope, state) =>
|
||||
{
|
||||
if (scope is not IEnumerable<KeyValuePair<string, object>> props) return;
|
||||
foreach (var pair in props)
|
||||
{
|
||||
if (pair.Key == "TraceId")
|
||||
state.TraceId = pair.Value?.ToString();
|
||||
}
|
||||
}, traceIdHolder);
|
||||
|
||||
if (!string.IsNullOrEmpty(traceIdHolder.TraceId))
|
||||
{
|
||||
textWriter.Write($" [{traceIdHolder.TraceId}]");
|
||||
}
|
||||
|
||||
// Category
|
||||
textWriter.Write($" {logEntry.Category}: ");
|
||||
|
||||
// Message
|
||||
textWriter.WriteLine(message);
|
||||
|
||||
if (logEntry.Exception != null)
|
||||
{
|
||||
textWriter.WriteLine(logEntry.Exception.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLogLevelString(LogLevel logLevel, bool useColor)
|
||||
{
|
||||
var logLevelString = logLevel switch
|
||||
{
|
||||
LogLevel.Trace => "trce",
|
||||
LogLevel.Debug => "dbug",
|
||||
LogLevel.Information => "info",
|
||||
LogLevel.Warning => "warn",
|
||||
LogLevel.Error => "fail",
|
||||
LogLevel.Critical => "crit",
|
||||
_ => "unknown"
|
||||
};
|
||||
|
||||
if (!useColor)
|
||||
{
|
||||
return logLevelString;
|
||||
}
|
||||
|
||||
var color = logLevel switch
|
||||
{
|
||||
LogLevel.Trace => "\x1B[90m",
|
||||
LogLevel.Debug => "\x1B[37m",
|
||||
LogLevel.Information => "\x1B[32m",
|
||||
LogLevel.Warning => "\x1B[33m",
|
||||
LogLevel.Error => "\x1B[31m",
|
||||
LogLevel.Critical => "\x1B[41m\x1B[37m",
|
||||
_ => "\x1B[39m"
|
||||
};
|
||||
|
||||
return $"{color}{logLevelString}\x1B[0m";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_optionsReloadToken?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class ConsoleFormatterOptions : Microsoft.Extensions.Logging.Console.ConsoleFormatterOptions
|
||||
{
|
||||
public LoggerColorBehavior ColorBehavior { get; set; }
|
||||
}
|
||||
|
||||
internal class TraceIdHolder
|
||||
{
|
||||
public string? TraceId { get; set; }
|
||||
}
|
||||
46
SfeduSchedule/Logging/LoggerExtensions.cs
Normal file
46
SfeduSchedule/Logging/LoggerExtensions.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace SfeduSchedule.Logging;
|
||||
|
||||
public static class LoggerExtensions
|
||||
{
|
||||
public static void LogTraceHere(this ILogger logger, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogTrace("[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogDebugHere(this ILogger logger, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogDebug("[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogInformationHere(this ILogger logger, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogInformation("[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogWarningHere(this ILogger logger, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogWarning("[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogErrorHere(this ILogger logger, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogError("[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogErrorHere(this ILogger logger, Exception exception, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogError(exception, "[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogCriticalHere(this ILogger logger, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogCritical("[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
|
||||
public static void LogCriticalHere(this ILogger logger, Exception exception, string message, [CallerMemberName] string memberName = "")
|
||||
{
|
||||
logger.LogCritical(exception, "[{Member}] {Message}", memberName, message);
|
||||
}
|
||||
}
|
||||
34
SfeduSchedule/Middleware/CorrelationIdMiddleware.cs
Normal file
34
SfeduSchedule/Middleware/CorrelationIdMiddleware.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SfeduSchedule.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Middleware для добавления и обработки Correlation ID в HTTP запросах.
|
||||
/// Нужно для трассировки запросов.
|
||||
/// </summary>
|
||||
public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<CorrelationIdMiddleware> logger)
|
||||
{
|
||||
private const string HeaderName = "X-Correlation-ID";
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
// 1. Берём из заголовка, если клиент прислал
|
||||
if (!context.Request.Headers.TryGetValue(HeaderName, out var correlationId) ||
|
||||
string.IsNullOrWhiteSpace(correlationId))
|
||||
{
|
||||
// 2. Иначе используем Activity TraceId или TraceIdentifier
|
||||
var activityId = Activity.Current?.TraceId.ToString();
|
||||
correlationId = !string.IsNullOrEmpty(activityId)
|
||||
? activityId
|
||||
: context.TraceIdentifier;
|
||||
}
|
||||
|
||||
// Положим в Items, чтобы можно было достать из сервисов
|
||||
// context.Items[HeaderName] = correlationId.ToString();
|
||||
|
||||
// 3. Прокинем в ответ
|
||||
context.Response.Headers[HeaderName] = correlationId.ToString();
|
||||
|
||||
await next(context);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Reflection;
|
||||
using System.Threading.RateLimiting;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.Identity.Web;
|
||||
@@ -13,12 +14,14 @@ using Quartz;
|
||||
using SfeduSchedule;
|
||||
using SfeduSchedule.Auth;
|
||||
using SfeduSchedule.Jobs;
|
||||
using SfeduSchedule.Logging;
|
||||
using SfeduSchedule.Middleware;
|
||||
using SfeduSchedule.Services;
|
||||
using X.Extensions.Logging.Telegram.Extensions;
|
||||
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
#region Работа с конфигурацией
|
||||
|
||||
var configuration = builder.Configuration;
|
||||
var preinstalledJwtToken = configuration["TOKEN"];
|
||||
@@ -36,15 +39,29 @@ if (string.IsNullOrEmpty(configuration["MODEUS_URL"]))
|
||||
var permitLimit = int.TryParse(configuration["PERMIT_LIMIT"], out var parsedPermitLimit) ? parsedPermitLimit : 40;
|
||||
var timeLimit = int.TryParse(configuration["TIME_LIMIT"], out var parsedTimeLimit) ? parsedTimeLimit : 10;
|
||||
|
||||
// создать папку data если не существует
|
||||
#endregion
|
||||
|
||||
#region Работа с папкой данных
|
||||
// Создать папку data если не существует
|
||||
var dataDirectory = Path.Combine(AppContext.BaseDirectory, "data");
|
||||
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory);
|
||||
|
||||
GlobalConsts.JwtFilePath = Path.Combine(dataDirectory, "jwt.txt");
|
||||
var pluginsPath = Path.Combine(dataDirectory, "Plugins");
|
||||
|
||||
// Создать подкаталог для плагинов
|
||||
var pluginsPath = Path.Combine(dataDirectory, "Plugins");
|
||||
if (!Directory.Exists(pluginsPath)) Directory.CreateDirectory(pluginsPath);
|
||||
|
||||
// Создать подкаталог для ключей Data Protection
|
||||
var dataProtectionKeysDirectory = Path.Combine(dataDirectory, "keys");
|
||||
if (!Directory.Exists(dataProtectionKeysDirectory)) Directory.CreateDirectory(dataProtectionKeysDirectory);
|
||||
#endregion
|
||||
|
||||
#region Работа с логированием
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddConsole();
|
||||
builder.Logging.AddConsole(options => options.FormatterName = "CustomConsoleFormatter")
|
||||
.AddConsoleFormatter<ConsoleFormatter, ConsoleFormatterOptions>();
|
||||
|
||||
builder.Logging.AddFilter("Quartz", LogLevel.Warning);
|
||||
if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken))
|
||||
builder.Logging.AddTelegram(options =>
|
||||
@@ -52,7 +69,7 @@ if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken))
|
||||
options.ChatId = tgChatId;
|
||||
options.AccessToken = tgToken;
|
||||
options.FormatterConfiguration.UseEmoji = true;
|
||||
options.FormatterConfiguration.ReadableApplicationName = "Sfedu Schedule";
|
||||
options.FormatterConfiguration.ReadableApplicationName = "Modeus Schedule Proxy";
|
||||
options.LogLevel = new Dictionary<string, LogLevel>
|
||||
{
|
||||
{ "Default", LogLevel.Error },
|
||||
@@ -61,6 +78,7 @@ if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken))
|
||||
{ "Quartz", LogLevel.Warning }
|
||||
};
|
||||
});
|
||||
#endregion
|
||||
|
||||
// Включаем MVC контроллеры
|
||||
var mvcBuilder = builder.Services.AddControllers();
|
||||
@@ -68,6 +86,7 @@ builder.Services.AddHttpClient("modeus", client =>
|
||||
{
|
||||
client.BaseAddress = new Uri(configuration["MODEUS_URL"]!);
|
||||
});
|
||||
builder.Services.AddSingleton<ModeusHttpClient>();
|
||||
builder.Services.AddSingleton<ModeusService>();
|
||||
builder.Services.AddHttpClient("authClient");
|
||||
|
||||
@@ -218,6 +237,10 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); // 172.16.x.x - 172.31.x.x
|
||||
});
|
||||
|
||||
// Хранение ключей Data Protection в папке data
|
||||
builder.Services.AddDataProtection()
|
||||
.PersistKeysToFileSystem(new DirectoryInfo(dataProtectionKeysDirectory));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||
@@ -225,6 +248,9 @@ var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||
// Используем настройки из DI (Configure<ForwardedHeadersOptions>)
|
||||
app.UseForwardedHeaders();
|
||||
|
||||
// Корреляция логов по запросам
|
||||
app.UseMiddleware<CorrelationIdMiddleware>();
|
||||
|
||||
if (string.IsNullOrEmpty(preinstalledJwtToken))
|
||||
{
|
||||
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
||||
|
||||
127
SfeduSchedule/Services/ModeusHttpClient.cs
Normal file
127
SfeduSchedule/Services/ModeusHttpClient.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using ModeusSchedule.Abstractions;
|
||||
using ModeusSchedule.Abstractions.DTO;
|
||||
using SfeduSchedule.Logging;
|
||||
|
||||
namespace SfeduSchedule.Services;
|
||||
|
||||
public class ModeusHttpClient
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ModeusHttpClient> _logger;
|
||||
public ModeusHttpClient(IHttpClientFactory httpClientFactory,
|
||||
ILogger<ModeusHttpClient> logger,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_httpClient = httpClientFactory.CreateClient("modeus");
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
SetToken(_configuration["TOKEN"]); // Установка предустановленного токена при инициализации, на случай если нет возможности связи с AUTH сервисом
|
||||
}
|
||||
public void SetToken(string? token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)) {
|
||||
_logger.LogInformationHere("Предоставленный токен пустой.");
|
||||
return;
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Remove(HeaderNames.Authorization);
|
||||
_httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, $"Bearer {token}");
|
||||
}
|
||||
|
||||
public async Task<string?> GetScheduleAsync(ModeusScheduleRequest msr)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
$"schedule-calendar-v2/api/calendar/events/search?tz={_configuration["TZ"]!}");
|
||||
request.Content = new StringContent(JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions),
|
||||
Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
_logger.LogErrorHere($"Неуспешный статус при получении расписания: {response.StatusCode}, Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
return null;
|
||||
}
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Attendees>> GetAttendeesAsync(Guid eventId)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get,
|
||||
$"schedule-calendar-v2/api/calendar/events/{eventId}/attendees");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
_logger.LogErrorHere($"Неуспешный статус при получении расписания: {response.StatusCode}, eventId: {eventId}");
|
||||
}
|
||||
List<Attendees>? attendees;
|
||||
try
|
||||
{
|
||||
attendees = Attendees.FromJson(await response.Content.ReadAsStringAsync());
|
||||
return attendees;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogErrorHere(ex, "Deserialization failed.");
|
||||
}
|
||||
|
||||
return new List<Attendees>();
|
||||
}
|
||||
|
||||
public async Task<string?> SearchRoomsAsync(RoomSearchRequest requestDto)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "schedule-calendar-v2/api/campus/rooms/search");
|
||||
request.Content =
|
||||
new StringContent(JsonSerializer.Serialize(requestDto, GlobalConsts.JsonSerializerOptions),
|
||||
Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
_logger.LogErrorHere($"Неуспешный статус при получении расписания: {response.StatusCode}, Request: {JsonSerializer.Serialize(requestDto, GlobalConsts.JsonSerializerOptions)}");
|
||||
return null;
|
||||
}
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
public async Task<string?> GetGuidAsync(string fullName)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "schedule-calendar-v2/api/people/persons/search");
|
||||
request.Content = new StringContent(JsonSerializer.Serialize(new
|
||||
{
|
||||
fullName,
|
||||
sort = "+fullName",
|
||||
size = 10,
|
||||
page = 0
|
||||
}), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
|
||||
_logger.LogInformationHere($"Ответ получен: {response.StatusCode}");
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
_logger.LogErrorHere($"Неуспешный статус при получении расписания: {response.StatusCode}, Name: {fullName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
|
||||
string? personId;
|
||||
try
|
||||
{
|
||||
personId = JsonDocument.Parse(json).RootElement
|
||||
.GetProperty("_embedded")
|
||||
.GetProperty("persons")[0]
|
||||
.GetProperty("id")
|
||||
.GetString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogWarningHere($"Не удалось получить идентификатор пользователя. FullName={fullName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return personId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +1,29 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Ical.Net;
|
||||
using Ical.Net.CalendarComponents;
|
||||
using Ical.Net.DataTypes;
|
||||
using Ical.Net.Serialization;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using ModeusSchedule.Abstractions;
|
||||
using ModeusSchedule.Abstractions.DTO;
|
||||
using SfeduSchedule.Logging;
|
||||
|
||||
namespace SfeduSchedule.Services;
|
||||
|
||||
public class ModeusService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<ModeusService> _logger;
|
||||
|
||||
public ModeusService(IHttpClientFactory httpClientFactory,
|
||||
private readonly ILogger<ModeusService> _logger;
|
||||
private readonly ModeusHttpClient _modeusHttpClient;
|
||||
|
||||
public ModeusService(
|
||||
ILogger<ModeusService> logger,
|
||||
IConfiguration configuration)
|
||||
IConfiguration configuration,
|
||||
ModeusHttpClient modeusHttpClient)
|
||||
{
|
||||
_httpClient = httpClientFactory.CreateClient("modeus");
|
||||
_modeusHttpClient = modeusHttpClient;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
SetToken(_configuration["TOKEN"]); // Установка предустановленного токена при инициализации, на случай если нет возможности связи с AUTH сервисом
|
||||
}
|
||||
|
||||
public void SetToken(string? token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)) {
|
||||
_logger.LogError("SetToken: Предоставленный токен пустой.");
|
||||
return;
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Remove(HeaderNames.Authorization);
|
||||
_httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, $"Bearer {token}");
|
||||
}
|
||||
|
||||
public async Task<string?> GetScheduleAsync(ModeusScheduleRequest msr)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
$"schedule-calendar-v2/api/calendar/events/search?tz={_configuration["TZ"]!}");
|
||||
request.Content = new StringContent(JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions),
|
||||
Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
_logger.LogInformation("GetScheduleAsync: Ответ получен: {StatusCode}", response.StatusCode);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Attendees>> GetAttendeesAsync(Guid eventId)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get,
|
||||
$"schedule-calendar-v2/api/calendar/events/{eventId}/attendees");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
_logger.LogInformation("GetAttendeesAsync: Ответ получен: {StatusCode}", response.StatusCode);
|
||||
response.EnsureSuccessStatusCode();
|
||||
List<Attendees>? attendees;
|
||||
try
|
||||
{
|
||||
attendees = Attendees.FromJson(await response.Content.ReadAsStringAsync());
|
||||
return attendees;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "GetAttendeesAsync: Deserialization failed.");
|
||||
}
|
||||
|
||||
return new List<Attendees>();
|
||||
}
|
||||
|
||||
public async Task<string?> SearchRoomsAsync(RoomSearchRequest requestDto)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "schedule-calendar-v2/api/campus/rooms/search");
|
||||
request.Content =
|
||||
new StringContent(JsonSerializer.Serialize(requestDto, GlobalConsts.JsonSerializerOptions),
|
||||
Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
_logger.LogInformation("SearchRoomsAsync: Ответ получен: {StatusCode}", response.StatusCode);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
public async Task<string?> GetGuidAsync(string fullName)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "schedule-calendar-v2/api/people/persons/search");
|
||||
request.Content = new StringContent(JsonSerializer.Serialize(new
|
||||
{
|
||||
fullName,
|
||||
sort = "+fullName",
|
||||
size = 10,
|
||||
page = 0
|
||||
}), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
|
||||
_logger.LogInformation("GetGuidAsync: Ответ получен: {StatusCode}", response.StatusCode);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
|
||||
string? personId;
|
||||
try
|
||||
{
|
||||
personId = JsonDocument.Parse(json).RootElement
|
||||
.GetProperty("_embedded")
|
||||
.GetProperty("persons")[0]
|
||||
.GetProperty("id")
|
||||
.GetString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"GetGuidAsync: Не удалось получить идентификатор пользователя, {FullName}, json: {Json}", fullName,
|
||||
json);
|
||||
return null;
|
||||
}
|
||||
|
||||
return personId;
|
||||
}
|
||||
|
||||
public async Task<Schedule?> GetScheduleJsonAsync(ModeusScheduleRequest msr)
|
||||
@@ -124,8 +31,8 @@ public class ModeusService
|
||||
var schedule = await GetScheduleAsync(msr);
|
||||
if (schedule == null)
|
||||
{
|
||||
_logger.LogError("GetScheduleJsonAsync: Schedule is null. Request: {@msr}", msr);
|
||||
throw new Exception("Schedule is null");
|
||||
_logger.LogErrorHere($"schedule is null. {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
return null;
|
||||
}
|
||||
|
||||
Schedule? scheduleJson;
|
||||
@@ -135,25 +42,16 @@ public class ModeusService
|
||||
switch (scheduleJson)
|
||||
{
|
||||
case null:
|
||||
_logger.LogError(
|
||||
"GetScheduleJsonAsync: scheduleJson is null. Schedule: {Schedule}\n Request: {msr}",
|
||||
schedule, JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions));
|
||||
_logger.LogErrorHere($"scheduleJson is null. Schedule: {schedule}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
break;
|
||||
case { Embedded: null }:
|
||||
_logger.LogError(
|
||||
"GetScheduleJsonAsync: scheduleJson.Embedded is null. Response: {@response}\nscheduleJson: {@scheduleJson}\n Request: {msr}",
|
||||
schedule, scheduleJson, JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions));
|
||||
_logger.LogErrorHere($"scheduleJson.Embedded is null. Response: {schedule}\nscheduleJson: {scheduleJson}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
break;
|
||||
case { Embedded.Events: null }:
|
||||
_logger.LogError(
|
||||
"GetScheduleJsonAsync: scheduleJson.Embedded.Events is null. Response: {@response}\nEmbedded: {@Embedded}\n Request: {msr}",
|
||||
schedule, scheduleJson.Embedded,
|
||||
JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions));
|
||||
_logger.LogErrorHere($"scheduleJson.Embedded.Events is null. Response: {schedule}\nscheduleJson: {scheduleJson}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
break;
|
||||
case { Embedded.Events.Length: 0 }:
|
||||
_logger.LogWarning(
|
||||
"GetScheduleJsonAsync: scheduleJson.Embedded.Events is empty. Embedded: {@Embedded}\n Request: {msr}",
|
||||
scheduleJson.Embedded, JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions));
|
||||
_logger.LogWarningHere($"scheduleJson.Embedded.Events is empty. Embedded: {scheduleJson.Embedded}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
break;
|
||||
default:
|
||||
return scheduleJson;
|
||||
@@ -161,9 +59,7 @@ public class ModeusService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
"GetScheduleJsonAsync: Deserialization failed. Schedule: {Schedule}\n Request: {msr}", schedule,
|
||||
JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions));
|
||||
_logger.LogErrorHere($"Deserialization failed. Schedule: {schedule}\n Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}\n Exception: {ex}");
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -174,8 +70,7 @@ public class ModeusService
|
||||
var scheduleJson = await GetScheduleJsonAsync(msr);
|
||||
if (scheduleJson == null)
|
||||
{
|
||||
_logger.LogError("GetIcsAsync: scheduleJson is null after deserialization. Request: " +
|
||||
JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions));
|
||||
_logger.LogErrorHere($"scheduleJson is null after deserialization. Request: {JsonSerializer.Serialize(msr, GlobalConsts.JsonSerializerOptions)}");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -283,8 +178,31 @@ public class ModeusService
|
||||
|
||||
var serializer = new CalendarSerializer();
|
||||
var serializedCalendar = serializer.SerializeToString(calendar);
|
||||
_logger.LogInformation("GetIcsAsync: Serialized calendar created. Length: {Length}",
|
||||
serializedCalendar?.Length ?? 0);
|
||||
_logger.LogInformationHere($"serialized calendar created. Length: {serializedCalendar?.Length ?? 0}");
|
||||
return serializedCalendar;
|
||||
}
|
||||
|
||||
#region Проксирование методов из ModeusHttpClient
|
||||
|
||||
public async Task<string?> SearchRoomsAsync(RoomSearchRequest request)
|
||||
{
|
||||
return await _modeusHttpClient.SearchRoomsAsync(request);
|
||||
}
|
||||
|
||||
public async Task<string?> GetScheduleAsync(ModeusScheduleRequest msr)
|
||||
{
|
||||
return await _modeusHttpClient.GetScheduleAsync(msr);
|
||||
}
|
||||
|
||||
public async Task<string?> GetGuidAsync(string fullname)
|
||||
{
|
||||
return await _modeusHttpClient.GetGuidAsync(fullname);
|
||||
}
|
||||
|
||||
public async Task<List<Attendees>> GetAttendeesAsync(Guid eventId)
|
||||
{
|
||||
return await _modeusHttpClient.GetAttendeesAsync(eventId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user