diff --git a/backend/Dockerfile b/backend/Dockerfile index 5990f8d..25dc31c 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,11 +1,12 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/aspnet:7.0.10-alpine3.18 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +#Todo: clean it up for multiproject build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY src/api/api.csproj src/api/ RUN dotnet restore "src/api/api.csproj" diff --git a/backend/UnitTests/UnitTests.csproj b/backend/UnitTests/UnitTests.csproj deleted file mode 100644 index 4ba4b01..0000000 --- a/backend/UnitTests/UnitTests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net7.0 - enable - enable - - false - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/backend/UnitTests/Usings.cs b/backend/UnitTests/Usings.cs deleted file mode 100644 index 8c927eb..0000000 --- a/backend/UnitTests/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; \ No newline at end of file diff --git a/backend/bruno/KSummarized/ToDo/List/Create.bru b/backend/bruno/KSummarized/ToDo/List/Create.bru new file mode 100644 index 0000000..b3e2ba6 --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Create.bru @@ -0,0 +1,25 @@ +meta { + name: Create + type: http + seq: 2 +} + +post { + url: https://{{api_base_url}}/api/todo/lists + body: json + auth: bearer +} + +auth:bearer { + token: {{token}} +} + +body:json { + { + "name": "Demo" + } +} + +vars:post-response { + listId: res.body.id +} diff --git a/backend/bruno/KSummarized/ToDo/List/Delete.bru b/backend/bruno/KSummarized/ToDo/List/Delete.bru new file mode 100644 index 0000000..996430a --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Delete.bru @@ -0,0 +1,19 @@ +meta { + name: Delete + type: http + seq: 4 +} + +delete { + url: https://{{api_base_url}}/api/todo/lists/{{listId}} + body: json + auth: bearer +} + +auth:bearer { + token: {{token}} +} + +body:json { + "ToDo" +} diff --git a/backend/bruno/KSummarized/ToDo/List/Get.bru b/backend/bruno/KSummarized/ToDo/List/Get.bru new file mode 100644 index 0000000..df97d89 --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Get.bru @@ -0,0 +1,15 @@ +meta { + name: Get + type: http + seq: 5 +} + +get { + url: https://{{api_base_url}}/api/todo/lists/{{listId}} + body: none + auth: bearer +} + +auth:bearer { + token: {{token}} +} diff --git a/backend/bruno/KSummarized/ToDo/List/List.bru b/backend/bruno/KSummarized/ToDo/List/List.bru new file mode 100644 index 0000000..7b6343c --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/List.bru @@ -0,0 +1,15 @@ +meta { + name: List + type: http + seq: 1 +} + +get { + url: https://{{api_base_url}}/api/todo/lists + body: none + auth: bearer +} + +auth:bearer { + token: {{token}} +} diff --git a/backend/bruno/KSummarized/ToDo/List/Rename.bru b/backend/bruno/KSummarized/ToDo/List/Rename.bru new file mode 100644 index 0000000..cf40331 --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Rename.bru @@ -0,0 +1,21 @@ +meta { + name: Rename + type: http + seq: 3 +} + +put { + url: https://{{api_base_url}}/api/todo/lists/{{listId}} + body: json + auth: bearer +} + +auth:bearer { + token: {{token}} +} + +body:json { + { + "name": "Renamed" + } +} diff --git a/backend/bruno/KSummarized/bruno.json b/backend/bruno/KSummarized/bruno.json new file mode 100644 index 0000000..a2739bb --- /dev/null +++ b/backend/bruno/KSummarized/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "KSummarized", + "type": "collection" +} \ No newline at end of file diff --git a/backend/bruno/KSummarized/environments/Local.bru b/backend/bruno/KSummarized/environments/Local.bru new file mode 100644 index 0000000..4dc37f4 --- /dev/null +++ b/backend/bruno/KSummarized/environments/Local.bru @@ -0,0 +1,12 @@ +vars { + keycloak_realm: KnowledgeSummarized + keycloak_url: http://localhost:8080 + api_base_url: localhost:5000 +} +vars:secret [ + token, + keycloak_client_id, + keycloak_client_secret, + keycloak_username, + keycloak_password +] diff --git a/backend/bruno/KSummarized/token.bru b/backend/bruno/KSummarized/token.bru new file mode 100644 index 0000000..064ea26 --- /dev/null +++ b/backend/bruno/KSummarized/token.bru @@ -0,0 +1,23 @@ +meta { + name: Get access token + type: http + seq: 1 +} + +post { + url: {{keycloak_url}}/realms/{{keycloak_realm}}/protocol/openid-connect/token + body: formUrlEncoded + auth: none +} + +body:form-urlencoded { + client_id: {{keycloak_client_id}} + grant_type: password + client_secret: {{keycloak_client_secret}} + username: {{keycloak_username}} + password: {{keycloak_password}} +} + +vars:post-response { + token: res.body.access_token +} diff --git a/backend/ksummarized.sln b/backend/ksummarized.sln index 58eff14..20d82c5 100644 --- a/backend/ksummarized.sln +++ b/backend/ksummarized.sln @@ -1,64 +1,43 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33213.308 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D7A1B70F-EE88-4B3C-A209-FE2517ADD026}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "api", "src\api\api.csproj", "{917A96E2-DAE3-432B-8C65-362D34194104}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A2E313A3-EE03-4A78-ABCE-42E7A4B7029A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D12C229D-2E15-43BD-951F-EF05AE7633D9}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{D21A0FAE-B25F-4975-9EBC-76E24CDA1969}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|Any CPU.Build.0 = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x64.ActiveCfg = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x64.Build.0 = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x86.ActiveCfg = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x86.Build.0 = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|Any CPU.ActiveCfg = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|Any CPU.Build.0 = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x64.ActiveCfg = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x64.Build.0 = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x86.ActiveCfg = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x86.Build.0 = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x64.ActiveCfg = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x64.Build.0 = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x86.ActiveCfg = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x86.Build.0 = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|Any CPU.Build.0 = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x64.ActiveCfg = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x64.Build.0 = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x86.ActiveCfg = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {917A96E2-DAE3-432B-8C65-362D34194104} = {D7A1B70F-EE88-4B3C-A209-FE2517ADD026} - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969} = {A2E313A3-EE03-4A78-ABCE-42E7A4B7029A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {EB55458A-327D-41D5-8211-352C32273E00} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DE2804D2-3D3D-4A41-91C7-4FEEF71A4434}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api", "src\api\api.csproj", "{949171D5-4018-4C40-AB2B-687B39F20974}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{4CA15B69-5E75-48E2-BE26-EA818B675CFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "unit", "test\unit\unit.csproj", "{B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "infrastructure", "src\infrastructure\infrastructure.csproj", "{9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {949171D5-4018-4C40-AB2B-687B39F20974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {949171D5-4018-4C40-AB2B-687B39F20974}.Debug|Any CPU.Build.0 = Debug|Any CPU + {949171D5-4018-4C40-AB2B-687B39F20974}.Release|Any CPU.ActiveCfg = Release|Any CPU + {949171D5-4018-4C40-AB2B-687B39F20974}.Release|Any CPU.Build.0 = Release|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Release|Any CPU.Build.0 = Release|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {949171D5-4018-4C40-AB2B-687B39F20974} = {DE2804D2-3D3D-4A41-91C7-4FEEF71A4434} + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC} = {4CA15B69-5E75-48E2-BE26-EA818B675CFC} + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB} = {DE2804D2-3D3D-4A41-91C7-4FEEF71A4434} + EndGlobalSection +EndGlobal diff --git a/backend/src/api/Constants/ErrorMessages.cs b/backend/src/api/Constants/ErrorMessages.cs deleted file mode 100644 index 12f82b7..0000000 --- a/backend/src/api/Constants/ErrorMessages.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace api.Constants; - -public static class ErrorMessages -{ - public const string UserAlreadyExists = "User already exists"; -} diff --git a/backend/src/api/Controllers/AuthenticationController.cs b/backend/src/api/Controllers/AuthenticationController.cs deleted file mode 100644 index de8b353..0000000 --- a/backend/src/api/Controllers/AuthenticationController.cs +++ /dev/null @@ -1,64 +0,0 @@ -using api.Constants; -using api.Data.DTO; -using api.Services.Users; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.IdentityModel.Tokens.Jwt; - -namespace api.Controllers; - -[ApiController] -[Route("/api/auth")] -public class AuthenticationController : ControllerBase -{ - private readonly IUserService _userService; - private readonly ILogger _logger; - - public AuthenticationController(IUserService userService, ILogger logger) - { - _userService = userService; - _logger = logger; - } - - [HttpGet("create-user")] - [Authorize] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task CreateUser() - { - var accessToken = Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - var jsonTokenData = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); - var keycloakUuid = jsonTokenData.Subject; - var userEmail = jsonTokenData.Claims.FirstOrDefault(claim => claim.Type == "email")?.Value; - - if (userEmail == null) - { - _logger.LogInformation("User with keycloakUuid={keycloakUuid} tried to log in without email.", keycloakUuid); - return BadRequest("Required data missing"); - } - - var (isSuccess, error) = await _userService.CreateKeycloakUser( - new UserDTO - { - KeycloakUuid = keycloakUuid, - Email = userEmail - } - ); - - if (isSuccess) - { - _logger.LogInformation("The user {email} has been created successfully.", userEmail); - return Ok("User created"); - } - else - { - if (error == ErrorMessages.UserAlreadyExists) - { - _logger.LogInformation("The user {email} has already been created.", userEmail); - return Ok("User is already created"); - } - _logger.LogInformation("Failure during creation of {email} user.", userEmail); - return BadRequest(error); - } - } -} diff --git a/backend/src/api/Controllers/GreetingsController.cs b/backend/src/api/Controllers/GreetingsController.cs deleted file mode 100644 index 665bed0..0000000 --- a/backend/src/api/Controllers/GreetingsController.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace api.Controllers; - -[ApiController] -[Route("/api/greetings")] -public class GreetingsController : ControllerBase -{ - [HttpGet] - [Authorize] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("user")] - public IActionResult Greet() - { - return Ok($"Hello {HttpContext?.User?.Identity?.Name ?? "World" }"); - } - - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("HelloWorld")] - public IActionResult HelloWorld() - { - return Ok("Hello World!"); - } -} diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs new file mode 100644 index 0000000..0a37197 --- /dev/null +++ b/backend/src/api/Controllers/TodoController.cs @@ -0,0 +1,107 @@ +using core.Ports; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace api.Controllers; + +[Authorize] +[Route("/api/todo")] +[ApiController] +public class TodoController : ControllerBase +{ + private readonly ITodoService _service; + private readonly ILogger _logger; + + public TodoController(ITodoService service, ILogger logger) + { + _service = service; + _logger = logger; + } + + [HttpGet("lists")] + public IActionResult GetLists() + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} requested his lists", userId); + return userId switch + { + null => Unauthorized(), + var user => Ok(_service.GetLists(user)), + }; + } + + [HttpGet("lists/{id}")] + public IActionResult GetList([FromRoute] int id) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} requested his list: {id}", userId, id); + if (userId is null) { return Unauthorized(); } + var list = _service.GetList(userId, id); + return list switch + { + null => NotFound(), + var user => Ok(list), + }; + } + + [HttpDelete("lists/{id}")] + public IActionResult DeleteList([FromRoute] int id) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} deleted his list: {id}", userId, id); + if (userId is null) { return Unauthorized(); } + var success = _service.DeleteList(userId, id); + if (success) + { + return Ok(); + } + return BadRequest(); + } + + [HttpPost("lists")] + public async Task CreateLists([FromBody] Request request) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} created: {list}", userId, request.Name); + return userId switch + { + null => Unauthorized(), + var user => await Create(user, request.Name), + }; + + async Task Create(string user, string name) + { + //TODO: return DTO instead of DAO + var list = await _service.CreateList(user, name); + return Created(HttpContext.Request.Path.Add(new PathString($"/{list.Id}")), list); + } + } + + [HttpPut("lists/{id}")] + public async Task RenameList([FromRoute] int id, [FromBody] Request request) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} renamed: {id} to: {list}", userId, id, request.Name); + return userId switch + { + null => Unauthorized(), + var user => await Rename(user, id, request.Name), + }; + + async Task Rename(string user, int id, string name) + { + //TODO: return DTO instead of DAO + var list = await _service.RenameList(user, id, name); + if (list) + { + return Ok(); + } + return BadRequest(); + } + } +} + +public class Request +{ + public required string Name { get; set; } +} diff --git a/backend/src/api/Data/DAO/UserModel.cs b/backend/src/api/Data/DAO/UserModel.cs deleted file mode 100644 index 12253d3..0000000 --- a/backend/src/api/Data/DAO/UserModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace api.Data.DAO; - -public class UserModel -{ - [Key] - public int Id { get; set; } - public required string KeycloakUuid { get; set; } - [EmailAddress] - public required string Email { get; set; } -} diff --git a/backend/src/api/Data/DTO/TodoListDTO.cs b/backend/src/api/Data/DTO/TodoListDTO.cs new file mode 100644 index 0000000..275e8d4 --- /dev/null +++ b/backend/src/api/Data/DTO/TodoListDTO.cs @@ -0,0 +1,3 @@ +namespace api.Data.DTO; + +public record TodoListDTO(int Id, string Name); diff --git a/backend/src/api/Data/DTO/UserDTO.cs b/backend/src/api/Data/DTO/UserDTO.cs deleted file mode 100644 index 7079db2..0000000 --- a/backend/src/api/Data/DTO/UserDTO.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace api.Data.DTO; - -public class UserDTO -{ - [Required] - public required string KeycloakUuid { get; set; } - [Required] - [EmailAddress] - public required string Email { get; set; } -} diff --git a/backend/src/api/Data/KeycloakJwtOptions.cs b/backend/src/api/Data/KeycloakJwtOptions.cs deleted file mode 100644 index a72287e..0000000 --- a/backend/src/api/Data/KeycloakJwtOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace api.Data; -public class KeycloakJwtOptions -{ - public required string Issuer { get; set; } - public required string Audience { get; set; } - public required string Secret { get; set; } - - [SetsRequiredMembers] - public KeycloakJwtOptions(string issuer, string audience, string secret) - { - Issuer = issuer; - Audience = audience; - Secret = secret; - } -} diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index 681bf74..4e0557e 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -1,111 +1,105 @@ -using api.Data; -using api.Services.Users; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; -using Microsoft.EntityFrameworkCore; -using Serilog; -using api; -using System.Security.Cryptography; - -const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; -Log.Logger = new LoggerConfiguration().Enrich.WithCorrelationId() - .WriteTo - .Console(outputTemplate: logFormat) - .CreateLogger(); - -try -{ - var builder = WebApplication.CreateBuilder(args); - builder.Services.AddHttpContextAccessor(); - builder.Host.UseSerilog(); - builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"))); - var keycloakJwtOptions = new KeycloakJwtOptions( - builder.Configuration.GetValue("KeycloakJwt:Issuer")!, - builder.Configuration.GetValue("KeycloakJwt:Audience")!, - builder.Configuration.GetValue("KeycloakJwt:Secret")! - ); - if(keycloakJwtOptions.Secret is null || keycloakJwtOptions.Issuer is null || keycloakJwtOptions.Audience is null) - { - throw new Exception("Can't start application without JWT options"); - } - - // Create RSA key for offline validation of Keycloak token - RSA rsa = RSA.Create(); - rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(keycloakJwtOptions.Secret), out _); - var rsaKeycloakSecurityKey = new RsaSecurityKey(rsa) - { - KeyId = Guid.NewGuid().ToString() - }; - - var tokenValidationParameters = new TokenValidationParameters() - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = rsaKeycloakSecurityKey, - ValidAudience = keycloakJwtOptions.Audience, - ValidateAudience = true, - ValidIssuer = keycloakJwtOptions.Issuer, - ValidateIssuer = true, - ValidateLifetime = true - }; - builder.Services.AddSingleton(tokenValidationParameters); - builder.Services.AddSingleton(keycloakJwtOptions); - - builder.Services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(options => - { - options.SaveToken = true; - options.RequireHttpsMetadata = true; - options.TokenValidationParameters = tokenValidationParameters; - }); - - builder.Services.AddControllers(); - builder.Services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); - }); - builder.Services.AddScoped(); - - builder.Services.AddCors(options => - { - options.AddPolicy(name: "AllowAll", builder => - { - builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); - }); - }); - - var app = builder.Build(); - - if (app.Environment.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1")); - } - - app.UseCors("AllowAll"); - app.UseAuthentication(); - - app.UseHttpsRedirection(); - app.UseHsts(); - - app.UseSerilogRequestLogging(); - app.UseRouting(); - app.UseAuthorization(); - - app.MapControllers(); - - app.Run(); -} -catch (Exception ex) -{ - Log.Fatal("Error starting the application: {Exception}", ex); -} -finally -{ - Log.CloseAndFlush(); -} +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using Serilog; +using System.Security.Cryptography; +using api; +using infrastructure.Data; +using infrastructure.Keycloak; +using core.Ports; + +const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; +Log.Logger = new LoggerConfiguration().Enrich.WithCorrelationId() + .WriteTo + .Console(outputTemplate: logFormat) + .CreateLogger(); + +try +{ + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddHttpContextAccessor(); + builder.Host.UseSerilog(); + builder.Services.AddDbContext( + options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"), + x => x.MigrationsAssembly("infrastructure") + )); + var keycloakJwtOptions = builder.Configuration.GetRequiredSection("KeycloakJwt").Get()!; + + // Create RSA key for offline validation of Keycloak token + RSA rsa = RSA.Create(); + rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(keycloakJwtOptions.Secret), out _); + var rsaKeycloakSecurityKey = new RsaSecurityKey(rsa) + { + KeyId = Guid.NewGuid().ToString() + }; + + var tokenValidationParameters = new TokenValidationParameters() + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = rsaKeycloakSecurityKey, + ValidAudience = keycloakJwtOptions.Audience, + ValidateAudience = true, + ValidIssuer = keycloakJwtOptions.Issuer, + ValidateIssuer = true, + ValidateLifetime = true + }; + + builder.Services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.SaveToken = true; + options.RequireHttpsMetadata = true; + options.TokenValidationParameters = tokenValidationParameters; + }); + + builder.Services.AddControllers(); + builder.Services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); + }); + builder.Services.AddScoped(); + + builder.Services.AddCors(options => + { + options.AddPolicy(name: "AllowAll", builder => + { + builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); + }); + }); + + var app = builder.Build(); + + if (app.Environment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1")); + } + + app.UseCors("AllowAll"); + app.UseAuthentication(); + + app.UseHttpsRedirection(); + app.UseHsts(); + + app.UseSerilogRequestLogging(); + app.UseRouting(); + app.UseAuthorization(); + + app.MapControllers(); + + app.Run(); +} +catch (Exception ex) +{ + Log.Fatal("Error starting the application: {Exception}", ex); +} +finally +{ + Log.CloseAndFlush(); +} diff --git a/backend/src/api/RequestExtensions.cs b/backend/src/api/RequestExtensions.cs new file mode 100644 index 0000000..2d45f01 --- /dev/null +++ b/backend/src/api/RequestExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.IdentityModel.JsonWebTokens; + +namespace api; + +public static class RequestExtensions +{ + public static string? UserId(this HttpRequest request) + { + try + { + var accessToken = request.Headers.Authorization.ToString().Split(' ')[1]; + var jsonTokenData = new JsonWebTokenHandler().ReadJsonWebToken(accessToken); + return jsonTokenData.Subject; + } + catch + { + return null; + } + } +} diff --git a/backend/src/api/Services/Users/IUserService.cs b/backend/src/api/Services/Users/IUserService.cs deleted file mode 100644 index 5b09ba7..0000000 --- a/backend/src/api/Services/Users/IUserService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using api.Data.DTO; - -namespace api.Services.Users; - -public interface IUserService -{ - Task<(bool isSuccess, string? error)> CreateKeycloakUser(UserDTO user); -} diff --git a/backend/src/api/Services/Users/UserService.cs b/backend/src/api/Services/Users/UserService.cs deleted file mode 100644 index 08d440f..0000000 --- a/backend/src/api/Services/Users/UserService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using api.Data; -using api.Data.DTO; -using api.Data.DAO; -using api.Constants; - -namespace api.Services.Users; - -public class UserService : IUserService -{ - private readonly ApplicationDbContext _applicationDbContext; - - public UserService(ApplicationDbContext applicationDbContext) - { - _applicationDbContext = applicationDbContext; - } - - public async Task<(bool isSuccess, string? error)> CreateKeycloakUser(UserDTO user) - { - var userInstance = _applicationDbContext.Users.FirstOrDefault(userInstance => userInstance.KeycloakUuid.Equals(user.KeycloakUuid)); - if (userInstance != null) - { - return (false, ErrorMessages.UserAlreadyExists); - } - var newUser = new UserModel() - { - KeycloakUuid = user.KeycloakUuid, - Email = user.Email - }; - await _applicationDbContext.Users.AddAsync(newUser); - await _applicationDbContext.SaveChangesAsync(); - return (true, null); - } -} diff --git a/backend/src/api/api.csproj b/backend/src/api/api.csproj index 217deeb..47086bf 100644 --- a/backend/src/api/api.csproj +++ b/backend/src/api/api.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 f7cb2e8e-61a4-4a0a-9259-839230fcfe2d Linux enable @@ -9,18 +9,26 @@ - - - - - all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all - - + + - + + + + + + + + + + diff --git a/backend/src/core/Ports/ITodoService.cs b/backend/src/core/Ports/ITodoService.cs new file mode 100644 index 0000000..21ef07a --- /dev/null +++ b/backend/src/core/Ports/ITodoService.cs @@ -0,0 +1,10 @@ +namespace core.Ports; + +public interface ITodoService +{ + Task CreateList(string user, string name); + IEnumerable GetLists(string userId); + TodoList? GetList(string userId, int id); + bool DeleteList(string userId, int id); + Task RenameList(string user, int id, string name); +} diff --git a/backend/src/core/TodoList.cs b/backend/src/core/TodoList.cs new file mode 100644 index 0000000..8392f04 --- /dev/null +++ b/backend/src/core/TodoList.cs @@ -0,0 +1,10 @@ +namespace core; + +public class TodoList +{ + public int Id { get; set; } + public required string Name { get; set; } + public required Guid Owner { get; set; } + + public bool IsAuthorized(Guid person) => Owner.Equals(person); +} diff --git a/backend/src/core/core.csproj b/backend/src/core/core.csproj new file mode 100644 index 0000000..bb23fb7 --- /dev/null +++ b/backend/src/core/core.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/backend/src/api/Data/ApplicationDbContext.cs b/backend/src/infrastructure/Data/ApplicationDbContext.cs similarity index 57% rename from backend/src/api/Data/ApplicationDbContext.cs rename to backend/src/infrastructure/Data/ApplicationDbContext.cs index 0ec7301..df8dfd3 100644 --- a/backend/src/api/Data/ApplicationDbContext.cs +++ b/backend/src/infrastructure/Data/ApplicationDbContext.cs @@ -1,13 +1,11 @@ -using api.Data.DAO; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -namespace api.Data; +namespace infrastructure.Data; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } - public DbSet Users { get; set; } + public DbSet TodoLists { get; set; } } diff --git a/backend/src/infrastructure/Data/TodoListModel.cs b/backend/src/infrastructure/Data/TodoListModel.cs new file mode 100644 index 0000000..d838b5b --- /dev/null +++ b/backend/src/infrastructure/Data/TodoListModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace infrastructure.Data; + +[Table("todo_lists")] +public class TodoListModel +{ + [Key] + public int Id { get; set; } + [MaxLength(512)] + public required string Name { get; set; } + public required Guid Owner { get; set; } +} diff --git a/backend/src/infrastructure/Data/TodoService.cs b/backend/src/infrastructure/Data/TodoService.cs new file mode 100644 index 0000000..3d2dcc0 --- /dev/null +++ b/backend/src/infrastructure/Data/TodoService.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore; +using core.Ports; +using core; + +namespace infrastructure.Data; + +public class TodoService : ITodoService +{ + + private readonly ApplicationDbContext _context; + + public TodoService(ApplicationDbContext context) + { + _context = context; + } + + public IEnumerable GetLists(string userId) + { + return _context.TodoLists.AsNoTracking() + .Where(list => list.Owner.Equals(Guid.Parse(userId))) + .Select(list => new TodoList { Id = list.Id, Name = list.Name, Owner = list.Owner }) + .AsEnumerable(); + } + + public TodoList? GetList(string userId, int id) + { + var list = _context.TodoLists.AsNoTracking() + .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + //TODO: Consider creating a mapper instead of this manual new + if (list is not null) { return new() { Id = list.Id, Name = list.Name, Owner = list.Owner }; } + return null; + } + + public async Task CreateList(string user, string name) + { + var newList = new TodoListModel() { Name = name, Owner = Guid.Parse(user) }; + _context.TodoLists.Add(newList); + await _context.SaveChangesAsync(); + return new TodoList() { Id = newList.Id, Name = newList.Name, Owner = newList.Owner }; + } + + public bool DeleteList(string userId, int id) + { + var list = _context.TodoLists + .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + if (list is null) { return false; } + _context.TodoLists.Remove(list); + _context.SaveChanges(); + return true; + } + + public async Task RenameList(string userId, int id, string name) + { + var list = _context.TodoLists + .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + + if (list is null) { return false; } + list.Name = name; + await _context.SaveChangesAsync(); + return true; + } +} diff --git a/backend/src/infrastructure/Keycloack/KeycloakJwtOptions.cs b/backend/src/infrastructure/Keycloack/KeycloakJwtOptions.cs new file mode 100644 index 0000000..83a139c --- /dev/null +++ b/backend/src/infrastructure/Keycloack/KeycloakJwtOptions.cs @@ -0,0 +1,7 @@ +namespace infrastructure.Keycloak; +public class KeycloakJwtOptions +{ + public required string Issuer { get; init; } + public required string Audience { get; init; } + public required string Secret { get; init; } +} diff --git a/backend/src/api/Migrations/20240223084836_Init.Designer.cs b/backend/src/infrastructure/Migrations/20240330212015_TodoList.Designer.cs similarity index 64% rename from backend/src/api/Migrations/20240223084836_Init.Designer.cs rename to backend/src/infrastructure/Migrations/20240330212015_TodoList.Designer.cs index a363f14..ba9fe02 100644 --- a/backend/src/api/Migrations/20240223084836_Init.Designer.cs +++ b/backend/src/infrastructure/Migrations/20240330212015_TodoList.Designer.cs @@ -1,30 +1,31 @@ -// +// +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using api.Data; +using infrastructure.Data; #nullable disable -namespace api.Migrations +namespace infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20240223084836_Init")] - partial class Init + [Migration("20240330212015_TodoList")] + partial class TodoList { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("ProductVersion", "8.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("api.Data.DAO.UserModel", b => + modelBuilder.Entity("api.Data.DAO.TodoListModel", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -32,17 +33,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("Email") + b.Property("Name") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(512) + .HasColumnType("character varying(512)"); - b.Property("KeycloakUuid") - .IsRequired() - .HasColumnType("text"); + b.Property("Owner") + .HasColumnType("uuid"); b.HasKey("Id"); - b.ToTable("Users"); + b.ToTable("todo_lists"); }); #pragma warning restore 612, 618 } diff --git a/backend/src/api/Migrations/20240223084836_Init.cs b/backend/src/infrastructure/Migrations/20240330212015_TodoList.cs similarity index 62% rename from backend/src/api/Migrations/20240223084836_Init.cs rename to backend/src/infrastructure/Migrations/20240330212015_TodoList.cs index 9f4ae7a..13b4cef 100644 --- a/backend/src/api/Migrations/20240223084836_Init.cs +++ b/backend/src/infrastructure/Migrations/20240330212015_TodoList.cs @@ -1,28 +1,29 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using System; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace api.Migrations +namespace infrastructure.Migrations { /// - public partial class Init : Migration + public partial class TodoList : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "Users", + name: "todo_lists", columns: table => new { Id = table.Column(type: "integer", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - KeycloakUuid = table.Column(type: "text", nullable: false), - Email = table.Column(type: "text", nullable: false) + Name = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Owner = table.Column(type: "uuid", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Users", x => x.Id); + table.PrimaryKey("PK_todo_lists", x => x.Id); }); } @@ -30,7 +31,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "Users"); + name: "todo_lists"); } } } diff --git a/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs similarity index 66% rename from backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs rename to backend/src/infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index c339c3c..42e6371 100644 --- a/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,13 +1,14 @@ -// +// +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using api.Data; +using infrastructure.Data; #nullable disable -namespace api.Migrations +namespace infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] partial class ApplicationDbContextModelSnapshot : ModelSnapshot @@ -16,12 +17,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("ProductVersion", "8.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("api.Data.DAO.UserModel", b => + modelBuilder.Entity("api.Data.DAO.TodoListModel", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -29,17 +30,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("Email") + b.Property("Name") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(512) + .HasColumnType("character varying(512)"); - b.Property("KeycloakUuid") - .IsRequired() - .HasColumnType("text"); + b.Property("Owner") + .HasColumnType("uuid"); b.HasKey("Id"); - b.ToTable("Users"); + b.ToTable("todo_lists"); }); #pragma warning restore 612, 618 } diff --git a/backend/src/infrastructure/infrastructure.csproj b/backend/src/infrastructure/infrastructure.csproj new file mode 100644 index 0000000..7d93446 --- /dev/null +++ b/backend/src/infrastructure/infrastructure.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/backend/test/ksummarized.UnitTests/UnitTest1.cs b/backend/test/ksummarized.UnitTests/UnitTest1.cs deleted file mode 100644 index 33840dd..0000000 --- a/backend/test/ksummarized.UnitTests/UnitTest1.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace ksummarized.UnitTests -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void TestMethod1() - { - } - } -} diff --git a/backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj b/backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj deleted file mode 100644 index d6f1239..0000000 --- a/backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net7.0 - - false - - - - - - - - - - diff --git a/backend/UnitTests/UnitTest1.cs b/backend/test/unit/UnitTest1.cs similarity index 75% rename from backend/UnitTests/UnitTest1.cs rename to backend/test/unit/UnitTest1.cs index d88634b..d334e44 100644 --- a/backend/UnitTests/UnitTest1.cs +++ b/backend/test/unit/UnitTest1.cs @@ -1,10 +1,10 @@ -namespace UnitTests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } +namespace unit; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } } \ No newline at end of file diff --git a/backend/test/unit/unit.csproj b/backend/test/unit/unit.csproj new file mode 100644 index 0000000..3aa9860 --- /dev/null +++ b/backend/test/unit/unit.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/frontend/src/helpers/RequireAuth.tsx b/frontend/src/helpers/RequireAuth.tsx index 3014338..8bfa471 100644 --- a/frontend/src/helpers/RequireAuth.tsx +++ b/frontend/src/helpers/RequireAuth.tsx @@ -7,21 +7,6 @@ import Constants from "./Constants"; export default function RequireAuth() { const { isLogin, keycloak } = useAuth(); - - const createUserInBackend = async () => { - const response = await fetch(`${Constants.BASE_URL}/auth/create-user`, { - headers: { Authorization: `Bearer ${keycloak!.token}` }, - }); - if (response.status !== 200) { - const errorMessage = await response.text(); - throw new Error(errorMessage); - } - }; - - if (isLogin) { - createUserInBackend(); - } - return isLogin ? :

