diff --git a/internal-services.yml b/internal-services.yml index 1cdd448..081e953 100644 --- a/internal-services.yml +++ b/internal-services.yml @@ -34,8 +34,6 @@ services: apigateway: image: ${DOCKER_REGISTRY-}apigateway - ports: - - 8083:80 build: context: . dockerfile: src/Gateway/Gateway.WebAPI/Dockerfile @@ -48,3 +46,15 @@ services: networks: - backend - fronted + + webui: + image: ${DOCKER_REGISTRY-}webui + ports: + - 8080:8080 + build: + context: . + dockerfile: src/Fronted/webui/Dockerfile + depends_on: + - apigateway + networks: + - fronted diff --git a/src/Fronted/webui/.env b/src/Fronted/webui/.env index 8fb14b0..cc3d0c9 100644 --- a/src/Fronted/webui/.env +++ b/src/Fronted/webui/.env @@ -1,2 +1,2 @@ VUE_APP_TITLE=Url Shortener App -VUE_APP_API_GATEWAY_URL=http://localhost:8083 +VUE_APP_API_GATEWAY_URL=http://localhost:5043 diff --git a/src/Fronted/webui/.env.Production b/src/Fronted/webui/.env.Production new file mode 100644 index 0000000..7b3483d --- /dev/null +++ b/src/Fronted/webui/.env.Production @@ -0,0 +1,2 @@ +VUE_APP_TITLE=Url Shortener App +VUE_APP_API_GATEWAY_URL=http://apigateway:80 diff --git a/src/Fronted/webui/Dockerfile b/src/Fronted/webui/Dockerfile new file mode 100644 index 0000000..0c42b79 --- /dev/null +++ b/src/Fronted/webui/Dockerfile @@ -0,0 +1,12 @@ +FROM node:lts-alpine +WORKDIR /app +# install vite globally +RUN npm install -g @vue/cli +# copy all filtes +COPY . . +# install all deps +RUN yarn install + +# vite default port +EXPOSE 8080 +CMD ["vue", "serve"] diff --git a/src/Fronted/webui/package-lock.json b/src/Fronted/webui/package-lock.json index 7c63ef0..ad27dea 100644 --- a/src/Fronted/webui/package-lock.json +++ b/src/Fronted/webui/package-lock.json @@ -9,6 +9,7 @@ "dependencies": { "@types/axios": "^0.14.0", "axios": "^1.5.0", + "bootstrap": "^5.3.1", "core-js": "^3.8.3", "querystring-es3": "^0.2.1", "vue": "^3.2.13", @@ -2026,6 +2027,16 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -4164,6 +4175,24 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bootstrap": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz", + "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -13881,6 +13910,12 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true + }, "@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -15551,6 +15586,12 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "bootstrap": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz", + "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==", + "requires": {} + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", diff --git a/src/Fronted/webui/package.json b/src/Fronted/webui/package.json index 653f214..d5a2413 100644 --- a/src/Fronted/webui/package.json +++ b/src/Fronted/webui/package.json @@ -10,6 +10,7 @@ "dependencies": { "@types/axios": "^0.14.0", "axios": "^1.5.0", + "bootstrap": "^5.3.1", "core-js": "^3.8.3", "querystring-es3": "^0.2.1", "vue": "^3.2.13", diff --git a/src/Gateway/Gateway.Infrastructure/Extensions/InfrastructureLayerExtensions.cs b/src/Gateway/Gateway.Infrastructure/Extensions/InfrastructureLayerExtensions.cs index 35c16ac..afe00fa 100644 --- a/src/Gateway/Gateway.Infrastructure/Extensions/InfrastructureLayerExtensions.cs +++ b/src/Gateway/Gateway.Infrastructure/Extensions/InfrastructureLayerExtensions.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Ocelot.DependencyInjection; -using Ocelot.Values; namespace Gateway.Infrastructure.Extensions { @@ -20,7 +19,6 @@ public static IServiceCollection LoadInfrastructreLayer(this IServiceCollection }); }); - var tempConfiguration = new ConfigurationBuilder() .AddConfiguration(configuration) .SetBasePath(Directory.GetCurrentDirectory()) diff --git a/src/Gateway/Gateway.WebAPI/ocelot.Development.json b/src/Gateway/Gateway.WebAPI/ocelot.Development.json index 2afb71e..bac848a 100644 --- a/src/Gateway/Gateway.WebAPI/ocelot.Development.json +++ b/src/Gateway/Gateway.WebAPI/ocelot.Development.json @@ -13,6 +13,19 @@ "UpstreamHttpMethod": [ "GET" ], "Key": "GetUrl" }, + { + "DownstreamPathTemplate": "/api/buffer/get_all_urls", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 5137 + } + ], + "UpstreamPathTemplate": "/get_all_urls", + "UpstreamHttpMethod": [ "GET" ], + "Key": "GetAllUrls" + }, { "DownstreamPathTemplate": "/api/shortener/publish_url", "DownstreamScheme": "http", @@ -27,17 +40,17 @@ "Key": "PublishUrl" }, { - "DownstreamPathTemplate": "/api/buffer/get_all_urls", + "DownstreamPathTemplate": "/api/shortener/redirect/{shortPath}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", - "Port": 5137 + "Port": 5202 } ], - "UpstreamPathTemplate": "/get_all_urls", + "UpstreamPathTemplate": "/redirect/{shortPath}", "UpstreamHttpMethod": [ "GET" ], - "Key": "GetAllUrls" + "Key": "ReachUrl" } ] } diff --git a/src/Gateway/Gateway.WebAPI/ocelot.Production.json b/src/Gateway/Gateway.WebAPI/ocelot.Production.json index 5e34b84..7ad63b4 100644 --- a/src/Gateway/Gateway.WebAPI/ocelot.Production.json +++ b/src/Gateway/Gateway.WebAPI/ocelot.Production.json @@ -13,7 +13,19 @@ "UpstreamHttpMethod": [ "GET" ], "Key": "GetUrl" }, - + { + "DownstreamPathTemplate": "/api/buffer/get_all_urls", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "buffer_api", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/get_all_urls", + "UpstreamHttpMethod": [ "GET" ], + "Key": "GetAllUrls" + }, { "DownstreamPathTemplate": "/api/shortener/publish_url", "DownstreamScheme": "http", @@ -27,19 +39,18 @@ "UpstreamHttpMethod": [ "POST" ], "Key": "PublishUrl" }, - { - "DownstreamPathTemplate": "/api/buffer/get_all_urls", + "DownstreamPathTemplate": "/api/shortener/redirect/{shortPath}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { - "Host": "buffer_api", + "Host": "shortener_api", "Port": 80 } ], - "UpstreamPathTemplate": "/get_all_urls", - "UpstreamHttpMethod": [ "GET" ] + "UpstreamPathTemplate": "/redirect/{shortPath}", + "UpstreamHttpMethod": [ "GET" ], + "Key": "ReachUrl" } - ] } diff --git a/src/Shortener/Infrastructure/Commands/ReachUrlCommandHandler.cs b/src/Shortener/Infrastructure/Commands/ReachUrlCommandHandler.cs new file mode 100644 index 0000000..644c140 --- /dev/null +++ b/src/Shortener/Infrastructure/Commands/ReachUrlCommandHandler.cs @@ -0,0 +1,41 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Shortener.Infrastructure.Context; + +namespace Shortener.Infrastructure.Commands +{ + public class ReachUrlDto + { + public string Message { get; set; } = string.Empty; + } + + public class ReachUrlCommand : IRequest + { + public string ShortPath { get; set; } + public ReachUrlCommand(string shortPath) + { + ShortPath = shortPath; + } + } + + public class ReachUrlCommandHandler : IRequestHandler + { + private readonly AppDbContext dbContext; + public ReachUrlCommandHandler(AppDbContext _dbContext) + { + dbContext = _dbContext; + } + public async Task Handle(ReachUrlCommand request, CancellationToken cancellationToken) + { + var url = await dbContext.Urls.FirstOrDefaultAsync(p => p.ShortPath == request.ShortPath); + if (url == null) + { + return new() { Message = "an error occurred" }; + } + url.RequestCounter++; + url.LastRequestedDate = DateTime.UtcNow; + await dbContext.SaveChangesAsync(); + return new() { Message = "it is okay" }; + } + } +} diff --git a/src/Shortener/WebAPI/Controllers/ShortenerController.cs b/src/Shortener/WebAPI/Controllers/ShortenerController.cs index 9efa630..d5e1e10 100644 --- a/src/Shortener/WebAPI/Controllers/ShortenerController.cs +++ b/src/Shortener/WebAPI/Controllers/ShortenerController.cs @@ -1,11 +1,14 @@ using MassTransit; +using MediatR; using Microsoft.AspNetCore.Mvc; using Shared.DTOs; using Shared.Extensions; +using Shortener.Infrastructure.Commands; using Shortener.Infrastructure.Context; using Shortener.Infrastructure.Models; using System.Text.Json; + namespace WebAPI.Controllers { [Route("api/[controller]")] @@ -14,11 +17,13 @@ public class ShortenerController : ControllerBase { private readonly ILogger logger; private readonly AppDbContext appDbContext; + private readonly IMediator mediator; - public ShortenerController(ILogger _logger, AppDbContext _appDbContext) + public ShortenerController(ILogger _logger, AppDbContext _appDbContext, IMediator _mediator) { logger = _logger; appDbContext = _appDbContext; + mediator = _mediator; } [HttpPost] @@ -48,6 +53,17 @@ public async Task PublishUrlToCreateShortPath([FromBody] ShortUrl logger.LogWarning(ex.ToJsonString()); return BadRequest(); } + } + + + [HttpGet] + [Route("redirect/{shortPath}")] + public async Task ReachUrl(string shortPath) + { + logger.LogInformation($"{shortPath} was send to command"); + return Ok(await mediator.Send(new ReachUrlCommand(shortPath))); + } + } } diff --git a/tests/Buffer/UnitTests/GlobalUsings.cs b/tests/Buffer/UnitTests/GlobalUsings.cs index c31b1f3..c1c8f0a 100644 --- a/tests/Buffer/UnitTests/GlobalUsings.cs +++ b/tests/Buffer/UnitTests/GlobalUsings.cs @@ -1,3 +1,3 @@ -global using Xunit; global using Microsoft.Extensions.Logging; -global using Moq; \ No newline at end of file +global using Moq; +global using Xunit; diff --git a/tests/Shortener/Shortener.UnitTests/CommandTests.cs b/tests/Shortener/Shortener.UnitTests/CommandTests.cs index d456b8e..c471dcd 100644 --- a/tests/Shortener/Shortener.UnitTests/CommandTests.cs +++ b/tests/Shortener/Shortener.UnitTests/CommandTests.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Moq; -using Shared.ConfigModels; using Shortener.Infrastructure.Commands; using Shortener.Infrastructure.Context;