From e6157ed3d786f77c253a7331cfa1bb5dd16c6d2d Mon Sep 17 00:00:00 2001 From: Sergey Karmanov Date: Sun, 31 Aug 2025 15:16:10 +0300 Subject: [PATCH] Upload code --- .dockerignore | 25 ++++ .gitignore | 119 ++++++++++++++++++ README.md | 6 +- SfeduSchedule.sln | 16 +++ .../Controllers/ScheduleController.cs | 29 +++++ SfeduSchedule/Controllers/SfeduController.cs | 32 +++++ SfeduSchedule/ModeusScheduleRequestDTO.cs | 10 ++ SfeduSchedule/Program.cs | 40 ++++++ SfeduSchedule/Properties/launchSettings.json | 23 ++++ SfeduSchedule/Services/ModeusService.cs | 70 +++++++++++ SfeduSchedule/SfeduSchedule.csproj | 22 ++++ SfeduSchedule/appsettings.json | 9 ++ 12 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 SfeduSchedule.sln create mode 100644 SfeduSchedule/Controllers/ScheduleController.cs create mode 100644 SfeduSchedule/Controllers/SfeduController.cs create mode 100644 SfeduSchedule/ModeusScheduleRequestDTO.cs create mode 100644 SfeduSchedule/Program.cs create mode 100644 SfeduSchedule/Properties/launchSettings.json create mode 100644 SfeduSchedule/Services/ModeusService.cs create mode 100644 SfeduSchedule/SfeduSchedule.csproj create mode 100644 SfeduSchedule/appsettings.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..38bece4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index d6be2b3..baf319e 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,122 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +SfeduSchedule/appsettings.Development.json diff --git a/README.md b/README.md index b5d1139..9823d30 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ -# SfeduSchedule + +## TODO + +- [ ] Добавить RateLimiter +- [ ] Добавить обработку ошибок при запросах к modeus \ No newline at end of file diff --git a/SfeduSchedule.sln b/SfeduSchedule.sln new file mode 100644 index 0000000..50668ae --- /dev/null +++ b/SfeduSchedule.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SfeduSchedule", "SfeduSchedule\SfeduSchedule.csproj", "{57B088A7-D7E2-4B5D-82A4-A3070A78A3E4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {57B088A7-D7E2-4B5D-82A4-A3070A78A3E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57B088A7-D7E2-4B5D-82A4-A3070A78A3E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57B088A7-D7E2-4B5D-82A4-A3070A78A3E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57B088A7-D7E2-4B5D-82A4-A3070A78A3E4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/SfeduSchedule/Controllers/ScheduleController.cs b/SfeduSchedule/Controllers/ScheduleController.cs new file mode 100644 index 0000000..9969e3f --- /dev/null +++ b/SfeduSchedule/Controllers/ScheduleController.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using SfeduSchedule.Services; + +namespace SfeduSchedule.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ScheduleController : ControllerBase + { + private readonly ModeusService _modeusService; + public ScheduleController(ModeusService modeusService) => + _modeusService = modeusService; + + [HttpGet] + public async Task Get([FromQuery] List attendeePersonId) + { + var msr = new ModeusScheduleRequest(500, DateTime.UtcNow, DateTime.UtcNow.AddDays(20), attendeePersonId); + var schedule = await _modeusService.GetScheduleAsync(msr); + return Ok(schedule); + } + + [HttpPost] + public async Task Post([FromBody] ModeusScheduleRequest request) + { + var schedule = await _modeusService.GetScheduleAsync(request); + return Ok(schedule); + } + } +} diff --git a/SfeduSchedule/Controllers/SfeduController.cs b/SfeduSchedule/Controllers/SfeduController.cs new file mode 100644 index 0000000..15854e5 --- /dev/null +++ b/SfeduSchedule/Controllers/SfeduController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using SfeduSchedule.Services; + +namespace SfeduSchedule.Controllers +{ + [ApiController] + [Route("api/[controller]")] + [Authorize] + public class SfeduController : ControllerBase + { + private readonly ModeusService _modeusService; + public SfeduController(ModeusService modeusService) => + _modeusService = modeusService; + + [HttpGet] + [Route("guid")] + public async Task Get() + { + var name = User.FindFirst("name")?.Value; + if (string.IsNullOrEmpty(name)) + return StatusCode(StatusCodes.Status500InternalServerError); + + var guid = await _modeusService.GetGuidAsync(name); + if (string.IsNullOrEmpty(guid)) + return StatusCode(StatusCodes.Status500InternalServerError); + + return Ok(guid); + } + + } +} diff --git a/SfeduSchedule/ModeusScheduleRequestDTO.cs b/SfeduSchedule/ModeusScheduleRequestDTO.cs new file mode 100644 index 0000000..b4f91a8 --- /dev/null +++ b/SfeduSchedule/ModeusScheduleRequestDTO.cs @@ -0,0 +1,10 @@ +namespace SfeduSchedule +{ + public class ModeusScheduleRequest(int size, DateTime timeMin, DateTime timeMax, List attendeePersonId) + { + public int Size { get; set; } = size; + public DateTime TimeMin { get; set; } = timeMin; + public DateTime TimeMax { get; set; } = timeMax; + public List AttendeePersonId { get; set; } = attendeePersonId; + } +} diff --git a/SfeduSchedule/Program.cs b/SfeduSchedule/Program.cs new file mode 100644 index 0000000..eecc17c --- /dev/null +++ b/SfeduSchedule/Program.cs @@ -0,0 +1,40 @@ +using Microsoft.Identity.Web; +using Scalar.AspNetCore; +using SfeduSchedule.Services; + +var builder = WebApplication.CreateBuilder(args); + +var configuration = builder.Configuration; +string? adminToken = configuration["TOKEN"]; + +if (string.IsNullOrEmpty(adminToken)) +{ + Console.WriteLine("Токен администратора не установлен"); + Environment.Exit(1); +} + +builder.Services.AddOpenApi(); +builder.Services.AddControllers(); +builder.Services.AddHttpClient(); + +builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration); + + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} +app.MapScalarApiReference(options => +{ + options.WithTitle("Расписание занятий ЮФУ"); + options.Theme = ScalarTheme.Kepler; +}); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); + diff --git a/SfeduSchedule/Properties/launchSettings.json b/SfeduSchedule/Properties/launchSettings.json new file mode 100644 index 0000000..645f71f --- /dev/null +++ b/SfeduSchedule/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5087", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7146;http://localhost:5087", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SfeduSchedule/Services/ModeusService.cs b/SfeduSchedule/Services/ModeusService.cs new file mode 100644 index 0000000..adaeafb --- /dev/null +++ b/SfeduSchedule/Services/ModeusService.cs @@ -0,0 +1,70 @@ +using System.Text.Json; +using Microsoft.Net.Http.Headers; + +namespace SfeduSchedule.Services +{ + public class ModeusService + { + private readonly HttpClient _httpClient; + private readonly JsonSerializerOptions _options = new(); + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + + public ModeusService(HttpClient httpClient, ILogger logger, IConfiguration configuration) + { + _options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + _httpClient = httpClient; + _logger = logger; + _configuration = configuration; + _httpClient.BaseAddress = new Uri("https://sfedu.modeus.org/"); + var token = _configuration["TOKEN"]; + _httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, $"Bearer {token}"); + } + + public async Task GetScheduleAsync(ModeusScheduleRequest msr, string TZ = "Europe/Moscow") + { + var request = new HttpRequestMessage(HttpMethod.Post, $"schedule-calendar-v2/api/calendar/events/search?tz={TZ}"); + request.Content = new StringContent(JsonSerializer.Serialize(msr, _options), System.Text.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 GetGuidAsync(string fullName) + { + var request = new HttpRequestMessage(HttpMethod.Post, $"schedule-calendar-v2/api/people/persons/search"); + request.Content = new StringContent(JsonSerializer.Serialize(new + { + fullName = fullName, + sort = "+fullName", + size = 10, + page = 0 + }), System.Text.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; + } + + } +} diff --git a/SfeduSchedule/SfeduSchedule.csproj b/SfeduSchedule/SfeduSchedule.csproj new file mode 100644 index 0000000..d3a2b4f --- /dev/null +++ b/SfeduSchedule/SfeduSchedule.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + Linux + + + + + + + + + + + .dockerignore + + + + diff --git a/SfeduSchedule/appsettings.json b/SfeduSchedule/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/SfeduSchedule/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}