Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Client.Wasm/Components/StudentCard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
</CardHeader>
<CardBody>
<UnorderedList Unstyled>
<UnorderedListItem>Номер <Strong>№X "Название лабораторной"</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№Х "Название варианта"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Фамилией Именем 65ХХ</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://puginarug.com/">Ссылка на форк</Link></UnorderedListItem>
<UnorderedListItem>Номер <Strong>№1 "Кэширование"</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№2 "Сотрудник компании"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Беляковой Вероникой 6511</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/Cat-sandwich/cloud-development">Ссылка на форк</Link></UnorderedListItem>
</UnorderedList>
</CardBody>
</Card>
4 changes: 2 additions & 2 deletions Client.Wasm/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5127",
"environmentVariables": {
Expand All @@ -22,7 +22,7 @@
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7282;http://localhost:5127",
"environmentVariables": {
Expand Down
2 changes: 1 addition & 1 deletion Client.Wasm/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
}
},
"AllowedHosts": "*",
"BaseAddress": ""
"BaseAddress": "http://localhost:5200/api/employee"
}
24 changes: 24 additions & 0 deletions CloudDevelopment.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ VisualStudioVersion = 17.14.36811.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.Wasm", "Client.Wasm\Client.Wasm.csproj", "{AE7EEA74-2FE0-136F-D797-854FD87E022A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Employee.ApiService", "Employee.ApiService\Employee.ApiService.csproj", "{3642B4FB-560F-150E-907B-C9C514725E87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Employee.AppHost", "Employee.AppHost\Employee.AppHost.csproj", "{BEB73446-37AA-8D05-2D56-A35572F27A3B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Employee.ServiceDefaults", "Employee.ServiceDefaults\Employee.ServiceDefaults.csproj", "{D85BA7A1-4E0F-9EEE-41E8-7A478C12984D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Employee.ApiGateway", "Employee.ApiGateway\Employee.ApiGateway.csproj", "{08732133-D1F2-00C9-671B-4197752A18B0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +23,22 @@ Global
{AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.Build.0 = Release|Any CPU
{3642B4FB-560F-150E-907B-C9C514725E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3642B4FB-560F-150E-907B-C9C514725E87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3642B4FB-560F-150E-907B-C9C514725E87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3642B4FB-560F-150E-907B-C9C514725E87}.Release|Any CPU.Build.0 = Release|Any CPU
{BEB73446-37AA-8D05-2D56-A35572F27A3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEB73446-37AA-8D05-2D56-A35572F27A3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEB73446-37AA-8D05-2D56-A35572F27A3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEB73446-37AA-8D05-2D56-A35572F27A3B}.Release|Any CPU.Build.0 = Release|Any CPU
{D85BA7A1-4E0F-9EEE-41E8-7A478C12984D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D85BA7A1-4E0F-9EEE-41E8-7A478C12984D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D85BA7A1-4E0F-9EEE-41E8-7A478C12984D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D85BA7A1-4E0F-9EEE-41E8-7A478C12984D}.Release|Any CPU.Build.0 = Release|Any CPU
{08732133-D1F2-00C9-671B-4197752A18B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08732133-D1F2-00C9-671B-4197752A18B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08732133-D1F2-00C9-671B-4197752A18B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08732133-D1F2-00C9-671B-4197752A18B0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
17 changes: 17 additions & 0 deletions Employee.ApiGateway/Employee.ApiGateway.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Ocelot" Version="24.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Employee.ServiceDefaults\Employee.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions Employee.ApiGateway/LoadBalancer/QueryBasedLoadBalanser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Ocelot.Values;
using Ocelot.Responses;
using Ocelot.LoadBalancer.Errors;
using Ocelot.LoadBalancer.Interfaces;
using Ocelot.ServiceDiscovery.Providers;

namespace Employee.ApiGateway.LoadBalancer;

/// <summary>
/// Балансировщик нагрузки, выбирающий реплику по значению параметра "id"
/// </summary>
/// <param name="serviceDiscovery">Провайдер для получения списка доступных сервисов</param>
public class QueryBasedLoadBalancer(IServiceDiscoveryProvider serviceDiscovery) : ILoadBalancer
{
private const string IdQuery = "id";

public string Type => nameof(QueryBasedLoadBalancer);

/// <summary>
/// Функция выбора сервиса по параметру "id"
/// </summary>
/// <param name="httpContext">Контекст HTTP-запроса</param>
/// <returns>Адрес выбранного сервиса или ошибка</returns>
public async Task<Response<ServiceHostAndPort>> LeaseAsync(HttpContext httpContext)
{
var services = await serviceDiscovery.GetAsync();

if (services is null || services.Count == 0)
{
return new ErrorResponse<ServiceHostAndPort>(
new ServicesAreNullError("Нет доступных сервисов"));
}

var idResult = TryGetValidId(httpContext.Request.Query);

if (!idResult.IsSuccess)
{
return new ErrorResponse<ServiceHostAndPort>(
new UnableToFindLoadBalancerError(idResult.ErrorMessage));
}

var id = idResult.Value;

var index = id % services.Count;
var selected = services[index];

return new OkResponse<ServiceHostAndPort>(selected.HostAndPort);
}

/// <summary>
/// Функция проверки параметра запроса
/// </summary>
/// <param name="query">Запрос</param>
/// <returns>Значение id и сообщение об ошибке</returns>
private static (bool IsSuccess, int Value, string ErrorMessage) TryGetValidId(IQueryCollection query)
{
if (!query.TryGetValue(IdQuery, out var idValues) || string.IsNullOrWhiteSpace(idValues))
return (false, 0, "Отсутствует или пустой параметр 'id'");

if (!int.TryParse(idValues.First(), out var id))
return (false, 0, "Параметр 'id' должен быть числом");

if (id < 0)
return (false, 0, "Параметр 'id' не может быть отрицательным");

return (true, id, string.Empty);
}
public void Release(ServiceHostAndPort hostAndPort) { }
}
44 changes: 44 additions & 0 deletions Employee.ApiGateway/Program.cs
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А сюда нужно добавить cors и настроить его так, чтобы разрешенным origin был клиент

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Employee.ApiGateway.LoadBalancer;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

var generators = builder.Configuration.GetSection("Generators").Get<string[]>() ?? [];

var overrides = new List<KeyValuePair<string, string?>>();

for (var i = 0; i < generators.Length; i++)
{
var serviceName = generators[i];
var url = builder.Configuration[$"services:{serviceName}:http:0"];

if (string.IsNullOrWhiteSpace(url))
continue;

if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
continue;

overrides.Add(new KeyValuePair<string, string?>(
$"Routes:0:DownstreamHostAndPorts:{i}:Host", uri.Host));

overrides.Add(new KeyValuePair<string, string?>(
$"Routes:0:DownstreamHostAndPorts:{i}:Port", uri.Port.ToString()));
}

if (overrides.Any())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (overrides.Count != 0)

{
builder.Configuration.AddInMemoryCollection(overrides);
}

builder.Services
.AddOcelot(builder.Configuration)
.AddCustomLoadBalancer((route, sp) =>
new QueryBasedLoadBalancer(sp));

var app = builder.Build();

await app.UseOcelot();
await app.RunAsync();
38 changes: 38 additions & 0 deletions Employee.ApiGateway/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50608",
"sslPort": 44382
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5200",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7041;http://localhost:5200",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
11 changes: 11 additions & 0 deletions Employee.ApiGateway/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5200"
}
}
9 changes: 9 additions & 0 deletions Employee.ApiGateway/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
28 changes: 28 additions & 0 deletions Employee.ApiGateway/ocelot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"Generators": [ "generator-1", "generator-2", "generator-3" ],
"Routes": [
{
"DownstreamPathTemplate": "/api/employee",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5201
},
{
"Host": "localhost",
"Port": 5202
},
{
"Host": "localhost",
"Port": 5203
}
],
"UpstreamPathTemplate": "/api/employee",
"UpstreamHttpMethod": [ "GET" ],
"LoadBalancerOptions": {
"Type": "QueryBasedLoadBalancer"
}
}
]
}
21 changes: 21 additions & 0 deletions Employee.ApiService/Employee.ApiService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Employee.ServiceDefaults\Employee.ServiceDefaults.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.StackExchange.Redis.DistributedCaching" Version="13.1.2" />
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.1.5" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.1.5" />
</ItemGroup>

</Project>
58 changes: 58 additions & 0 deletions Employee.ApiService/Models/EmployeeModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace Employee.ApiService.Models;

/// <summary>
/// Класс сотрудник компании
/// </summary>
public class EmployeeModel
{

/// <summary>
/// Идентификатор сотрудника в системе
/// </summary>
public required int Id { get; set; }

/// <summary>
/// ФИО
/// </summary>
public required string Name { get; set; }

/// <summary>
/// Должность
/// </summary>
public required string Position { get; set; }

/// <summary>
/// Отдел
/// </summary>
public required string Department { get; set; }

/// <summary>
/// Дата приема
/// </summary>
public required DateOnly DateAdmission { get; set; }

/// <summary>
/// Оклад
/// </summary>
public required decimal Salary { get; set; }

/// <summary>
/// Электронная почта
/// </summary>
public required string Email { get; set; }

/// <summary>
/// Номер телефона
/// </summary>
public required string Phone { get; set; }

/// <summary>
/// Индикатор увольнения
/// </summary>
public bool DismissalIndicator { get; set; } = false;

/// <summary>
/// Дата увольнения
/// </summary>
public DateOnly? DateDismissal { get; set; }
}
Loading
Loading