Authenticating...

; } diff --git a/global.json b/global.json deleted file mode 100644 index 8058dfb..0000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "7.0.405", - "rollForward": "latestFeature" - } -} diff --git a/scripts/apply_migrations.ps1 b/scripts/apply_migrations.ps1 index 201925c..773a305 100755 --- a/scripts/apply_migrations.ps1 +++ b/scripts/apply_migrations.ps1 @@ -11,9 +11,9 @@ get-content ../.env | foreach { Write-Host "Starting DB" docker compose up db -d 2>&1 > $null Write-Output "Generateing migration script" -Set-Location ../backend/src/api +Set-Location ../backend mkdir migration_scripts 2>&1 > $null -dotnet ef migrations script --idempotent -o ./migration_scripts/migration.sql 2>&1 > $null +dotnet ef migrations script --idempotent -o ./migration_scripts/migration.sql -p ./src/infrastructure -s ./src/api Write-Output "Applying.." $passwd = "PGPASSWORD=" + $Env:POSTGRES_PASSWORD @@ -24,7 +24,7 @@ docker run --network host -e $passwd -v ${src}:/migrations/ --rm postgres ` Write-Output "Cleanup" Remove-Item -Recurse -Force migration_scripts -Set-Location ../../../scripts +Set-Location ../scripts Write-Output "Migrations had been applyed!" Write-Host "Stoping DB" docker stop ks-database 2>&1 > $null