diff --git a/GatewayAPI/.dockerignore b/GatewayAPI/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..fe1152bdb8442f4d14f9b9533e63fe0c2680bcee --- /dev/null +++ b/GatewayAPI/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*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 +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/GatewayAPI/Clients/FlightService/FlightServiceClient.cs b/GatewayAPI/Clients/FlightService/FlightServiceClient.cs new file mode 100644 index 0000000000000000000000000000000000000000..cdf5f3a75248f64842cd7cc1bde1d9a0879f1a66 --- /dev/null +++ b/GatewayAPI/Clients/FlightService/FlightServiceClient.cs @@ -0,0 +1,75 @@ +using GatewayAPI.Models; + +namespace GatewayAPI.Clients.FlightService +{ + public class FlightServiceClient : IFlightServiceClient + { + private readonly HttpClient httpClient; + private static readonly string FLIGHT_API_PATH = "api/Flight"; + private static readonly string SEAT_API_PATH = "api/Seat"; + + public FlightServiceClient(HttpClient httpClient) + { + this.httpClient = httpClient; + } + + public async Task<HttpResponseMessage> GetFlightAsync(int flightId) + { + return await httpClient.GetAsync($"{FLIGHT_API_PATH}/{flightId}"); + } + + public async Task<HttpResponseMessage> GetFlightsAsync(string? origin = null, string? destination = null, DateTime? departureTime = null, DateTime? arrivalTime = null) + { + var queryParams = new List<string>(); + + if (origin != null) + queryParams.Add($"origin={Uri.EscapeDataString(origin)}"); + + if (destination != null) + queryParams.Add($"destination={Uri.EscapeDataString(destination)}"); + + if (departureTime.HasValue) + queryParams.Add($"departureTime={departureTime.Value.ToString("yyyy-MM-dd HH:mm:ss.fff") + "000"}"); + + if (arrivalTime.HasValue) + queryParams.Add($"arrivalTime={arrivalTime.Value.ToString("yyyy-MM-dd HH:mm:ss.fff") + "000"}"); + + string queryString = queryParams.Any() ? $"?{string.Join("&", queryParams)}" : string.Empty; + + return await httpClient.GetAsync($"{FLIGHT_API_PATH}{queryString}"); + } + + public async Task<HttpResponseMessage> AddFlightAsync(FlightCreation flight) + { + return await httpClient.PostAsJsonAsync(FLIGHT_API_PATH, flight); + } + + public async Task<HttpResponseMessage> GetFlightCapacityAsync(int flightId, int classType) + { + return await httpClient.GetAsync($"{FLIGHT_API_PATH}/{flightId}/capacity?ClassType={classType}"); + } + + + + + public async Task<HttpResponseMessage> GetSeatsAsync() + { + return await httpClient.GetAsync(SEAT_API_PATH); + } + + public async Task<HttpResponseMessage> GetSeatAsync(int seatId) + { + return await httpClient.GetAsync($"{SEAT_API_PATH}/{seatId}"); + } + + public async Task<HttpResponseMessage> IsSeatAvailableAsync(int seatId) + { + return await httpClient.GetAsync($"{SEAT_API_PATH}/{seatId}/isAvailable"); + } + + public async Task<HttpResponseMessage> BookSeatAsync(int seatId) + { + return await httpClient.PutAsync($"{SEAT_API_PATH}/{seatId}", null); + } + } +} diff --git a/GatewayAPI/Clients/FlightService/IFlightServiceClient.cs b/GatewayAPI/Clients/FlightService/IFlightServiceClient.cs new file mode 100644 index 0000000000000000000000000000000000000000..def17a9d8dc494c5e1dd6242f36ff299e3b954a2 --- /dev/null +++ b/GatewayAPI/Clients/FlightService/IFlightServiceClient.cs @@ -0,0 +1,18 @@ +using GatewayAPI.Models; + +namespace GatewayAPI.Clients.FlightService +{ + public interface IFlightServiceClient + { + Task<HttpResponseMessage> GetFlightAsync(int flightId); + Task<HttpResponseMessage> GetFlightsAsync(string? origin = null, string? destination = null, DateTime? departureTime = null, DateTime? arrivalTime = null); + Task<HttpResponseMessage> AddFlightAsync(FlightCreation flight); + Task<HttpResponseMessage> GetFlightCapacityAsync(int flightId, int classType); + + + Task<HttpResponseMessage> GetSeatsAsync(); + Task<HttpResponseMessage> GetSeatAsync(int seatId); + Task<HttpResponseMessage> IsSeatAvailableAsync(int seatId); + Task<HttpResponseMessage> BookSeatAsync(int seatId); + } +} diff --git a/GatewayAPI/Clients/UserService/IUserServiceClient.cs b/GatewayAPI/Clients/UserService/IUserServiceClient.cs new file mode 100644 index 0000000000000000000000000000000000000000..b37273fca5d87d0b0683788602a8879112b26b74 --- /dev/null +++ b/GatewayAPI/Clients/UserService/IUserServiceClient.cs @@ -0,0 +1,14 @@ +using GatewayAPI.Models; + +namespace GatewayAPI.Clients.UserService +{ + public interface IUserServiceClient + { + Task<HttpResponseMessage> GetUserAsync(int id); + Task<HttpResponseMessage> GetUsersAsync(); + Task<HttpResponseMessage> RegisterUserAsync(UserRegistration user); + Task<HttpResponseMessage> LoginUserAsync(UserLogin user); + Task<HttpResponseMessage> AuthorizeUserAsync(); + Task<HttpResponseMessage> LogoutUserAsync(); + } +} diff --git a/GatewayAPI/Clients/UserService/UserServiceClient.cs b/GatewayAPI/Clients/UserService/UserServiceClient.cs new file mode 100644 index 0000000000000000000000000000000000000000..e6533bd3d153f1848ce5ac3282e9f37993bd1a99 --- /dev/null +++ b/GatewayAPI/Clients/UserService/UserServiceClient.cs @@ -0,0 +1,49 @@ +using GatewayAPI.Models; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; + +namespace GatewayAPI.Clients.UserService +{ + public class UserServiceClient : IUserServiceClient + { + private readonly HttpClient httpClient; + private static readonly string API_PATH = "api/User"; + + public UserServiceClient(HttpClient httpClient) + { + this.httpClient = httpClient; + } + + public async Task<HttpResponseMessage> GetUserAsync(int id) + { + return await httpClient.GetAsync($"{API_PATH}/{id}"); + } + + public async Task<HttpResponseMessage> GetUsersAsync() + { + return await httpClient.GetAsync($"{API_PATH}"); + } + + public async Task<HttpResponseMessage> RegisterUserAsync(UserRegistration user) + { + return await httpClient.PostAsJsonAsync($"{API_PATH}/register", user); + } + + public async Task<HttpResponseMessage> LoginUserAsync(UserLogin user) + { + return await httpClient.PostAsJsonAsync($"{API_PATH}/login", user); + } + + public async Task<HttpResponseMessage> AuthorizeUserAsync() + { + return await httpClient.PostAsync($"{API_PATH}/authorize", null); + } + + public async Task<HttpResponseMessage> LogoutUserAsync() + { + return await httpClient.PostAsync($"{API_PATH}/logout", null); + } + + } +} diff --git a/GatewayAPI/Controllers/FlightController.cs b/GatewayAPI/Controllers/FlightController.cs new file mode 100644 index 0000000000000000000000000000000000000000..e335bd102327e36d733c4b3e59b23020fa25182a --- /dev/null +++ b/GatewayAPI/Controllers/FlightController.cs @@ -0,0 +1,47 @@ +using GatewayAPI.Clients.FlightService; +using GatewayAPI.Clients.UserService; +using GatewayAPI.Models; +using Microsoft.AspNetCore.Mvc; + +namespace GatewayAPI.Controllers +{ + [ApiController] + [Route("api/[Controller]")] + public class FlightController : ControllerBase + { + private readonly IFlightServiceClient flightServiceClient; + public FlightController(IFlightServiceClient flightServiceClient) + { + this.flightServiceClient = flightServiceClient; + } + + [HttpGet()] + public async Task<IActionResult> GetFlights(string? origin = null, string? destination = null, DateTime? departureTime = null, DateTime? arrivalTime = null) + { + HttpResponseMessage response = await flightServiceClient.GetFlightsAsync(origin, destination, departureTime, arrivalTime); + return new HttpResponseMessageResult(response); + } + + [HttpGet("{id}")] + public async Task<IActionResult> GetFlights(int id) + { + HttpResponseMessage response = await flightServiceClient.GetFlightAsync(id); + return new HttpResponseMessageResult(response); + } + + [HttpPost()] + public async Task<IActionResult> AddFlight([FromBody] FlightCreation flightCreation) + { + HttpResponseMessage response = await flightServiceClient.AddFlightAsync(flightCreation); + return new HttpResponseMessageResult(response); + } + + + [HttpGet("{id}/capacity")] + public async Task<IActionResult> GetFlightCapacity([FromRoute] int id, [FromQuery] int classType) + { + HttpResponseMessage response = await flightServiceClient.GetFlightCapacityAsync(id, classType); + return new HttpResponseMessageResult(response); + } + } +} diff --git a/GatewayAPI/Controllers/SeatController.cs b/GatewayAPI/Controllers/SeatController.cs new file mode 100644 index 0000000000000000000000000000000000000000..750dfa67d086a9b8eded50d22a24ab1a469131be --- /dev/null +++ b/GatewayAPI/Controllers/SeatController.cs @@ -0,0 +1,45 @@ +using GatewayAPI.Clients.FlightService; +using Microsoft.AspNetCore.Mvc; + +namespace GatewayAPI.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class SeatController : ControllerBase + { + private readonly IFlightServiceClient flightServiceClient; + public SeatController(IFlightServiceClient flightServiceClient) + { + this.flightServiceClient = flightServiceClient; + } + + [HttpGet()] + public async Task<IActionResult> GetSeats() + { + HttpResponseMessage response = await flightServiceClient.GetSeatsAsync(); + return new HttpResponseMessageResult(response); + } + + [HttpGet("{id}")] + public async Task<IActionResult> GetSeats(int id) + { + HttpResponseMessage response = await flightServiceClient.GetSeatAsync(id); + return new HttpResponseMessageResult(response); + } + + [HttpPut("{id}")] + public async Task<IActionResult> BookSeat(int id) + { + HttpResponseMessage response = await flightServiceClient.BookSeatAsync(id); + return new HttpResponseMessageResult(response); + } + + [HttpGet("{id}/isAvailable")] + public async Task<IActionResult> IsAvailable(int id) + { + HttpResponseMessage response = await flightServiceClient.IsSeatAvailableAsync(id); + return new HttpResponseMessageResult(response); + } + + } +} diff --git a/GatewayAPI/Controllers/UserController.cs b/GatewayAPI/Controllers/UserController.cs new file mode 100644 index 0000000000000000000000000000000000000000..2971951e1d5adbf70a4f1548f13324ddc4c5de5f --- /dev/null +++ b/GatewayAPI/Controllers/UserController.cs @@ -0,0 +1,60 @@ +using GatewayAPI.Clients.UserService; +using GatewayAPI.Models; +using Microsoft.AspNetCore.Mvc; +using System.Text; + +namespace GatewayAPI.Controllers +{ + [ApiController] + [Route("api/[Controller]")] + public class UserController : ControllerBase + { + private readonly IUserServiceClient userServiceClient; + public UserController(IUserServiceClient userServiceClient) + { + this.userServiceClient = userServiceClient; + } + + [HttpPost("register")] + public async Task<IActionResult> Register([FromBody] UserRegistration userRegistration) + { + HttpResponseMessage response = await userServiceClient.RegisterUserAsync(userRegistration); + return new HttpResponseMessageResult(response); + } + + [HttpPost("authorize")] + public async Task<IActionResult> Authorize() + { + HttpResponseMessage response = await userServiceClient.AuthorizeUserAsync(); + return new HttpResponseMessageResult(response); + } + + [HttpPost("login")] + public async Task<IActionResult> Login([FromBody] UserLogin userLogin) + { + HttpResponseMessage response = await userServiceClient.LoginUserAsync(userLogin); + return new HttpResponseMessageResult(response); + } + + [HttpPost("logout")] + public async Task<IActionResult> Logout() + { + HttpResponseMessage response = await userServiceClient.LogoutUserAsync(); + return new HttpResponseMessageResult(response); + } + + [HttpGet()] + public async Task<IActionResult> GetUsers() + { + HttpResponseMessage response = await userServiceClient.GetUsersAsync(); + return new HttpResponseMessageResult(response); + } + + [HttpGet("{id}")] + public async Task<IActionResult> GetUser(int id) + { + HttpResponseMessage response = await userServiceClient.GetUserAsync(id); + return new HttpResponseMessageResult(response); + } + } +} diff --git a/GatewayAPI/Dockerfile b/GatewayAPI/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..91552e7d806230d9e91a2ac38b4a877899d87b03 --- /dev/null +++ b/GatewayAPI/Dockerfile @@ -0,0 +1,25 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["GatewayAPI.csproj", "."] +RUN dotnet restore "./GatewayAPI.csproj" +COPY . . +WORKDIR "/src/." +RUN dotnet build "./GatewayAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./GatewayAPI.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "GatewayAPI.dll"] \ No newline at end of file diff --git a/GatewayAPI/GatewayAPI.csproj b/GatewayAPI/GatewayAPI.csproj new file mode 100644 index 0000000000000000000000000000000000000000..a6c8e7aed19e4a88d321cabd92a6bc86dbc715f6 --- /dev/null +++ b/GatewayAPI/GatewayAPI.csproj @@ -0,0 +1,17 @@ +<Project Sdk="Microsoft.NET.Sdk.Web"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <Nullable>enable</Nullable> + <ImplicitUsings>enable</ImplicitUsings> + <UserSecretsId>50dbee94-e07d-4906-85d3-8337755e0027</UserSecretsId> + <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> + <DockerfileContext>.</DockerfileContext> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> + </ItemGroup> + +</Project> diff --git a/GatewayAPI/GatewayAPI.csproj.user b/GatewayAPI/GatewayAPI.csproj.user new file mode 100644 index 0000000000000000000000000000000000000000..983ecfc07a3622c3f9e0e73b96ae3e61aada8cb9 --- /dev/null +++ b/GatewayAPI/GatewayAPI.csproj.user @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <ActiveDebugProfile>http</ActiveDebugProfile> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> + <DebuggerFlavor>ProjectDebugger</DebuggerFlavor> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/GatewayAPI/GatewayAPI.http b/GatewayAPI/GatewayAPI.http new file mode 100644 index 0000000000000000000000000000000000000000..a5bc22a7d7b0f11192f97786eee250cd270ef58a --- /dev/null +++ b/GatewayAPI/GatewayAPI.http @@ -0,0 +1,6 @@ +@GatewayAPI_HostAddress = http://localhost:5267 + +GET {{GatewayAPI_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/GatewayAPI/GatewayAPI.sln b/GatewayAPI/GatewayAPI.sln new file mode 100644 index 0000000000000000000000000000000000000000..018f984ffaca373a57f72a68095a682c7e8f0b3e --- /dev/null +++ b/GatewayAPI/GatewayAPI.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GatewayAPI", "GatewayAPI.csproj", "{2ABFA760-35A9-4BF8-ADAF-3751CCC8C20A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2ABFA760-35A9-4BF8-ADAF-3751CCC8C20A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2ABFA760-35A9-4BF8-ADAF-3751CCC8C20A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2ABFA760-35A9-4BF8-ADAF-3751CCC8C20A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2ABFA760-35A9-4BF8-ADAF-3751CCC8C20A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7FFD5B8C-F828-4EBE-B2B7-888D63CDE589} + EndGlobalSection +EndGlobal diff --git a/GatewayAPI/HttpResponseMessageResult.cs b/GatewayAPI/HttpResponseMessageResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..3873c553f6453a6f17bf9731c31873f9c5b02417 --- /dev/null +++ b/GatewayAPI/HttpResponseMessageResult.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Mvc; +using System.Net.Http; +using System.Threading.Tasks; + +namespace GatewayAPI +{ + public class HttpResponseMessageResult : IActionResult + { + private readonly HttpResponseMessage responseMessage; + + public HttpResponseMessageResult(HttpResponseMessage responseMessage) + { + this.responseMessage = responseMessage ?? throw new ArgumentNullException(nameof(responseMessage)); + } + + public async Task ExecuteResultAsync(ActionContext context) + { + HttpContext httpContext = context.HttpContext; + HttpResponse response = httpContext.Response; + + response.ContentType = "application/json; charset=utf-8"; + + // Copy the status code + response.StatusCode = (int)responseMessage.StatusCode; + + // Copy the cookies + if (responseMessage.Headers.TryGetValues("Set-Cookie", out var cookieValues)) + { + foreach (string cookie in cookieValues) + response.Headers.Append("Set-Cookie", cookie); + } + + // Copy the response body directly to the response stream + using (Stream responseStream = await responseMessage.Content.ReadAsStreamAsync()) + { + await responseStream.CopyToAsync(response.Body); + await response.Body.FlushAsync(); + } + } + } +} diff --git a/GatewayAPI/Models/FlightCreation.cs b/GatewayAPI/Models/FlightCreation.cs new file mode 100644 index 0000000000000000000000000000000000000000..e3bdfa48d381f6c4c91fee0691b517b1a0ab763d --- /dev/null +++ b/GatewayAPI/Models/FlightCreation.cs @@ -0,0 +1,15 @@ +namespace GatewayAPI.Models +{ + public class FlightCreation + { + public required string Origin { get; set; } + public required string Destination { get; set; } + public required DateTime DepartureTime { get; set; } + public required DateTime ArrivalTime { get; set; } + public required int EconomyCapacity { get; set; } + public required int BusinessCapacity { get; set; } + public required decimal EconomyPrice { get; set; } + public required decimal BusinessPrice { get; set; } + + } +} diff --git a/GatewayAPI/Models/UserLogin.cs b/GatewayAPI/Models/UserLogin.cs new file mode 100644 index 0000000000000000000000000000000000000000..47ae2b3b7eea2c4f248314bec714506cc59dcc01 --- /dev/null +++ b/GatewayAPI/Models/UserLogin.cs @@ -0,0 +1,9 @@ +namespace GatewayAPI.Models +{ + public class UserLogin + { + public required string Username { get; set; } + public required string Password { get; set; } + + } +} diff --git a/GatewayAPI/Models/UserRegistration.cs b/GatewayAPI/Models/UserRegistration.cs new file mode 100644 index 0000000000000000000000000000000000000000..dc94c025e13b9b705c39b251ee69d33e8fbc2d3f --- /dev/null +++ b/GatewayAPI/Models/UserRegistration.cs @@ -0,0 +1,11 @@ +namespace GatewayAPI.Models +{ + public class UserRegistration + { + public required string Username { get; set; } + public required string Email { get; set; } + public required string Password { get; set; } + public required int UserType { get; set; } + + } +} diff --git a/GatewayAPI/Program.cs b/GatewayAPI/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..6f900f8f9e9a965c4a1d1bc242379f522d75dc26 --- /dev/null +++ b/GatewayAPI/Program.cs @@ -0,0 +1,27 @@ +using GatewayAPI; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddHttpContextAccessor(); +builder.Services.AddTransient<RequestCookieHandler>(); +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +// Add Http Typed Clients for each Microservice +builder.Services.AddHttpClients(builder.Configuration); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); +app.Run(); diff --git a/GatewayAPI/Properties/launchSettings.json b/GatewayAPI/Properties/launchSettings.json new file mode 100644 index 0000000000000000000000000000000000000000..bf79d5709995c33286eba3471cf4872823ff1759 --- /dev/null +++ b/GatewayAPI/Properties/launchSettings.json @@ -0,0 +1,52 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5267" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7130;http://localhost:5267" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_HTTPS_PORTS": "8081", + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52758", + "sslPort": 44344 + } + } +} \ No newline at end of file diff --git a/GatewayAPI/RequestCookieHandler .cs b/GatewayAPI/RequestCookieHandler .cs new file mode 100644 index 0000000000000000000000000000000000000000..11a59596adc6a9ae9a0a51f2e6904f42f09fe4bc --- /dev/null +++ b/GatewayAPI/RequestCookieHandler .cs @@ -0,0 +1,36 @@ +using System.Net; + +namespace GatewayAPI +{ + public class RequestCookieHandler : DelegatingHandler + { + private readonly IHttpContextAccessor httpContextAccessor; + + public RequestCookieHandler(IHttpContextAccessor httpContextAccessor) + { + this.httpContextAccessor = httpContextAccessor; + } + + protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpContext? context = httpContextAccessor.HttpContext; + + if (context?.Request.Cookies != null && request.RequestUri != null) + { + CookieContainer cookieContainer = new CookieContainer(); + foreach (KeyValuePair<string, string> cookie in context.Request.Cookies) + { + if (!string.IsNullOrEmpty(cookie.Value) && (cookie.Key == "AccessToken" || cookie.Key == "RefreshToken")) + cookieContainer.Add(request.RequestUri, new Cookie(cookie.Key, cookie.Value)); + } + + var cookieHeader = cookieContainer.GetCookieHeader(request.RequestUri); + if (!string.IsNullOrEmpty(cookieHeader)) + request.Headers.Add("Cookie", cookieHeader); + } + + return await base.SendAsync(request, cancellationToken); + } + + } +} diff --git a/GatewayAPI/ServiceCollectionExtensions.cs b/GatewayAPI/ServiceCollectionExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..b928371493b0c2b72e8bd18237b32858bc008623 --- /dev/null +++ b/GatewayAPI/ServiceCollectionExtensions.cs @@ -0,0 +1,26 @@ +using GatewayAPI.Clients.FlightService; +using GatewayAPI.Clients.UserService; +using System.Runtime.CompilerServices; + +namespace GatewayAPI +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddHttpClients(this IServiceCollection services, ConfigurationManager configurationManager) + { + services.AddHttpClient<IUserServiceClient, UserServiceClient>(client => + { + string? baseUrl = configurationManager["UserMicroservice:BaseUrl"]; + client.BaseAddress = new Uri(baseUrl ?? throw new InvalidOperationException("UserMicroservice BaseUrl is not configured.")); + }).AddHttpMessageHandler<RequestCookieHandler>(); + + services.AddHttpClient<IFlightServiceClient, FlightServiceClient>(client => + { + string? baseUrl = configurationManager["FlightMicroservice:BaseUrl"]; + client.BaseAddress = new Uri(baseUrl ?? throw new InvalidOperationException("FlightMicroservice BaseUrl is not configured.")); + }).AddHttpMessageHandler<RequestCookieHandler>(); + + return services; + } + } +} diff --git a/GatewayAPI/appsettings.Development.json b/GatewayAPI/appsettings.Development.json new file mode 100644 index 0000000000000000000000000000000000000000..0c208ae9181e5e5717e47ec1bd59368aebc6066e --- /dev/null +++ b/GatewayAPI/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/GatewayAPI/appsettings.json b/GatewayAPI/appsettings.json new file mode 100644 index 0000000000000000000000000000000000000000..0345e2e60cf8011168c1b2b2d826c9fb2df87042 --- /dev/null +++ b/GatewayAPI/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "UserMicroservice": { + "BaseUrl": "http://localhost:5089/" + }, + "FlightMicroservice": { + "BaseUrl": "http://localhost:5175/" + } +}