Upload code

This commit is contained in:
2025-08-31 15:16:10 +03:00
parent 63545ee8dc
commit e6157ed3d7
12 changed files with 400 additions and 1 deletions

25
.dockerignore Normal file
View File

@@ -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

119
.gitignore vendored
View File

@@ -91,3 +91,122 @@ fabric.properties
# Android studio 3.1+ serialized cache file # Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser .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

View File

@@ -1,2 +1,6 @@
# SfeduSchedule
## TODO
- [ ] Добавить RateLimiter
- [ ] Добавить обработку ошибок при запросах к modeus

16
SfeduSchedule.sln Normal file
View File

@@ -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

View File

@@ -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<IActionResult> Get([FromQuery] List<Guid> 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<IActionResult> Post([FromBody] ModeusScheduleRequest request)
{
var schedule = await _modeusService.GetScheduleAsync(request);
return Ok(schedule);
}
}
}

View File

@@ -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<IActionResult> 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);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace SfeduSchedule
{
public class ModeusScheduleRequest(int size, DateTime timeMin, DateTime timeMax, List<Guid> attendeePersonId)
{
public int Size { get; set; } = size;
public DateTime TimeMin { get; set; } = timeMin;
public DateTime TimeMax { get; set; } = timeMax;
public List<Guid> AttendeePersonId { get; set; } = attendeePersonId;
}
}

40
SfeduSchedule/Program.cs Normal file
View File

@@ -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<ModeusService>();
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();

View File

@@ -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"
}
}
}
}

View File

@@ -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<ModeusService> _logger;
private readonly IConfiguration _configuration;
public ModeusService(HttpClient httpClient, ILogger<ModeusService> 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<string?> 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<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 = 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;
}
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8"/>
<PackageReference Include="Microsoft.Identity.Web" Version="3.14.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.7.2" />
</ItemGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}