From c28b248b7fe262bf0f98b67085678dc563a65223 Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Mon, 27 Apr 2026 02:54:46 +0300 Subject: [PATCH 01/16] feat: add CI for backend, fix tests --- .dockerignore | 9 ++ .github/workflows/pr-validation.yml | 33 +++++ .gitignore | 2 +- SmartEventBooking.slnx | 8 +- src/api/SmartEventBooking.Web/Dockerfile | 27 ++++ src/client/angular.json | 3 +- src/client/package-lock.json | 140 +++--------------- .../Persistence/IdentitySeederTests.cs | 0 ...ing.Infrastructure.IntegrationTests.csproj | 8 +- .../Helpers/MockHelpers.cs | 0 .../Identity/AuthServiceTests.cs | 0 ...entBooking.Infrastructure.UnitTests.csproj | 10 +- .../Controllers/AuthControllerTests.cs | 116 +++++---------- .../Controllers/HomeControllerTests.cs | 43 +++--- .../SmartEventBooking.Web.Tests.csproj | 3 +- 15 files changed, 162 insertions(+), 240 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/pr-validation.yml create mode 100644 src/api/SmartEventBooking.Web/Dockerfile rename {SmartEventBooking.Infrastructure.IntegrationTests => tests/SmartEventBooking.Infrastructure.IntegrationTests}/Persistence/IdentitySeederTests.cs (100%) rename {SmartEventBooking.Infrastructure.IntegrationTests => tests/SmartEventBooking.Infrastructure.IntegrationTests}/SmartEventBooking.Infrastructure.IntegrationTests.csproj (65%) rename {SmartEventBooking.Infrastructure.UnitTests => tests/SmartEventBooking.Infrastructure.UnitTests}/Helpers/MockHelpers.cs (100%) rename {SmartEventBooking.Infrastructure.UnitTests => tests/SmartEventBooking.Infrastructure.UnitTests}/Identity/AuthServiceTests.cs (100%) rename {SmartEventBooking.Infrastructure.UnitTests => tests/SmartEventBooking.Infrastructure.UnitTests}/SmartEventBooking.Infrastructure.UnitTests.csproj (65%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e031e1f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.vs +**/.vscode +**/bin +**/obj +**/Dockerfile \ No newline at end of file diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml new file mode 100644 index 0000000..495b94c --- /dev/null +++ b/.github/workflows/pr-validation.yml @@ -0,0 +1,33 @@ +name: PR Validation + +on: + pull_request: + branches: + - develop + - main + +jobs: + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + + - name: Restore dependencies + run: dotnet restore -p:Configuration=Release + + - name: Build project + run: dotnet build --no-restore --configuration Release + + - name: Run Unit & Integration Tests + run: dotnet test --no-build --configuration Release --verbosity normal + + - name: Verify Dockerfile builds successfully + run: docker build -t smarteventbooking:test -f src/api/SmartEventBooking.Web/Dockerfile . \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3101c45..bb85a2a 100644 --- a/.gitignore +++ b/.gitignore @@ -418,4 +418,4 @@ FodyWeavers.xsd *.msp docker-compose.yml -Dockerfile \ No newline at end of file +#Dockerfile \ No newline at end of file diff --git a/SmartEventBooking.slnx b/SmartEventBooking.slnx index 4537440..1669172 100644 --- a/SmartEventBooking.slnx +++ b/SmartEventBooking.slnx @@ -8,12 +8,8 @@ - - - - - - + + diff --git a/src/api/SmartEventBooking.Web/Dockerfile b/src/api/SmartEventBooking.Web/Dockerfile new file mode 100644 index 0000000..93cb69a --- /dev/null +++ b/src/api/SmartEventBooking.Web/Dockerfile @@ -0,0 +1,27 @@ +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base +WORKDIR /app +EXPOSE 8080 + +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +COPY ["src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj", "src/api/SmartEventBooking.Web/"] +COPY ["src/api/SmartEventBooking.Application/SmartEventBooking.Application.csproj", "src/api/SmartEventBooking.Application/"] +COPY ["src/api/SmartEventBooking.Domain/SmartEventBooking.Domain.csproj", "src/api/SmartEventBooking.Domain/"] +COPY ["src/api/SmartEventBooking.Infrastructure/SmartEventBooking.Infrastructure.csproj", "src/api/SmartEventBooking.Infrastructure/"] +COPY ["src/api/SmartEventBooking.Shared/SmartEventBooking.Shared.csproj", "src/api/SmartEventBooking.Shared/"] + +RUN dotnet restore "src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj" + +COPY . . +WORKDIR "/src/src/api/SmartEventBooking.Web" + +RUN dotnet build "SmartEventBooking.Web.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "SmartEventBooking.Web.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "SmartEventBooking.Web.dll"] \ No newline at end of file diff --git a/src/client/angular.json b/src/client/angular.json index c71771d..d14c691 100644 --- a/src/client/angular.json +++ b/src/client/angular.json @@ -2,7 +2,8 @@ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "cli": { - "packageManager": "npm" + "packageManager": "npm", + "analytics": false }, "newProjectRoot": "projects", "projects": { diff --git a/src/client/package-lock.json b/src/client/package-lock.json index d2902e5..bc1d9ce 100644 --- a/src/client/package-lock.json +++ b/src/client/package-lock.json @@ -463,6 +463,7 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.9.tgz", "integrity": "sha512-7spQcF3hPN/fjTx6Pwa32KRRdO0NcixnRuPV4lo50ejtXesjiLVR+fkaX38sawAyGoq89IuuYvUDrbLwCMypmQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -479,6 +480,7 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.9.tgz", "integrity": "sha512-clsK1EsSPtAuqlRl4CciA/gsvsW7xe0eWcvHxtrMW6DYaUJ6X4AAuDxEEJ5cf/3Mpw4s8KssjIUPPtbrUIGLSQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -492,6 +494,7 @@ "integrity": "sha512-hTTW/OiqTXrwTneS18CMp47OX0XSbLYl2rIomLS3nXVJniSETH6S/k+LqQtGWWgLbzsd3PzUOOckHnvzpTBTsA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "7.29.0", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -524,6 +527,7 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.9.tgz", "integrity": "sha512-uZLq2aedJ+0uEZxyf6a1Nc7y1aZ7akAW7K1Kon8JUDZOvI2IDbk0i00MzkELt8q9uSmSSqg9zNKuhjspFf0Pyw==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -568,6 +572,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.9.tgz", "integrity": "sha512-MjEtFvoFtsjsAeu2yzauqGgwwEHV4ml25c9vGFmw4OmSoNme4yp41f2DegwOkn1TTHL3OF3GE65ng2U2feJU4Q==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -692,6 +697,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1055,6 +1061,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -1103,35 +1110,11 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1139,7 +1122,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1871,6 +1853,7 @@ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -2442,9 +2425,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2462,9 +2442,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2482,9 +2459,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2502,9 +2476,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2522,9 +2493,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2542,9 +2510,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2562,9 +2527,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3008,9 +2970,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3032,9 +2991,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3056,9 +3012,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3080,9 +3033,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3104,9 +3054,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3128,9 +3075,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3308,9 +3252,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3328,9 +3269,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3348,9 +3286,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3368,9 +3303,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3547,9 +3479,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3564,9 +3493,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3581,9 +3507,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3598,9 +3521,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3615,9 +3535,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3632,9 +3549,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3649,9 +3563,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3666,9 +3577,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3683,9 +3591,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3700,9 +3605,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3717,9 +3619,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3734,9 +3633,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3751,9 +3647,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4413,6 +4306,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -4564,6 +4458,7 @@ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -5252,6 +5147,7 @@ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5580,6 +5476,7 @@ "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -5921,6 +5818,7 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -6029,6 +5927,7 @@ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", @@ -7126,6 +7025,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7439,6 +7339,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -8063,7 +7964,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tuf-js": { "version": "4.1.0", @@ -8101,6 +8003,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8186,6 +8089,7 @@ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8261,6 +8165,7 @@ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.1.5", "@vitest/mocker": "4.1.5", @@ -8653,6 +8558,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/SmartEventBooking.Infrastructure.IntegrationTests/Persistence/IdentitySeederTests.cs b/tests/SmartEventBooking.Infrastructure.IntegrationTests/Persistence/IdentitySeederTests.cs similarity index 100% rename from SmartEventBooking.Infrastructure.IntegrationTests/Persistence/IdentitySeederTests.cs rename to tests/SmartEventBooking.Infrastructure.IntegrationTests/Persistence/IdentitySeederTests.cs diff --git a/SmartEventBooking.Infrastructure.IntegrationTests/SmartEventBooking.Infrastructure.IntegrationTests.csproj b/tests/SmartEventBooking.Infrastructure.IntegrationTests/SmartEventBooking.Infrastructure.IntegrationTests.csproj similarity index 65% rename from SmartEventBooking.Infrastructure.IntegrationTests/SmartEventBooking.Infrastructure.IntegrationTests.csproj rename to tests/SmartEventBooking.Infrastructure.IntegrationTests/SmartEventBooking.Infrastructure.IntegrationTests.csproj index a7d6e10..dfadfa3 100644 --- a/SmartEventBooking.Infrastructure.IntegrationTests/SmartEventBooking.Infrastructure.IntegrationTests.csproj +++ b/tests/SmartEventBooking.Infrastructure.IntegrationTests/SmartEventBooking.Infrastructure.IntegrationTests.csproj @@ -22,10 +22,10 @@ - - - - + + + + \ No newline at end of file diff --git a/SmartEventBooking.Infrastructure.UnitTests/Helpers/MockHelpers.cs b/tests/SmartEventBooking.Infrastructure.UnitTests/Helpers/MockHelpers.cs similarity index 100% rename from SmartEventBooking.Infrastructure.UnitTests/Helpers/MockHelpers.cs rename to tests/SmartEventBooking.Infrastructure.UnitTests/Helpers/MockHelpers.cs diff --git a/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs b/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs similarity index 100% rename from SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs rename to tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs diff --git a/SmartEventBooking.Infrastructure.UnitTests/SmartEventBooking.Infrastructure.UnitTests.csproj b/tests/SmartEventBooking.Infrastructure.UnitTests/SmartEventBooking.Infrastructure.UnitTests.csproj similarity index 65% rename from SmartEventBooking.Infrastructure.UnitTests/SmartEventBooking.Infrastructure.UnitTests.csproj rename to tests/SmartEventBooking.Infrastructure.UnitTests/SmartEventBooking.Infrastructure.UnitTests.csproj index 31f2528..8bc9eae 100644 --- a/SmartEventBooking.Infrastructure.UnitTests/SmartEventBooking.Infrastructure.UnitTests.csproj +++ b/tests/SmartEventBooking.Infrastructure.UnitTests/SmartEventBooking.Infrastructure.UnitTests.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable false @@ -10,7 +10,7 @@ - + @@ -18,9 +18,9 @@ - - - + + + diff --git a/tests/SmartEventBooking.Web.Tests/Controllers/AuthControllerTests.cs b/tests/SmartEventBooking.Web.Tests/Controllers/AuthControllerTests.cs index 3a963c6..70b253c 100644 --- a/tests/SmartEventBooking.Web.Tests/Controllers/AuthControllerTests.cs +++ b/tests/SmartEventBooking.Web.Tests/Controllers/AuthControllerTests.cs @@ -1,101 +1,53 @@ +using FluentAssertions; using Microsoft.AspNetCore.Mvc; using Moq; -using FluentAssertions; -using SmartEventBooking.Web.Controllers; using SmartEventBooking.Application.Abstractions.Identity; using SmartEventBooking.Application.DTOs.Auth; -using Microsoft.AspNetCore.Http; -using System.Security.Claims; - -namespace SmartEventBooking.Web.Tests.Controllers -{ - public class AuthControllerTests - { - private readonly Mock _authServiceMock; - private readonly AuthController _controller; - - public AuthControllerTests() - { - _authServiceMock = new Mock(); - _controller = new AuthController(_authServiceMock.Object); - - var user = new ClaimsPrincipal(new ClaimsIdentity()); - _controller.ControllerContext = new ControllerContext - { - HttpContext = new DefaultHttpContext { User = user } - }; - } - - [Fact] - public void Register_ReturnsViewResult_WhenNotAuthenticated() - { - var result = _controller.Register(); - - result.Should().BeOfType(); - } +using SmartEventBooking.Web.Controllers; - [Fact] - public async Task Register_Post_ReturnsViewResult_WhenModelStateIsInvalid() - { - _controller.ModelState.AddModelError("Email", "Required"); - var dto = new RegisterDto - { - Email = "", - Password = "123", - ConfirmPassword = "123", - FirstName = "Test" - }; +namespace SmartEventBooking.Web.Tests.Controllers; - var result = await _controller.Register(dto); +public class AuthControllerTests +{ + private readonly Mock _authServiceMock = new(); - result.Should().BeOfType(); - var viewResult = result as ViewResult; - viewResult?.Model.Should().Be(dto); - } + private AuthController CreateController() => new(_authServiceMock.Object); - [Fact] - public async Task Register_Post_RedirectsToHome_WhenRegistrationSucceeds() + [Fact] + public async Task Register_ReturnsOk_WhenRegistrationSucceeds() + { + var dto = new RegisterDto { - var dto = new RegisterDto - { - Email = "test@test.com", - Password = "Password123!", - ConfirmPassword = "Password123!", - FirstName = "Ivan" - }; + Email = "test@example.com", + Password = "Password123!", + ConfirmPassword = "Password123!", + FirstName = "Ivan" + }; - _authServiceMock.Setup(x => x.RegisterAsync(dto)) - .ReturnsAsync(new AuthResultDto { Succeeded = true }); + _authServiceMock.Setup(x => x.RegisterAsync(dto)) + .ReturnsAsync(new AuthResultDto { Succeeded = true }); - var result = await _controller.Register(dto); + var result = await CreateController().Register(dto); - result.Should().BeOfType(); - var redirectResult = result as RedirectToActionResult; - redirectResult?.ActionName.Should().Be("Index"); - redirectResult?.ControllerName.Should().Be("Home"); - } + result.Should().BeOfType(); + } - [Fact] - public async Task Register_Post_ReturnsViewWithErrors_WhenRegistrationFails() + [Fact] + public async Task Register_ReturnsBadRequest_WhenRegistrationFails() + { + var dto = new RegisterDto { - var dto = new RegisterDto - { - Email = "test@test.com", - Password = "Password123!", - ConfirmPassword = "Password123!", - FirstName = "Ivan" - }; + Email = "test@example.com", + Password = "Password123!", + ConfirmPassword = "Password123!", + FirstName = "Ivan" + }; - _authServiceMock.Setup(x => x.RegisterAsync(dto)) - .ReturnsAsync(new AuthResultDto { Succeeded = false, Errors = new[] { "Error 1" } }); + _authServiceMock.Setup(x => x.RegisterAsync(dto)) + .ReturnsAsync(new AuthResultDto { Succeeded = false, Errors = ["Email already taken"] }); - var result = await _controller.Register(dto); + var result = await CreateController().Register(dto); - result.Should().BeOfType(); - _controller.ModelState.IsValid.Should().BeFalse(); - _controller.ModelState.Values.SelectMany(v => v.Errors) - .Should().ContainSingle() - .Which.ErrorMessage.Should().Be("Error 1"); - } + result.Should().BeOfType(); } } diff --git a/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs b/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs index c83573b..0c534ed 100644 --- a/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs +++ b/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs @@ -3,46 +3,43 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging.Abstractions; using SmartEventBooking.Web.Controllers; -using SmartEventBooking.Web.Models; namespace SmartEventBooking.Web.Tests.Controllers; public class HomeControllerTests { + private static HomeController CreateController() => + new(NullLogger.Instance) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + [Fact] - public void Index_ReturnsViewResult() + public void Index_ReturnsOk() { - var controller = new HomeController(NullLogger.Instance); - - var result = controller.Index(); + var result = CreateController().Index(); - result.Should().BeOfType(); + result.Should().BeOfType(); } [Fact] - public void Privacy_ReturnsViewResult() + public void Privacy_ReturnsOk() { - var controller = new HomeController(NullLogger.Instance); + var result = CreateController().Privacy(); - var result = controller.Privacy(); - - result.Should().BeOfType(); + result.Should().BeOfType(); } [Fact] - public void Error_ReturnsViewWithErrorViewModel() + public void Error_ReturnsObjectResult_WithProblemDetails() { - var controller = new HomeController(NullLogger.Instance) - { - ControllerContext = new ControllerContext - { - HttpContext = new DefaultHttpContext() - } - }; - - var result = controller.Error(); + var result = CreateController().Error(); - var viewResult = result.Should().BeOfType().Subject; - viewResult.Model.Should().BeOfType(); + var objectResult = result.Should().BeOfType().Subject; + objectResult.StatusCode.Should().Be(500); + objectResult.Value.Should().BeOfType(); } } diff --git a/tests/SmartEventBooking.Web.Tests/SmartEventBooking.Web.Tests.csproj b/tests/SmartEventBooking.Web.Tests/SmartEventBooking.Web.Tests.csproj index cf01b78..a5a993d 100644 --- a/tests/SmartEventBooking.Web.Tests/SmartEventBooking.Web.Tests.csproj +++ b/tests/SmartEventBooking.Web.Tests/SmartEventBooking.Web.Tests.csproj @@ -21,7 +21,8 @@ - + + From 50b44297beeea9afdd86670ca381e35aac10c231 Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:49:21 +0300 Subject: [PATCH 02/16] feat: add frontend CI, add dockerfile for frontend and docker-compose and configure nginx --- .github/workflows/pr-validation.yml | 38 ++++++++++++-- .gitignore | 2 +- docker-compose.yml | 50 +++++++++++++++++++ .../DependencyInjection.cs | 5 +- src/api/SmartEventBooking.Web/Program.cs | 3 +- src/client/.nvmrc | 1 + src/client/Dockerfile | 22 ++++++++ src/client/config.json.template | 3 ++ src/client/docker-entrypoint.sh | 5 ++ src/client/nginx.custom.conf | 21 ++++++++ src/client/package-lock.json | 50 ++++++++++--------- src/client/src/app/app.config.ts | 9 +++- .../src/app/core/services/category.service.ts | 7 ++- .../src/app/core/services/config.service.ts | 23 +++++++++ .../src/app/core/services/event.service.ts | 8 ++- src/client/src/assets/config.json | 3 ++ .../environments/environment.development.ts | 3 +- src/client/src/environments/environment.ts | 3 +- 18 files changed, 216 insertions(+), 40 deletions(-) create mode 100644 docker-compose.yml create mode 100644 src/client/.nvmrc create mode 100644 src/client/Dockerfile create mode 100644 src/client/config.json.template create mode 100644 src/client/docker-entrypoint.sh create mode 100644 src/client/nginx.custom.conf create mode 100644 src/client/src/app/core/services/config.service.ts create mode 100644 src/client/src/assets/config.json diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 495b94c..8615baa 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -7,8 +7,8 @@ on: - main jobs: - build-and-test: - name: Build and Test + build-and-test-api: + name: API Validation runs-on: ubuntu-latest steps: @@ -29,5 +29,35 @@ jobs: - name: Run Unit & Integration Tests run: dotnet test --no-build --configuration Release --verbosity normal - - name: Verify Dockerfile builds successfully - run: docker build -t smarteventbooking:test -f src/api/SmartEventBooking.Web/Dockerfile . \ No newline at end of file + - name: Verify API Dockerfile builds + run: docker build -t smarteventbooking-api:test -f src/api/SmartEventBooking.Web/Dockerfile . + + + build-and-test-client: + name: Client UI Validation + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./src/client + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: src/client/.nvmrc + cache: 'npm' + cache-dependency-path: ./src/client/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build Angular project + run: npm run build -- --configuration production + + - name: Verify Client Dockerfile builds + working-directory: . + run: docker build -t smarteventbooking-client:test src/client/ diff --git a/.gitignore b/.gitignore index bb85a2a..2daa04b 100644 --- a/.gitignore +++ b/.gitignore @@ -417,5 +417,5 @@ FodyWeavers.xsd *.msm *.msp -docker-compose.yml +#docker-compose.yml #Dockerfile \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6815a43 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ + +services: + db: + image: mcr.microsoft.com/mssql/server:2022-latest + container_name: smart-event-booking-sql-db + environment: + - ACCEPT_EULA=Y + - MSSQL_SA_PASSWORD=${Database__Password} + ports: + - "1433:1433" + volumes: + - sql_data:/var/opt/mssql + healthcheck: + test: ["CMD", "/opt/mssql-tools18/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "${Database__Password}", "-Q", "SELECT 1", "-No"] + interval: 5s + timeout: 3s + retries: 10 + start_period: 10s + + api: + image: smart-event-api:latest + build: + context: . + dockerfile: src/api/SmartEventBooking.Web/Dockerfile + container_name: smart-event-booking-api-app + ports: + - "5270:8080" + env_file: + - .env + environment: + - Database__Server=db + depends_on: + db: + condition: service_healthy + + client: + image: smart-event-booking-client:latest + build: + context: ./src/client + dockerfile: Dockerfile + container_name: smart-event-booking-client-app + ports: + - "4200:80" + environment: + - API_BASE_URL=http://localhost:5270 + depends_on: + - api + +volumes: + sql_data: \ No newline at end of file diff --git a/src/api/SmartEventBooking.Infrastructure/DependencyInjection.cs b/src/api/SmartEventBooking.Infrastructure/DependencyInjection.cs index bb40337..3515b6d 100644 --- a/src/api/SmartEventBooking.Infrastructure/DependencyInjection.cs +++ b/src/api/SmartEventBooking.Infrastructure/DependencyInjection.cs @@ -40,8 +40,9 @@ public static IServiceCollection AddInfrastructure( options.UseSqlServer(connectionString, sqlOptions => { - sqlOptions.EnableRetryOnFailure(maxRetryCount: 3, - maxRetryDelay: TimeSpan.FromSeconds(5), + sqlOptions.EnableRetryOnFailure( + maxRetryCount: 3, + maxRetryDelay: TimeSpan.FromSeconds(10), errorNumbersToAdd: null); }); }); diff --git a/src/api/SmartEventBooking.Web/Program.cs b/src/api/SmartEventBooking.Web/Program.cs index 8c276b2..4a0ce89 100644 --- a/src/api/SmartEventBooking.Web/Program.cs +++ b/src/api/SmartEventBooking.Web/Program.cs @@ -39,7 +39,8 @@ { builder.WithOrigins("http://localhost:4200", "https://localhost:4200") .AllowAnyMethod() - .AllowAnyHeader(); + .AllowAnyHeader() + .AllowCredentials(); }); }); diff --git a/src/client/.nvmrc b/src/client/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/src/client/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/src/client/Dockerfile b/src/client/Dockerfile new file mode 100644 index 0000000..f1dabea --- /dev/null +++ b/src/client/Dockerfile @@ -0,0 +1,22 @@ +FROM node:24-alpine AS build +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . + +RUN npm run build -- --configuration production + +FROM nginx:stable-alpine + +RUN apk add --no-cache gettext + +COPY --from=build /app/dist/client/browser /usr/share/nginx/html +COPY nginx.custom.conf /etc/nginx/conf.d/default.conf +COPY config.json.template /usr/share/nginx/html/assets/config.json.template +COPY docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh + +EXPOSE 80 +CMD ["/docker-entrypoint.sh"] \ No newline at end of file diff --git a/src/client/config.json.template b/src/client/config.json.template new file mode 100644 index 0000000..7553824 --- /dev/null +++ b/src/client/config.json.template @@ -0,0 +1,3 @@ +{ + "apiBaseUrl": "${API_BASE_URL}" +} diff --git a/src/client/docker-entrypoint.sh b/src/client/docker-entrypoint.sh new file mode 100644 index 0000000..beaf0cb --- /dev/null +++ b/src/client/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/sh +envsubst '${API_BASE_URL}' \ + < /usr/share/nginx/html/assets/config.json.template \ + > /usr/share/nginx/html/assets/config.json +exec nginx -g 'daemon off;' diff --git a/src/client/nginx.custom.conf b/src/client/nginx.custom.conf new file mode 100644 index 0000000..681e1a6 --- /dev/null +++ b/src/client/nginx.custom.conf @@ -0,0 +1,21 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|eot|ttf|otf)$ { + root /usr/share/nginx/html; + expires 1y; + add_header Cache-Control "public, no-transform"; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/src/client/package-lock.json b/src/client/package-lock.json index bc1d9ce..ee0d0c8 100644 --- a/src/client/package-lock.json +++ b/src/client/package-lock.json @@ -463,7 +463,6 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.9.tgz", "integrity": "sha512-7spQcF3hPN/fjTx6Pwa32KRRdO0NcixnRuPV4lo50ejtXesjiLVR+fkaX38sawAyGoq89IuuYvUDrbLwCMypmQ==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -480,7 +479,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.9.tgz", "integrity": "sha512-clsK1EsSPtAuqlRl4CciA/gsvsW7xe0eWcvHxtrMW6DYaUJ6X4AAuDxEEJ5cf/3Mpw4s8KssjIUPPtbrUIGLSQ==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -494,7 +492,6 @@ "integrity": "sha512-hTTW/OiqTXrwTneS18CMp47OX0XSbLYl2rIomLS3nXVJniSETH6S/k+LqQtGWWgLbzsd3PzUOOckHnvzpTBTsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "7.29.0", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -527,7 +524,6 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.9.tgz", "integrity": "sha512-uZLq2aedJ+0uEZxyf6a1Nc7y1aZ7akAW7K1Kon8JUDZOvI2IDbk0i00MzkELt8q9uSmSSqg9zNKuhjspFf0Pyw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -572,7 +568,6 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.9.tgz", "integrity": "sha512-MjEtFvoFtsjsAeu2yzauqGgwwEHV4ml25c9vGFmw4OmSoNme4yp41f2DegwOkn1TTHL3OF3GE65ng2U2feJU4Q==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -697,7 +692,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1061,7 +1055,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -1110,11 +1103,35 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1122,6 +1139,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1853,7 +1871,6 @@ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -4306,7 +4323,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -4458,7 +4474,6 @@ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -5147,7 +5162,6 @@ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5476,7 +5490,6 @@ "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -5818,7 +5831,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -5927,7 +5939,6 @@ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", @@ -7025,7 +7036,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7339,7 +7349,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -7964,8 +7973,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tuf-js": { "version": "4.1.0", @@ -8003,7 +8011,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8089,7 +8096,6 @@ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8165,7 +8171,6 @@ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.1.5", "@vitest/mocker": "4.1.5", @@ -8558,7 +8563,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/client/src/app/app.config.ts b/src/client/src/app/app.config.ts index 4451829..6b83381 100644 --- a/src/client/src/app/app.config.ts +++ b/src/client/src/app/app.config.ts @@ -1,13 +1,18 @@ -import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; +import { ApplicationConfig, inject, provideBrowserGlobalErrorListeners, provideAppInitializer } from '@angular/core'; import { provideRouter } from '@angular/router'; import { provideHttpClient } from '@angular/common/http'; import { routes } from './app.routes'; +import { ConfigService } from './core/services/config.service'; export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideRouter(routes), - provideHttpClient() + provideHttpClient(), + provideAppInitializer(() => { + const configService = inject(ConfigService); + return configService.load(); + }) ] }; diff --git a/src/client/src/app/core/services/category.service.ts b/src/client/src/app/core/services/category.service.ts index 6377cbc..2cf49a2 100644 --- a/src/client/src/app/core/services/category.service.ts +++ b/src/client/src/app/core/services/category.service.ts @@ -2,13 +2,18 @@ import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Category } from '../models/category.model'; +import { ConfigService } from './config.service'; @Injectable({ providedIn: 'root' }) export class CategoryService { private http = inject(HttpClient); - private apiUrl = 'https://localhost:7134/api/categories'; + private config = inject(ConfigService); + + private get apiUrl(): string { + return `${this.config.apiBaseUrl}/api/categories`; + } getCategories(): Observable { return this.http.get(this.apiUrl); diff --git a/src/client/src/app/core/services/config.service.ts b/src/client/src/app/core/services/config.service.ts new file mode 100644 index 0000000..de9ba89 --- /dev/null +++ b/src/client/src/app/core/services/config.service.ts @@ -0,0 +1,23 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { firstValueFrom } from 'rxjs'; + +interface AppConfig { + apiBaseUrl: string; +} + +@Injectable({ providedIn: 'root' }) +export class ConfigService { + private readonly http = inject(HttpClient); + private config: AppConfig = { apiBaseUrl: '' }; + + async load(): Promise { + this.config = await firstValueFrom( + this.http.get('/assets/config.json') + ); + } + + get apiBaseUrl(): string { + return this.config.apiBaseUrl; + } +} diff --git a/src/client/src/app/core/services/event.service.ts b/src/client/src/app/core/services/event.service.ts index c8a0a8a..5b53807 100644 --- a/src/client/src/app/core/services/event.service.ts +++ b/src/client/src/app/core/services/event.service.ts @@ -4,14 +4,18 @@ import { Observable } from 'rxjs'; import { Event } from '../models/event.model'; import { PaginatedList } from '../models/paginated-list.model'; import { EventSearchDto } from '../models/event-search.model'; -import { environment } from '../../../environments/environment'; +import { ConfigService } from './config.service'; @Injectable({ providedIn: 'root' }) export class EventService { private http = inject(HttpClient); - private apiUrl = environment.apiUrl; + private config = inject(ConfigService); + + private get apiUrl(): string { + return `${this.config.apiBaseUrl}/api/events`; + } getUpcomingEvents(page: number = 1, pageSize: number = 5, search?: EventSearchDto): Observable> { let params = new HttpParams() diff --git a/src/client/src/assets/config.json b/src/client/src/assets/config.json new file mode 100644 index 0000000..31c6818 --- /dev/null +++ b/src/client/src/assets/config.json @@ -0,0 +1,3 @@ +{ + "apiBaseUrl": "https://localhost:7134" +} diff --git a/src/client/src/environments/environment.development.ts b/src/client/src/environments/environment.development.ts index 49a1bc0..ffe8aed 100644 --- a/src/client/src/environments/environment.development.ts +++ b/src/client/src/environments/environment.development.ts @@ -1,4 +1,3 @@ export const environment = { - production: false, - apiUrl: 'https://localhost:7134/api/events' + production: false }; diff --git a/src/client/src/environments/environment.ts b/src/client/src/environments/environment.ts index 2a66107..3612073 100644 --- a/src/client/src/environments/environment.ts +++ b/src/client/src/environments/environment.ts @@ -1,4 +1,3 @@ export const environment = { - production: true, - apiUrl: 'https://localhost:7134/api/events' + production: true }; From c0373ba5d56231bae65acfbae82564dda194cd35 Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:36:58 +0300 Subject: [PATCH 03/16] add swagger for api, fix node.js error for local run and update readme --- README.md | 100 ++++++++++++++++-- src/api/SmartEventBooking.Web/Program.cs | 11 +- .../Properties/launchSettings.json | 2 + .../SmartEventBooking.Web.csproj | 1 + src/client/{src => public}/assets/config.json | 0 src/client/tsconfig.app.json | 1 + 6 files changed, 106 insertions(+), 9 deletions(-) rename src/client/{src => public}/assets/config.json (100%) diff --git a/README.md b/README.md index 7f21047..3201d47 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,56 @@ # Smart Event Booking -ASP.NET Core MVC application for event discovery and ticket booking. +Full-stack event discovery and ticket booking platform — ASP.NET Core 10 API + Angular client. For product vision, scope, and contribution workflow, see `PROJECT_SCOPE.md`. ## Prerequisites +**To run with Docker Compose:** +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) + +**To run locally:** - .NET SDK 10.0 +- Node.js 24 LTS (see [Node version setup](#node-version-setup)) - SQL Server (local instance or Docker) +## Running with Docker Compose + +The quickest way to run the full stack (API + client + database) without any local tooling beyond Docker. + +1. Copy the env file and fill in your credentials: + ```bash + cp .env.example .env + ``` + +2. Start everything: + ```bash + docker compose up -d --build + ``` + + | Service | URL | + |---|---| + | Client (Angular) | http://localhost:4200 | + | API (ASP.NET Core) | http://localhost:5270 | + | SQL Server | localhost:1433 | + + Migrations run automatically on API startup — no manual `dotnet ef` step needed. + +3. Stop everything: + ```bash + docker compose down + ``` + Add `-v` to also wipe the database volume. + +--- + +## Running locally + +Follow the numbered steps below to run the API and client separately. + ## 1) Configure the database settings -This app reads database values from environment variables (`Database__Server`, `Database__Name`, `Database__User`, `Database__Password`, `Database__TrustServerCertificate`) and falls back to `src/SmartEventBooking.Web/appsettings.json`. +This app reads database values from environment variables (`Database__Server`, `Database__Name`, `Database__User`, `Database__Password`, `Database__TrustServerCertificate`) and falls back to `src/api/SmartEventBooking.Web/appsettings.json`. Admin seeding credentials are also read from environment variables: @@ -43,19 +82,20 @@ From the repository root: ```bash dotnet restore -dotnet ef database update --project src/SmartEventBooking.Infrastructure/SmartEventBooking.Infrastructure.csproj --startup-project src/SmartEventBooking.Web/SmartEventBooking.Web.csproj --context ApplicationDbContext +dotnet ef database update --project src/api/SmartEventBooking.Infrastructure/SmartEventBooking.Infrastructure.csproj --startup-project src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj --context ApplicationDbContext ``` -## 4) Run the app +## 4) Run the API ```bash -dotnet run --project src/SmartEventBooking.Web/SmartEventBooking.Web.csproj --launch-profile https +dotnet run --project src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj --launch-profile https ``` Default URLs (development): - `https://localhost:7134` - `http://localhost:5270` +- Swagger UI: `https://localhost:7134/swagger` If you run without the `https` launch profile, you may see: @@ -63,7 +103,17 @@ If you run without the `https` launch profile, you may see: Use `--launch-profile https` (shown above), or set `ASPNETCORE_URLS` to include an HTTPS URL. -## 5) Verify connectivity +## 5) Run the client + +```bash +cd src/client +npm ci +ng serve +``` + +Client runs at `http://localhost:4200`. The API URL it connects to is configured in `src/client/public/assets/config.json`. + +## 6) Verify connectivity Health endpoint: @@ -81,3 +131,41 @@ Expected result: dotnet build dotnet test ``` + +--- + +## Node version setup + +The client requires **Node.js 24 LTS**, pinned in `src/client/.nvmrc`. + +### Option A — Install Node 24 directly (simplest) + +Download and install Node 24 LTS from [nodejs.org](https://nodejs.org/en/download). No version manager needed if you only work on this project. + +### Option B — Use fnm (if you work on multiple projects with different Node versions) + +**Windows:** +```powershell +winget install Schniz.fnm +``` +Add to your PowerShell profile (run once): +```powershell +Add-Content $PROFILE "fnm env --use-on-cd --shell power-shell | Out-String | Invoke-Expression" +``` +Restart PowerShell, then inside `src/client/`: +```powershell +fnm install +fnm use +``` + +**macOS / Linux:** +```bash +curl -fsSL https://fnm.vercel.app/install | bash +``` +Then inside `src/client/`: +```bash +fnm install +fnm use +``` + +> **Switching versions and seeing native module errors?** Delete `node_modules` and run `npm ci` again — native binaries are version-specific. diff --git a/src/api/SmartEventBooking.Web/Program.cs b/src/api/SmartEventBooking.Web/Program.cs index 4a0ce89..0bfb7a5 100644 --- a/src/api/SmartEventBooking.Web/Program.cs +++ b/src/api/SmartEventBooking.Web/Program.cs @@ -13,6 +13,8 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); builder.Services.AddValidatorsFromAssemblyContaining(); @@ -48,11 +50,14 @@ await app.InitialiseDatabaseAsync(); -// Configure the HTTP request pipeline. -if (!app.Environment.IsDevelopment()) +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} +else { app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } diff --git a/src/api/SmartEventBooking.Web/Properties/launchSettings.json b/src/api/SmartEventBooking.Web/Properties/launchSettings.json index 695f794..b84c5ba 100644 --- a/src/api/SmartEventBooking.Web/Properties/launchSettings.json +++ b/src/api/SmartEventBooking.Web/Properties/launchSettings.json @@ -5,6 +5,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, + "launchUrl": "swagger", "applicationUrl": "http://localhost:5270", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -14,6 +15,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, + "launchUrl": "swagger", "applicationUrl": "https://localhost:7134;http://localhost:5270", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj b/src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj index 48af957..e51df86 100644 --- a/src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj +++ b/src/api/SmartEventBooking.Web/SmartEventBooking.Web.csproj @@ -12,6 +12,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/src/client/src/assets/config.json b/src/client/public/assets/config.json similarity index 100% rename from src/client/src/assets/config.json rename to src/client/public/assets/config.json diff --git a/src/client/tsconfig.app.json b/src/client/tsconfig.app.json index 264f459..e4dd97c 100644 --- a/src/client/tsconfig.app.json +++ b/src/client/tsconfig.app.json @@ -4,6 +4,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", + "rootDir": "./src", "types": [] }, "include": [ From 7ebee97635ccf759d46b0a567077cfa968101d8c Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:46:24 +0300 Subject: [PATCH 04/16] feat: testing new workflow for deploy --- .dockerignore | 5 +- .gitattributes | 2 + .github/workflows/release-to-prod.yml | 55 +++++++++++++++++++ docker-compose.yml | 2 +- src/api/SmartEventBooking.Web/Program.cs | 7 ++- .../appsettings.Development.json | 4 ++ .../SmartEventBooking.Web/appsettings.json | 5 +- src/api/SmartEventBooking.Web/temp.txt | 0 src/client/Dockerfile | 2 +- 9 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/release-to-prod.yml delete mode 100644 src/api/SmartEventBooking.Web/temp.txt diff --git a/.dockerignore b/.dockerignore index e031e1f..03fc0eb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,4 +6,7 @@ **/.vscode **/bin **/obj -**/Dockerfile \ No newline at end of file +**/Dockerfile +**/node_modules +**/dist +**/.angular \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..efdba87 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.sh text eol=lf diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml new file mode 100644 index 0000000..d6d778e --- /dev/null +++ b/.github/workflows/release-to-prod.yml @@ -0,0 +1,55 @@ +name: Release to Production + +#on: +# push: +# branches: [ main ] + +on: + pull_request: + branches: + - develop + + +env: + BACKEND_IMAGE: smart-event-api-app + FRONTEND_IMAGE: smart-event-client-app + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESSTOKEN }} + + - name: Build and push Backend + uses: docker/build-push-action@v5 + with: + context: . + file: src/api/SmartEventBooking.Web/Dockerfile + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.BACKEND_IMAGE }}:latest + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.BACKEND_IMAGE }}:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build and push Frontend + uses: docker/build-push-action@v5 + with: + context: ./src/client + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.FRONTEND_IMAGE }}:latest + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.FRONTEND_IMAGE }}:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # ---------------------- Don't forget to write job for deploy \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 6815a43..514e7dd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: start_period: 10s api: - image: smart-event-api:latest + image: smart-event-booking-api:latest build: context: . dockerfile: src/api/SmartEventBooking.Web/Dockerfile diff --git a/src/api/SmartEventBooking.Web/Program.cs b/src/api/SmartEventBooking.Web/Program.cs index 0bfb7a5..8951f31 100644 --- a/src/api/SmartEventBooking.Web/Program.cs +++ b/src/api/SmartEventBooking.Web/Program.cs @@ -35,11 +35,14 @@ options.ExpireTimeSpan = TimeSpan.FromDays(7); }); +var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() + ?? new[] { "http://localhost:4200", "https://localhost:4200" }; + builder.Services.AddCors(options => { - options.AddDefaultPolicy(builder => + options.AddDefaultPolicy(corsBuilder => { - builder.WithOrigins("http://localhost:4200", "https://localhost:4200") + corsBuilder.WithOrigins(allowedOrigins) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); diff --git a/src/api/SmartEventBooking.Web/appsettings.Development.json b/src/api/SmartEventBooking.Web/appsettings.Development.json index 7568bef..a23a2ec 100644 --- a/src/api/SmartEventBooking.Web/appsettings.Development.json +++ b/src/api/SmartEventBooking.Web/appsettings.Development.json @@ -1,4 +1,8 @@ { + "AllowedOrigins": [ + "http://localhost:4200", + "https://localhost:4200" + ], "ConnectionStrings": { "DefaultConnection": "" }, diff --git a/src/api/SmartEventBooking.Web/appsettings.json b/src/api/SmartEventBooking.Web/appsettings.json index a796e57..6ad1def 100644 --- a/src/api/SmartEventBooking.Web/appsettings.json +++ b/src/api/SmartEventBooking.Web/appsettings.json @@ -1,11 +1,12 @@ { + "AllowedOrigins": [], "ConnectionStrings": { "DefaultConnection": "" }, "Database": { - "Server": "localhost,1433", + "Server": "", "Name": "SmartEventBookingDb", - "User": "sa", + "User": "", "Password": "", "TrustServerCertificate": true }, diff --git a/src/api/SmartEventBooking.Web/temp.txt b/src/api/SmartEventBooking.Web/temp.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/client/Dockerfile b/src/client/Dockerfile index f1dabea..cae194d 100644 --- a/src/client/Dockerfile +++ b/src/client/Dockerfile @@ -16,7 +16,7 @@ COPY --from=build /app/dist/client/browser /usr/share/nginx/html COPY nginx.custom.conf /etc/nginx/conf.d/default.conf COPY config.json.template /usr/share/nginx/html/assets/config.json.template COPY docker-entrypoint.sh /docker-entrypoint.sh -RUN chmod +x /docker-entrypoint.sh +RUN sed -i 's/\r//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh EXPOSE 80 CMD ["/docker-entrypoint.sh"] \ No newline at end of file From bed1cf3188b4f13c8e895f313a34ac1c2db4b561 Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:00:49 +0300 Subject: [PATCH 05/16] fix: change dockerhub image name --- .github/workflows/release-to-prod.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml index d6d778e..979b241 100644 --- a/.github/workflows/release-to-prod.yml +++ b/.github/workflows/release-to-prod.yml @@ -11,8 +11,8 @@ on: env: - BACKEND_IMAGE: smart-event-api-app - FRONTEND_IMAGE: smart-event-client-app + BACKEND_IMAGE: smart-event-booking-api-app + FRONTEND_IMAGE: smart-event-booking-client-app jobs: build-and-push: From 6135ac9ba793c6a61459ee315800f7dee271cdff Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:38:17 +0300 Subject: [PATCH 06/16] feat: add deploy to azure to workflow --- .github/workflows/release-to-prod.yml | 40 +++++++++++++++++++++------ src/client/Dockerfile | 7 +++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml index 979b241..7d06b9e 100644 --- a/.github/workflows/release-to-prod.yml +++ b/.github/workflows/release-to-prod.yml @@ -11,8 +11,10 @@ on: env: - BACKEND_IMAGE: smart-event-booking-api-app - FRONTEND_IMAGE: smart-event-booking-client-app + API_IMAGE: smart-event-booking-api-app + CLIENT_IMAGE: smart-event-booking-client-app + AZURE_WEBAPP_API_NAME: app-smarteventbooking-api + AZURE_WEBAPP_CLIENT_NAME: app-smarteventbooking-client jobs: build-and-push: @@ -29,27 +31,47 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_ACCESSTOKEN }} - - name: Build and push Backend + - name: Build and push API uses: docker/build-push-action@v5 with: context: . file: src/api/SmartEventBooking.Web/Dockerfile push: true tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.BACKEND_IMAGE }}:latest - ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.BACKEND_IMAGE }}:${{ github.sha }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.API_IMAGE }}:latest + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.API_IMAGE }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max - - name: Build and push Frontend + - name: Build and push Client uses: docker/build-push-action@v5 with: context: ./src/client push: true tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.FRONTEND_IMAGE }}:latest - ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.FRONTEND_IMAGE }}:${{ github.sha }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CLIENT_IMAGE }}:latest + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CLIENT_IMAGE }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max - # ---------------------- Don't forget to write job for deploy \ No newline at end of file + deploy-api: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - name: Deploy API to Azure Web App + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_WEBAPP_API_NAME }} + publish-profile: ${{ secrets.AZURE_API_PUBLISH_PROFILE }} + images: '${{ secrets.DOCKERHUB_USERNAME }}/${{ env.API_IMAGE }}:${{ github.sha }}' + + deploy-client: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - name: Deploy Client to Azure Web App + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_WEBAPP_CLIENT_NAME }} + publish-profile: ${{ secrets.AZURE_CLIENT_PUBLISH_PROFILE }} + images: '${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CLIENT_IMAGE }}:${{ github.sha }}' diff --git a/src/client/Dockerfile b/src/client/Dockerfile index cae194d..f858451 100644 --- a/src/client/Dockerfile +++ b/src/client/Dockerfile @@ -5,7 +5,6 @@ COPY package.json package-lock.json ./ RUN npm ci COPY . . - RUN npm run build -- --configuration production FROM nginx:stable-alpine @@ -14,9 +13,13 @@ RUN apk add --no-cache gettext COPY --from=build /app/dist/client/browser /usr/share/nginx/html COPY nginx.custom.conf /etc/nginx/conf.d/default.conf + COPY config.json.template /usr/share/nginx/html/assets/config.json.template + COPY docker-entrypoint.sh /docker-entrypoint.sh + RUN sed -i 's/\r//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh EXPOSE 80 -CMD ["/docker-entrypoint.sh"] \ No newline at end of file + +ENTRYPOINT ["/docker-entrypoint.sh"] \ No newline at end of file From e59649bcf388986dd363e5302815fbecaa8e0abf Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 01:05:33 +0300 Subject: [PATCH 07/16] fix: fix tests after merge --- .../Controllers/HomeControllerTests.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs b/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs index 73302cd..8abd23f 100644 --- a/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs +++ b/tests/SmartEventBooking.Web.Tests/Controllers/HomeControllerTests.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging.Abstractions; using SmartEventBooking.Web.Controllers; @@ -31,14 +32,4 @@ public void Privacy_ReturnsOkResult() result.Should().BeOfType(); } - - [Fact] - public void Error_ReturnsObjectResult_WithProblemDetails() - { - var result = CreateController().Error(); - - var objectResult = result.Should().BeOfType().Subject; - objectResult.StatusCode.Should().Be(500); - objectResult.Value.Should().BeOfType(); - } } \ No newline at end of file From d488a579ca5afa39d32aed621483117c3d1af80c Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 01:10:33 +0300 Subject: [PATCH 08/16] change deploy worflow to deploy only on merge with main --- .github/workflows/release-to-prod.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml index 7d06b9e..b146ef3 100644 --- a/.github/workflows/release-to-prod.yml +++ b/.github/workflows/release-to-prod.yml @@ -1,14 +1,8 @@ name: Release to Production -#on: -# push: -# branches: [ main ] - on: - pull_request: - branches: - - develop - + push: + branches: [ main ] env: API_IMAGE: smart-event-booking-api-app From d07429787e52dae42f5b37477179c59f825feaad Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 01:43:30 +0300 Subject: [PATCH 09/16] fix comments and test --- .github/workflows/pr-validation.yml | 6 ++--- .github/workflows/release-to-prod.yml | 35 +++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 8615baa..37ee133 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -54,9 +54,9 @@ jobs: - name: Install dependencies run: npm ci - - - name: Build Angular project - run: npm run build -- --configuration production + + - name: Run client tests + run: npm test -- --watch=false --browsers=ChromeHeadless - name: Verify Client Dockerfile builds working-directory: . diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml index b146ef3..a6e502f 100644 --- a/.github/workflows/release-to-prod.yml +++ b/.github/workflows/release-to-prod.yml @@ -1,8 +1,14 @@ name: Release to Production +# on: +# push: +# branches: [ main ] + on: - push: - branches: [ main ] + pull_request: + branches: + - develop + - main env: API_IMAGE: smart-event-booking-api-app @@ -11,7 +17,22 @@ env: AZURE_WEBAPP_CLIENT_NAME: app-smarteventbooking-client jobs: + run-tests: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + + - name: Run API tests + run: dotnet test --configuration Release --verbosity normal + build-and-push: + needs: run-tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -59,6 +80,11 @@ jobs: publish-profile: ${{ secrets.AZURE_API_PUBLISH_PROFILE }} images: '${{ secrets.DOCKERHUB_USERNAME }}/${{ env.API_IMAGE }}:${{ github.sha }}' + - name: Smoke test API + run: | + curl --retry 10 --retry-delay 10 --retry-connrefused --fail \ + https://${{ env.AZURE_WEBAPP_API_NAME }}.azurewebsites.net/health/db + deploy-client: needs: build-and-push runs-on: ubuntu-latest @@ -69,3 +95,8 @@ jobs: app-name: ${{ env.AZURE_WEBAPP_CLIENT_NAME }} publish-profile: ${{ secrets.AZURE_CLIENT_PUBLISH_PROFILE }} images: '${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CLIENT_IMAGE }}:${{ github.sha }}' + + - name: Smoke test Client + run: | + curl --retry 10 --retry-delay 10 --retry-connrefused --fail \ + https://${{ env.AZURE_WEBAPP_CLIENT_NAME }}.azurewebsites.net From d51e4b08fa181a1cc307b66e3fb37adf7399b32e Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 01:45:59 +0300 Subject: [PATCH 10/16] fix for failing client test job in pr-validation --- .github/workflows/pr-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 37ee133..660b167 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -56,7 +56,7 @@ jobs: run: npm ci - name: Run client tests - run: npm test -- --watch=false --browsers=ChromeHeadless + run: npm test -- --watch=false - name: Verify Client Dockerfile builds working-directory: . From 54246bc2a59ff2212a7c723ec7fb2b17cfbfca3f Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 02:02:57 +0300 Subject: [PATCH 11/16] fix deployed apps testing --- .github/workflows/release-to-prod.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml index a6e502f..12abe3b 100644 --- a/.github/workflows/release-to-prod.yml +++ b/.github/workflows/release-to-prod.yml @@ -83,7 +83,7 @@ jobs: - name: Smoke test API run: | curl --retry 10 --retry-delay 10 --retry-connrefused --fail \ - https://${{ env.AZURE_WEBAPP_API_NAME }}.azurewebsites.net/health/db + ${{ vars.AZURE_WEBAPP_API_URL }}/health/db deploy-client: needs: build-and-push @@ -99,4 +99,4 @@ jobs: - name: Smoke test Client run: | curl --retry 10 --retry-delay 10 --retry-connrefused --fail \ - https://${{ env.AZURE_WEBAPP_CLIENT_NAME }}.azurewebsites.net + ${{ vars.AZURE_WEBAPP_CLIENT_URL }} From 18fb0d1d4176da9e50690fc0163084a980c831cc Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 02:08:22 +0300 Subject: [PATCH 12/16] add caching to pr-validation and remove "on pr" from release workflow --- .github/workflows/pr-validation.yml | 23 ++++++++++++++++++----- .github/workflows/release-to-prod.yml | 10 ++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 660b167..bd8f87a 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -14,7 +14,10 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 - + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -22,7 +25,7 @@ jobs: - name: Restore dependencies run: dotnet restore -p:Configuration=Release - + - name: Build project run: dotnet build --no-restore --configuration Release @@ -30,7 +33,13 @@ jobs: run: dotnet test --no-build --configuration Release --verbosity normal - name: Verify API Dockerfile builds - run: docker build -t smarteventbooking-api:test -f src/api/SmartEventBooking.Web/Dockerfile . + uses: docker/build-push-action@v5 + with: + context: . + file: src/api/SmartEventBooking.Web/Dockerfile + push: false + cache-from: type=gha + cache-to: type=gha,mode=max build-and-test-client: @@ -59,5 +68,9 @@ jobs: run: npm test -- --watch=false - name: Verify Client Dockerfile builds - working-directory: . - run: docker build -t smarteventbooking-client:test src/client/ + uses: docker/build-push-action@v5 + with: + context: ./src/client + push: false + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release-to-prod.yml b/.github/workflows/release-to-prod.yml index 12abe3b..680c3d7 100644 --- a/.github/workflows/release-to-prod.yml +++ b/.github/workflows/release-to-prod.yml @@ -1,14 +1,8 @@ name: Release to Production -# on: -# push: -# branches: [ main ] - on: - pull_request: - branches: - - develop - - main + push: + branches: [ main ] env: API_IMAGE: smart-event-booking-api-app From 19c2cc6798ceecbde7552409bdb0e03dadaf9109 Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 02:10:17 +0300 Subject: [PATCH 13/16] minor fix --- .github/workflows/pr-validation.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index bd8f87a..da47953 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -54,6 +54,9 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Setup Node.js uses: actions/setup-node@v4 with: From 2f57ded1f1629e6c4a4786d0c59ef7360b62effa Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:43:25 +0300 Subject: [PATCH 14/16] fix: fix ci tests failure --- src/client/src/app/core/services/auth.service.ts | 9 +++++---- .../Identity/AuthServiceTests.cs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/client/src/app/core/services/auth.service.ts b/src/client/src/app/core/services/auth.service.ts index 9cb87c8..4d0f4d0 100644 --- a/src/client/src/app/core/services/auth.service.ts +++ b/src/client/src/app/core/services/auth.service.ts @@ -1,7 +1,7 @@ import { Injectable, computed, inject, signal } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, tap } from 'rxjs'; -import { environment } from '../../../environments/environment'; +import { ConfigService } from './config.service'; export interface LoginRequest { email: string; @@ -35,15 +35,16 @@ export class AuthService { private static readonly AUTH_SESSION_STORAGE_KEY = 'auth.session'; private readonly http = inject(HttpClient); + private readonly config = inject(ConfigService); private readonly authSessionSignal = signal(this.readStoredAuthSession()); readonly authSession = computed(() => this.authSessionSignal()); readonly userEmail = computed(() => this.authSessionSignal()?.email ?? null); readonly isLoggedIn = computed(() => this.authSessionSignal() !== null); - private readonly authApiUrl = environment.apiUrl.endsWith('/events') - ? environment.apiUrl.replace('/events', '/auth') - : `${environment.apiUrl}/auth`; + private get authApiUrl(): string { + return `${this.config.apiBaseUrl}/api/auth`; + } register(request: RegisterRequest): Observable { return this.http.post(`${this.authApiUrl}/register`, request); diff --git a/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs b/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs index d7a1d74..9e3e877 100644 --- a/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs +++ b/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs @@ -240,7 +240,7 @@ public async Task LoginAsync_ShouldReturnErrors_WhenUserIsLockedOut() var result = await _authService.LoginAsync(dto); result.Succeeded.Should().BeFalse(); - result.Errors.Should().ContainSingle().Which.Should().Be("Login failed."); + result.Errors.Should().ContainSingle().Which.Should().Be("Login failed: Rate limited."); } [Fact] @@ -298,7 +298,7 @@ public async Task LoginAsync_ShouldReturnErrors_WhenPasswordIsInvalid() var result = await _authService.LoginAsync(dto); result.Succeeded.Should().BeFalse(); - result.Errors.Should().ContainSingle().Which.Should().Be("Login failed."); + result.Errors.Should().ContainSingle().Which.Should().Be("Login failed: Please verify your email (should never happen for now)."); } [Theory] From 763f9e290e292c871eb71bcc4e81a54252009e1c Mon Sep 17 00:00:00 2001 From: viktorpekhun <129255792+viktorpekhun@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:49:01 +0300 Subject: [PATCH 15/16] minor fix --- .../Identity/AuthServiceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs b/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs index 9e3e877..1dec78b 100644 --- a/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs +++ b/tests/SmartEventBooking.Infrastructure.UnitTests/Identity/AuthServiceTests.cs @@ -269,7 +269,7 @@ public async Task LoginAsync_ShouldReturnErrors_WhenUserIsNotAllowed() var result = await _authService.LoginAsync(dto); result.Succeeded.Should().BeFalse(); - result.Errors.Should().ContainSingle().Which.Should().Be("Login failed."); + result.Errors.Should().ContainSingle().Which.Should().Be("Login failed: Please verify your email (should never happen for now)."); } [Fact] @@ -298,7 +298,7 @@ public async Task LoginAsync_ShouldReturnErrors_WhenPasswordIsInvalid() var result = await _authService.LoginAsync(dto); result.Succeeded.Should().BeFalse(); - result.Errors.Should().ContainSingle().Which.Should().Be("Login failed: Please verify your email (should never happen for now)."); + result.Errors.Should().ContainSingle().Which.Should().Be("Login failed."); } [Theory] From 4731e4de3ee304f49670deefc7e76cbef592900f Mon Sep 17 00:00:00 2001 From: Ivan Lutsenko <108870263+eevatsen@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:51:13 +0300 Subject: [PATCH 16/16] Update package-lock.json --- src/client/package-lock.json | 50 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/client/package-lock.json b/src/client/package-lock.json index bc1d9ce..ee0d0c8 100644 --- a/src/client/package-lock.json +++ b/src/client/package-lock.json @@ -463,7 +463,6 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.9.tgz", "integrity": "sha512-7spQcF3hPN/fjTx6Pwa32KRRdO0NcixnRuPV4lo50ejtXesjiLVR+fkaX38sawAyGoq89IuuYvUDrbLwCMypmQ==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -480,7 +479,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.9.tgz", "integrity": "sha512-clsK1EsSPtAuqlRl4CciA/gsvsW7xe0eWcvHxtrMW6DYaUJ6X4AAuDxEEJ5cf/3Mpw4s8KssjIUPPtbrUIGLSQ==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -494,7 +492,6 @@ "integrity": "sha512-hTTW/OiqTXrwTneS18CMp47OX0XSbLYl2rIomLS3nXVJniSETH6S/k+LqQtGWWgLbzsd3PzUOOckHnvzpTBTsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "7.29.0", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -527,7 +524,6 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.9.tgz", "integrity": "sha512-uZLq2aedJ+0uEZxyf6a1Nc7y1aZ7akAW7K1Kon8JUDZOvI2IDbk0i00MzkELt8q9uSmSSqg9zNKuhjspFf0Pyw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -572,7 +568,6 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.9.tgz", "integrity": "sha512-MjEtFvoFtsjsAeu2yzauqGgwwEHV4ml25c9vGFmw4OmSoNme4yp41f2DegwOkn1TTHL3OF3GE65ng2U2feJU4Q==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -697,7 +692,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1061,7 +1055,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -1110,11 +1103,35 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1122,6 +1139,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1853,7 +1871,6 @@ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -4306,7 +4323,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -4458,7 +4474,6 @@ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -5147,7 +5162,6 @@ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5476,7 +5490,6 @@ "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -5818,7 +5831,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -5927,7 +5939,6 @@ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", @@ -7025,7 +7036,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7339,7 +7349,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -7964,8 +7973,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tuf-js": { "version": "4.1.0", @@ -8003,7 +8011,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8089,7 +8096,6 @@ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8165,7 +8171,6 @@ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.1.5", "@vitest/mocker": "4.1.5", @@ -8558,7 +8563,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }